WPF 基础(十五)UI更新:App.Current和System.Windows.Application.Current、BeginInvoke和Invoke、阻塞问题

本文介绍非MVVM和MVVM模式下的UI更新方法,包括不同场景下的Bitmap处理与绑定更新策略,并探讨了循环执行导致UI阻塞的问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、非MVVM的UI更新方法

1、Bing+Image+Bitmap

以下方法都可直接更新

//方法一
_showFaceImage = bmp;

//方法二
ShowFaceImage = bmp;
Dispatcher.CurrentDispatcher.Invoke(() =>
    ShowFaceImage = bmp
);

//方法三
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate ()
{
    ShowFaceImage = bmp;
});

//方法四
App.Current.Dispatcher.Invoke((Action)delegate ()
{
    ShowFaceImage = bmp;
});

其中,ShowFaceImage属性:

      private Image _showFaceImage;

        /// <summary>
        /// 人脸图片
        /// </summary>
        public Image ShowFaceImage
        {
            get { return _showFaceImage; }
            set { SetProperty(ref _showFaceImage, value); }
        }

Image控件: 

<Image x:Name="imgFaceImage" Stretch="None"  Height="322" Width="260" Canvas.Top="16" Canvas.Left="15" Source="{Binding ShowFaceImage}"/>

2、控件ImageSource+BitmapImage

<Image Stretch="Fill" x:Name="imgVideoSurveillance" Canvas.Top="132" Canvas.Right="10" Width="984" Height="822" Canvas.Left="198" RenderTransformOrigin="0.5,0.5">
           //字节转BitmapImage
                BitmapImage bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.CacheOption = BitmapCacheOption.OnDemand;
                MemoryStream ms = new MemoryStream(btCurrentBitmapImage);
                bitmapImage.StreamSource = ms;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                //更新UI
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    imgVideoSurveillance.Source = bitmapImage;
                });

3、Bing+ImageSource+BitmapImage

 <Image x:Name="imgFaceImage" Stretch="None"  Height="322" Width="260" Canvas.Top="16" Canvas.Left="15" Source="{Binding ShowFaceImage}"/>
        private ImageSource _showFaceImage;

        /// <summary>
        /// 人脸图片
        /// </summary>
        public ImageSource ShowFaceImage
        {
            get { return _showFaceImage; }
            set { SetProperty(ref _showFaceImage, value); }
        }
ShowFaceImage = ConvertBitmapToBitmapImage(bmp);
        public BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
        {
            MemoryStream stream = new MemoryStream();
            bitmap.Save(stream, ImageFormat.Png);
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = stream;
            bitmapImage.EndInit();
            return bitmapImage;
        }

二、MVVM的UI更新方法

       实际上,MVVM架构的UI更新问题,常用的方法还是绑定的方式。最有代表的应用,就是视频监控画面的实时更新问题。

       但是,以上方法,属性Image和属性ImageSource的绑定方法,我都尝过了,虽然代码运行起来没有什么问题,但是,UI确实没方法更新。

      于是,问了下同时,原来发现是自己学艺不精。

1、错误方式

          Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate ()
            {
                //ShowFaceImage = bitmap;
                ShowFaceImage = ConvertBitmapToBitmapImage(bitmap);
            });

Dispatcher.CurrentDispatcher.BeginInvoke——属于次线程,我们不能在次线程上再调用次线程的委托函数来更新主线程的UI。

2、解决办法

我们只能在次线程上再调用主线程的委托函数来更新主线程的UI

我们只能在次线程上再调用主线程的委托函数来更新主线程的UI。

我们只能在次线程上再调用主线程的委托函数来更新主线程的UI。

         System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate ()
            {
                ShowFaceImage1 = ConvertBitmapToBitmapImage(bitmap);
                //string strImagePath2 = @"C:\Users\lanmage2\Desktop\png.png";
                //BitmapImage bitmapImage = new BitmapImage(new Uri(strImagePath2, UriKind.Absolute));
                //ShowFaceImage1 = bitmapImage;
            });

        /// <summary>
        /// Bitmap转BitmapImage
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
        {
            MemoryStream stream = new MemoryStream();
            bitmap.Save(stream, ImageFormat.Png);
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = stream;
            bitmapImage.EndInit();
            return bitmapImage;
        }

或者用下面的方法:

