一、非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;