一、概述
在應用程序中,線程可以被看做是應用程序的一個較小的執行單位。每個應用程序都至少擁有一個線程,我們稱為主線程。當調用和操作主線程的時候,該操作將動作添加到一個隊列中,每個操作均按照將它們添加到隊列中的順序連續執行,但是可以通過為這些動作指定優先級來影響執行順序,而負責管理此隊列的對象稱之為線程調度程序。
我們知道,WPF程序中,控件是屬於UI線程的,如果試圖在子線程中訪問或者更新UI,就需要在子線程中通知主線程來處理UI, 通過向主線程的Dispatcher隊列注冊工作項,來通知UI線程更新結果。
Dispatcher提供兩個注冊工作項的方法:Invoke 和 BeginInvoke。
這兩個方法均調度一個委托來執行。Invoke 是同步調用,也就是說,直到 UI 線程實際執行完該委托它才返回。BeginInvoke是異步的,將立即返回。
代碼片段如下:
this.Dispatcher.BeginInvoke((Action)delegate() { 更新UI控件ing;
});
二、MVVMLight模式下ViewModel中更新UI
通常情況下,ViewModel 不從 DispatcherObject 繼承,不能訪問 Dispatcher 屬性。這時候,我們需要使用DispatcherHelper 組件來更新UI。
實際上,該類所做的是將主線程的調度程序保存在靜態屬性中,並公開一些實用的方法,以便通過統一的方式訪問。
為了實現正常功能,需要在主線程上初始化該類。
通常,在 MVVM Light 應用程序中,DispatcherHelper 可以在 App.xaml.cs 或者ViewModel的構造函數中進行初始化,App.xaml.cs 是定義應用程序啟動類的文件。
在 WPF 中,該類一般是在 App 構造函數中進行初始化的。
DispatcherHelper組件初始化以后,DispatcherHelper 類的 UIDispatcher 屬性包含對主線程的調度程序的引用。
但是一般很少直接使用該屬性,雖然確實可以使用。通常我們會使用 CheckBeginInvokeOnUi 方法來更新UI。
代碼片段如下:
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Threading; using MvvmLightClosableTabControl.Models; using MvvmLightClosableTabControl.Pages; using System; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Threading; namespace MvvmLightClosableTabControl.ViewModel { public class MainViewModel : ViewModelBase { private ObservableCollection<TabItemModel> tabItemsList = new ObservableCollection<TabItemModel>() { new TabItemModel(){ Img="\\MvvmLightClosableTabControl;component\\Img\\1.png",Uri="\\Pages\\Page1.xaml",IsSelected=true , Header="TabItem1" }, new TabItemModel(){ Img="\\MvvmLightClosableTabControl;component\\Img\\2.png",Uri="\\Pages\\Page2.xaml",IsSelected=false, Header="TabItem2" }, new TabItemModel(){ Img="\\MvvmLightClosableTabControl;component\\Img\\3.png",Uri="\\Pages\\Page3.xaml",IsSelected=false, Header="TabItem3" }, new TabItemModel(){ Img="\\MvvmLightClosableTabControl;component\\Img\\4.png",Uri="\\Pages\\Page4.xaml",IsSelected=false, Header="TabItem4" }, }; public ObservableCollection<TabItemModel> TabItemsList { get { return tabItemsList; } set { tabItemsList = value; RaisePropertyChanged(() => TabItemsList); } } public MainViewModel() { DispatcherHelper.Initialize(); } #region Command private RelayCommand closeCurrentTabItemCommand; public RelayCommand CloseCurrentTabItemCommand { get { if (closeCurrentTabItemCommand == null) { closeCurrentTabItemCommand = new RelayCommand(CloseCurrentTabItemImpl); } return closeCurrentTabItemCommand; } set { closeCurrentTabItemCommand = value; } } private void CloseCurrentTabItemImpl() { foreach(var item in TabItemsList) { if(item.IsSelected == true) { TabItemsList.Remove(item); break; } } } //傳遞一個字符串參數的命令 private RelayCommand<string> addPageCommand; public RelayCommand<string> AddPageCommand { get { if (addPageCommand == null) { addPageCommand = new RelayCommand<string>(AddPage); } return addPageCommand; } set { addPageCommand = value; } } private void AddPage(string page) { try { TabItemModel myTabItemModel = new TabItemModel() { Img = $"\\MvvmLightClosableTabControl;component\\Img\\{page[4]}.png", Uri = $"\\Pages\\{page}.xaml", IsSelected = true, Header = page }; Task.Run( () => DispatcherHelper.CheckBeginInvokeOnUI( () => { TabItemsList.Add(myTabItemModel); } ));//故意在子線程中添加,為了使用DispatcherHelper,在子線程中訪問UI } catch (AggregateException err) { foreach (var iem in err.InnerExceptions) { string msg = $"{iem.GetType()}{iem.Source}{iem.Message}"; MessageBox.Show(msg); } } } //傳遞事件參數的命令 private RelayCommand<MouseButtonEventArgs> tabItemMouseDoubleClickCommand; public RelayCommand<MouseButtonEventArgs> TabItemMouseDoubleClickCommand { get { if (tabItemMouseDoubleClickCommand == null) { tabItemMouseDoubleClickCommand = new RelayCommand<MouseButtonEventArgs>(TabItemMouseDoubleClickImpl); } return tabItemMouseDoubleClickCommand; } set { tabItemMouseDoubleClickCommand = value; } } private int _clickCnt = 0; private void TabItemMouseDoubleClickImpl(MouseButtonEventArgs e) { _clickCnt += 1; DispatcherTimer timer = new DispatcherTimer(); timer.Interval = new TimeSpan(0, 0, 0, 0, 300); timer.Tick += (s, e1) => { timer.IsEnabled = false; _clickCnt = 0; }; timer.IsEnabled = true; if (_clickCnt %2 == 0) { foreach (var item in TabItemsList) { if (item.IsSelected == true) { TabItemsList.Remove(item); PageWindow win = new PageWindow(); win.frm.Source = new System.Uri(item.Uri, UriKind.Relative); win.Title = item.Header; win.Show(); break; } } } } #endregion } }