App.Current.Dispatcher.Invoke((Action)delegate ()
{
    Panel.SetZIndex(imgVideoSurveillance, 2);
});

三、循环执行导致主线程UI阻塞,binding方式也不能更新UI

1、问题描述

<TextBox x:Name="txtTip" Height="131" Canvas.Left="292" TextWrapping="Wrap"  Canvas.Top="26" Width="111" BorderBrush="#FF472530" Text="{Binding TipText,Mode=TwoWay}"/>
      public string TipText
        {
            get { return _tipText; }
            set { SetProperty(ref _tipText, value); }
        }

 

  private void RegisterFingerFunc()
        {
            try
            {
                if (EMessageBox.Show("是否确定注册指纹?", "提示", EMessageBoxButton.YesNo, EMessageBoxImage.Information) == EMessageBoxResult.Yes)
                {
                    IdInputNotification idInputNotification = new IdInputNotification();
                    idInputNotification.Title = "注册指纹";
                    idInputNotification.IDnum = ShowFingerIDs() + 1;
                    IdInputNotificationRequest.Raise(idInputNotification, returned =>
                    {
                        if (returned != null && returned.Confirmed)
                        {
                            iID = returned.IDnum;
                        }
                    });
                    TipText = ("xxxxxxxxx");
                    Register(iID);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("FingerViewModel.cs::RegisterFingerFunc()注册指纹失败--" + ex.ToString());
                LogHelper.Error("FingerViewModel.cs::RegisterFingerFunc()注册指纹失败--" + ex.Message);
            }     
        }

   //注册
        private unsafe void Register(int iID)
        {

            int ret = 0;
            UInt32 nDevAddr = 0xffffffff;
            if (isconnect == false)
            {
                TipText = ("请先打开设备..."); return;
            }
            //3获取指纹图像  4.上传指纹图像(可省略) 5.显示指纹图像(可省略)  6.生成特征A  
            if (getchar(hHandle, nDevAddr, 1) != 1)
            { goto ENDERR; }
            Thread.Sleep(200);
    
            /****************合成模板*********/
            ret = Fingerdll.ZAZRegModule(hHandle, nDevAddr);  //合并特征
            if (ret != 0)
            { TipText =(Fingerdll.ZAZErr2Strt(ret)); goto ENDERR; }
            else
            { TipText =("合成指纹模板成功"); }
            Thread.Sleep(200);

            //本例以存在在指纹设备库中进行
            ret = Fingerdll.ZAZStoreChar(hHandle, nDevAddr, 1, iID);    //存放模板
            if (ret != 0)
            { TipText =(Fingerdll.ZAZErr2Strt(ret)); goto ENDERR; }
            else
            { TipText =("存储指纹成功"); ShowFingerIDs(); }

            ENDERR:
            return;
        }

        以上程序中,在 BEIG1进行了循环执行,所以阻塞了主线程,导致UI不能更新。但是在下面的这个程序,TipText确实可以更新,因为它没有阻塞主线程。

      /// <summary>
        /// 查找人脸库
        /// </summary>
        private void SearchAllFacesFunc()
        {
            try
            {
                accountList.Clear();
                QueryConditionsExpression = u => string.IsNullOrEmpty(null);              //账号表查询条件            
                accountList = settingConfigDal.GetAccountList(QueryConditionsExpression);
                foreach (AccountList singleAccount in accountList)
                {
                    if (singleAccount.AccountListFaceStoreFlag == "true")
                    {
                        strAllFaceIDs = strAllFaceIDs + "ID = " + singleAccount.AccountID.ToString() + "\n";
                    }
                }
                TipText = "人脸IDs:" + "\n" + strAllFaceIDs;
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("FaceViewModel.cs::FaceCountFunc()人脸总数失败--" + ex.ToString());
                LogHelper.Error("FaceViewModel.cs::FaceCountFunc()人脸总数失败--" + ex.Message);
            }
        }

2、解决办法创建一个次线程即可。

        原来的主线程有个函数在进行循环操作,导致UI不能更新。解决办法:

              Thread threadFingerRegister = new Thread(new ThreadStart(Register));
                    threadFingerRegister.Start();
                    threadFingerRegister.IsBackground = true;

四、总结

1、App.Current和System.Windows.Application.Current都表示主线程的意思。

2、Dispatcher.BeginInvoke异步委托、Dispatcher.Invoke同步委托。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值