wpf DoEvents
如果在执行一段卡UI的代码,这时如何让UI响应。如果存在代码需要获得依赖属性,那么代码就需要在UI线程执行,但是这时就会卡UI,为了让UI响应,所以就需要使用DoEvents
来让UI响应。 首先需要知道,DoEvents
是在 WinForm 有的,在 WPF 没有这个函数,但是可以自己写出来。
- 用法
- 原理
- 存在的坑
- OnLoad 上其他坑
- 使用 DispatcherTimer 出现窗口冻结
- 推荐方法
先做一个例子让大家知道DoEvents
的作用,使用的呆磨很简单,请看代码
<Window x:Class="ZuindmMbx.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:ZuindmMbx"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Grid><ListView ItemsSource="{Binding KatudefZubpobryk}"><ListView.ItemTemplate><DataTemplate><TextBlock Text="{Binding}"></TextBlock></DataTemplate></ListView.ItemTemplate></ListView><Button Content="确定" HorizontalAlignment="Left" Margin="424,292,0,0" VerticalAlignment="Top" Width="75" Click="Button_OnClick"/></Grid>
</Window>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();DataContext = this;}public ObservableCollection<string> KatudefZubpobryk { get; set; } = new ObservableCollection<string>();private void Button_OnClick(object sender, RoutedEventArgs e){for (int i = 0; i < 10; i++){Foo(10);KatudefZubpobryk.Add(i.ToString());}}private void Foo(int n){for (int i = 0; i < n; i++){Foo(n - 1);}}}
这时点击确定可以看到,需要等待一些时间才可以响应界面
如果加上了 DoEvents 就可以看到下图的效果
用法
在呆磨的程序做一些修改,请看代码
private void Button_OnClick(object sender, RoutedEventArgs e){for (int i = 0; i < 10; i++){Foo(10);KatudefZubpobryk.Add(i.ToString());DoEvents();}}public static void DoEvents(){DispatcherFrame frame = new DispatcherFrame();Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);Dispatcher.PushFrame(frame);}private static Object ExitFrame(Object state){((DispatcherFrame) state).Continue = false;return null;}
所以只需要在循环加上代码就可以了。可以复制下面的两个方法到需要使用让UI响应的地方,在需要的地方调用,使用的方法很简单。
建议在下面的地方使用:
- 后台操作比较耗时,未完全加载也能正常使用
- 性能已经没有办法优化
- 性能没有时间优化,可作为临时性方案
- DoEvents建议一定是在主线程上使用
原理
请看一下底层的PushFrameImpl
下面的代码有删减
会导致UI重绘的消息:0xC25A及0xC262 所以发送这个消息就可以让UI响应
存在的坑
这里的坑是 PushFrame 的坑,关于他的原理,请看 https://walterlv.github.io/post/dotnet/2017/09/26/dispatcher-push-frame.html
如果点击确定按钮之后,再次点击确定按钮,那么就会出现很多个重复的数。如果使用这个方法,那么需要禁用确定按钮,小心用户多次点击。
在使用方法的时候拖动窗口,可能让窗口卡死。
复现步骤:
修改上面呆磨代码,加上OnLoaded
,里面使用Dispatcher.Invoke
或DoEvents
,然后运行拖动窗口,这时窗口卡死
public MainWindow(){InitializeComponent();DataContext = this;Loaded += OnLoaded;}private async void OnLoaded(object sender, RoutedEventArgs e){await Task.Delay(2000);Dispatcher.Invoke(() => { }, DispatcherPriority.Background);}
但是这时使用 Alt+Tab 到其他窗口,然后回来,可以看到窗口正常
实际上尝试改变窗口大小也会让窗口卡死,请看WPF application intermittently hangs when using Dispatcher.Invoke and/or Dispatcher.PushFrame while user is resizing or draging window
OnLoad 上其他坑
我必须说,不仅是 OnLoad 会出现这些坑,在很多情况也会,但是我还不知道条件。
请把await Task.Delay(2000)
换为Foo(10);
进行一些计算,这时在软件启动的时候,尝试拖动窗口,可以看到窗口是没有显示内容,但是鼠标放开的时候,就可以看到界面显示。
private void OnLoaded(object sender, RoutedEventArgs e){Foo(10);Dispatcher.Invoke(() =>{}, DispatcherPriority.Background);}
接着把Invoke
换为DoEvents
,结果相同,在启动拖动窗口,窗口没有内容。
使用 DispatcherTimer 出现窗口冻结
下面的代码是创建一个 time 不停在里面使用Dispatcher.Invoke
public MainWindow(){InitializeComponent();DataContext = this;Loaded += OnLoaded;DispatcherTimer time = new DispatcherTimer();time.Interval = new TimeSpan(0, 0, 1);time.Tick += Time_Tick;time.Start();}private void Time_Tick(object sender, EventArgs e){Foo(10);Dispatcher.Invoke(() => { }, DispatcherPriority.Background);}
这时拖动窗口会出现冻结,和上面一样。
实际把上面代码的运算去掉也会冻住,但是我尝试10次,有2次在放开的时候才冻住。
推荐方法
实际上垃圾wr是不是要让开发者去写这样的方法?实际上垃圾wr已经做了这个东西,但是没有直接告诉开发者,请尝试使用下面的代码代替上面呆磨
private void Button_OnClick(object sender, RoutedEventArgs e){for (int i = 0; i < 10; i++){Foo(10);KatudefZubpobryk.Add(i.ToString());Dispatcher.Invoke(() => { }, DispatcherPriority.Background);}}
关键就是Dispatcher.Invoke(() => { }, DispatcherPriority.Background);
,这句代码就是在主线程插入一个Background
因为优先级,所以这时就可以让UI处理其他的输入
但是直接使用Dispatcher.Invoke
代码太长,是不是可以使用比较简单的?实际上还是有的,请看代码。
private async void Button_OnClick(object sender, RoutedEventArgs e){for (int i = 0; i < 10; i++){Foo(10);KatudefZubpobryk.Add(i.ToString());await System.Windows.Threading.Dispatcher.Yield();}}
实际上System.Windows.Threading.Dispatcher.Yield
这个方法的实现和Dispatcher.Invoke(() => { }, DispatcherPriority.Background
一点也不同,他使用的是 async 以及其他我还不知道怎么说的科技。
最后的方法是在UI主线程执行的函数上添加async
和直接使用Dispatcher.Yield
就可以在循环中让UI响应。不会在循环中让UI卡住。
建议使用最后的方法,因为这个方法可以解决坑,而且使用简单
实际上,使用了上面无论哪个方法都不会让界面一直都响应,如果页面有一个循环的动画,就可以看到动画播放实际上有些卡,下面写一个呆磨就可以知道。在上面的界面添加下面的代码,不停做动画。
<Grid><Grid.Triggers><EventTrigger RoutedEvent="Grid.Loaded"><BeginStoryboard><Storyboard RepeatBehavior="Forever"><DoubleAnimation Storyboard.TargetName="T" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:1"></DoubleAnimation></Storyboard></BeginStoryboard></EventTrigger></Grid.Triggers><Grid x:Name="G" Background="#565656" Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center"><Grid.RenderTransform><RotateTransform x:Name="T" CenterX="100" CenterY="100" Angle="0"></RotateTransform></Grid.RenderTransform></Grid></Grid>
这时点击按钮,可以看到动画有些卡,点击窗口拖动就可以看到动画正常。
本文会经常更新,请阅读原文: https://lindexi.gitee.io/lindexi/post/wpf-DoEvents.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://lindexi.gitee.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。
转载于:https://www.cnblogs.com/lonelyxmas/p/10218324.html
wpf DoEvents相关推荐
- WPF 延时操作实现 --- Application.DoEvents()
WPF 延时操作实现 - Application.DoEvents() 如果是在Winform中,我们可以这样实现 sleep延时方法: System.Threading.Thread.Sleep(1 ...
- 分享Silverlight/WPF/Windows Phone一周学习导读(07月18日-07月24日)
上周,微软推出Silverlight新版官方网站,新网站综合旧版网站内容,并增加更多Silverlight学习资源以及案例展示,Silverlight官网是学习Silverlight开发技术的主要资源 ...
- 分享Silverlight/WPF/Windows Phone一周学习导读(12月13日-12月19日)
每年的十二月,雪和圣诞都是主题.特别是国外的软件公司,大家都沉浸在节日和假期的快乐中.近一周,没有过于让开发人员兴奋的新消息,这期导读就总结一下上周的Silverlight,WPF和Windows P ...
- WPF刷新界面之坎坷路
项目需要一个硬件检测功能,需要用到界面刷新,刚开始想用个定时器,对检测过的硬设定时添加后刷新界面. 但是很遗憾,定时器并不能进行刷新.后台检测List数据里面已经添加了很多了很多数据了,就是不能显示到 ...
- 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分)
在上一篇文章 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分) 中我们发现 Dispatcher.Invoke 方法内部是靠 Dispatcher.Pu ...
- WPF 键盘全局接收消息
1.========================================================================== 在c#中怎样禁用鼠标左键的使用,其实我们可以通 ...
- 基于 WPF + Modern UI 的 公司OA小助手 开发总结
前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会发现自己的不足. 公司每天都要在OA系统上上班点击签到,下班点击签退 ...
- 学习Modern UI for WPF
这两天断断续续的学了学Modern UI for WPF 没啥学习笔记呵呵,来自大牛王春明的博客园 http://www.cnblogs.com/wangchunming/category/34288 ...
- [转][小结][三种方法]实现WPF不规则窗体
实现WPF不规则窗体的三种常用的方法如下: 1.使用Blend等工具绘制一个不规则xaml,然后作为窗体的背景.这个可以参考xiaowei0705的这篇博文:WPF制作不规则的窗体 . 2.给wind ...
- WPF:跨应用程序会话保持和还原应用程序范围的属性
所谓的wpf夸应用程序员会话保持和还原.其实就是将多个应用程序都用的资源保存到一个独立的文件存储系统中.这个应用程序退出的时候将数据写入文件中,其他应用程序使用的时候可以去读取这个文件 这个地方用到了 ...
最新文章
- 【机器视觉】 global算子
- html中的盒子设置时间设置,CSS中的间距设置与盒子模型
- 流程管理软件如何适应变化
- vs怎么写html5页面,怎么使用vscode写html5
- 【华为云技术分享】云图说 | 初识云耀云服务器,打造“极优、极简”的云上体验
- java 重力脚本_Java中非常简单的脚本解析器
- 用Entlib的配置程序块遇到的问题
- asp.net中使用excel类导出Excel文件,并导出到web客户端中遇到的问题
- 计算ex值 c语言编译,C语言常用的数学符号.doc
- 路由器下一跳地址怎么判断_路由器的功能及工作原理
- Tomcat之—— linux/centos 解决Tomcat内存溢出
- B站视频下载与字幕下载转换
- osip和mysql_osip2/eXosip2调试笔记
- xmapp mysql启动失败 Attempting to start MySQL service...
- 分数阶微积分基本理论(课堂笔记1)
- C++使用模板重载vector的加减法实现矩阵向量加减法
- 【读书分享】《解忧杂货店》东野圭吾
- 计算机ip地址是指什么作用是什么,ip地址的作用是什么
- 斐迅路由器刷华硕固件
- PLUS模型教程五:多情景设置,附全套教程练习数据