学校英文版网站建设,专业排名优化工具,织梦网站防止注入,我想做电商怎么加入WPF支持单线程单元模型#xff0c;该模型与在Windows窗体应用程序中使用的模型非常类似#xff0c;具有以下几条原则#xff1a;
WPF元素具有线程关联性。创建WPF元素的线程拥有所创建的元素#xff0c;其他线程不能直接与这些WPF元素进行交互。WPF对象都在类层次的某个位…WPF支持单线程单元模型该模型与在Windows窗体应用程序中使用的模型非常类似具有以下几条原则
WPF元素具有线程关联性。创建WPF元素的线程拥有所创建的元素其他线程不能直接与这些WPF元素进行交互。WPF对象都在类层次的某个位置继承自DispatcherObject类DispatcherObject类提供了少量成员用于核实访问WPF对象的代码是否在正确的线程上执行如果没有是否能切换位置。
Dispatcher类
Disparcher类的实例为一个调度程序管理在WPF应用程序中发生的操作。调度程序拥有应用程序线程也就是拥有线程中创建的WPF元素并管理工作项队列当应用程序运行时调度程序接受新的工作请求并且一次执行一个任务。
在WPF中有一个基类DisparcherObject所有WPF组件如Window、Button等都继承自DispatcherObject当某个线程中第一次实例化DisparcherObject的子类时会创建一个调度程序。因此如果开多个线程每个线程都展示独立的窗体那么将会创建多个调度程序。但在一般情况下开发应用程序只使用一个用户界面线程和一个调度程序。
注意区分Dispatcher与DispatcherObject并不是父子类。
DispatcherObject类
一、常用成员
Dispatcher属性成员返回管理该对象的调度程序。
CheckAccess()如果代码在正确的线程上使用对象就返回true否则返回false。
VerifyAccess()如果代码在正确的线程上使用对象就什么也不做否则抛出InvalidOperationException异常。
一般情况下我们不需要自己去调用VerifyAccess()方法WPF对象为保护自身会频繁调用VerifyAccess()方法从而不可能在错误的线程中长时间使用一个对象。
在需要跨线程访问控件时可以通过控件的调度程序即Dispatcher对象的Invoke()或BeginInvoke()方法来将代码安排为调度程序的任务然后控件的调度程序会去执行这些代码。
BeginInvoke(DispatcherPriority priority, Delegate method)第一个参数指示任务的优先级为DispatcherPriority枚举类型一般情况下使用DispatcherPriority.Normal即可如果任务不需要被立即完成也可以使用更低的优先级第二个参数为一个方法的委托Delegate类型该委托指向具体任务的方法。
DispatcherPriority.ApplicationIdle等待应用程序在完成所有其他工作时执行指定的任务。DispatcherPriority.SystemIdle比ApplicationIdle优先级更低直到整个系统都处于休息状态并且CPU处于空闲状态才执行。
private void Button_Click(object sender, RoutedEventArgs e)
{Task.Run(() {Change();});
}private void Change()
{if (!CheckAccess()){Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() {lbl_Test.Content Test-ChangeOne;})); ;}else{lbl_Test.Content Test-ChangeTwo;}
}Invoke()Invoke()函数的参数是一个Action或Func类型对象与BeginInvoke的区别是BeginInvoke是异步执行的Invoke同步执行的使用Invoke时如果执行任务比较耗时会导致UI界面卡死。
private void Button_Click(object sender, RoutedEventArgs e)
{Task.Run(() {Change();});
}private void Change()
{if (!CheckAccess()){Dispatcher.Invoke(DispatcherPriority.Normal, new Action(({Thread.Sleep(5000);//这里做延时会发现UI界面卡住lbl_Test.Content Test-ChangeOne;})); }else{lbl_Test.Content Test-ChangeTwo;}
}二、Dispacher调度器对象的获取
常见的获取Dispacher调度器的方式有如下三种
直接调用Dispatcher属性
由于WPF中的绝大多是类型都是DispatcherObject的子类因此继承了Dispatcher属性可以直接在类中通过Dispatcher来获取。视图的后台代码继承了Window或UserControl等都是DispatcherObject的子类
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();Dispatcher.BeginInvoke(() {});}
}通过Dispatcher的静态属性
通过System.Windows.Threading命名空间下的Dispatcher属性可以获得当先线程的调度程序对象。
注意这里是获取当前线程的调度器对象并不一定能获得UI的调度器对象。
var dispatcher System.Windows.Threading.Dispatcher.CurrentDispatcher;通过Application获取
如果是在应用程序中如WPF可以通过Application.Current.Dispatcher来获取当前UI线程的调度程序对象。
这里可以直接获取到当前应用的UI调度器对象
var dispatcher Application.Current.Dispatcher;DispatcherTimer
在WPF中常常会遇到按照一定间隔时间执行同一个任务的场景这个时候就可以使用定时器DispatcherTimer来进行定时任务的设定了。
DispatcherTimer执行任务的线程是在UI调度器所在线程上所以可以在执行任务中直接访问和操作UI元素而不会引发线程安全问题。
常用的两种创建方式
//第一种
private DispatcherTimer? _timer;
private void MyTask(){ ... }
public MainWindowViewModel()
{_timer new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Loaded, new EventHandler((s, e) MyTask()), Application.Current.Dispatcher);
}//第二种
private DispatcherTimer _timer new DispatcherTimer();
private void MyTask(object? sender, EventArgs e) { ... }
public MainWindowViewModel()
{_timer.Interval TimeSpan.FromSeconds(1);_timer.Tick MyTask;
}计时器的开始与停止
_timer?.Start();
_timer?.Stop();