wpf DoEvents
原文:wpf DoEvents

如果在执行一段卡UI的代码,这时如何让UI响应。如果存在代码需要获得依赖属性,那么代码就需要在UI线程执行,但是这时就会卡UI,为了让UI响应,所以就需要使用DoEvents来让UI响应。 首先需要知道,DoEvents是在 WinForm 有的,在 WPF 没有这个函数,但是可以自己写出来。

目录

  1. 用法
  2. 原理
  3. 存在的坑
    1. OnLoad 上其他坑
    2. 使用 DispatcherTimer 出现窗口冻结
  4. 推荐方法

先做一个例子让大家知道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.InvokeDoEvents,然后运行拖动窗口,这时窗口卡死

        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 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。

posted on 2019-01-04 10:13 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10218324.html

wpf DoEvents相关推荐

  1. WPF 延时操作实现 --- Application.DoEvents()

    WPF 延时操作实现 - Application.DoEvents() 如果是在Winform中,我们可以这样实现 sleep延时方法: System.Threading.Thread.Sleep(1 ...

  2. 分享Silverlight/WPF/Windows Phone一周学习导读(07月18日-07月24日)

    上周,微软推出Silverlight新版官方网站,新网站综合旧版网站内容,并增加更多Silverlight学习资源以及案例展示,Silverlight官网是学习Silverlight开发技术的主要资源 ...

  3. 分享Silverlight/WPF/Windows Phone一周学习导读(12月13日-12月19日)

    每年的十二月,雪和圣诞都是主题.特别是国外的软件公司,大家都沉浸在节日和假期的快乐中.近一周,没有过于让开发人员兴奋的新消息,这期导读就总结一下上周的Silverlight,WPF和Windows P ...

  4. WPF刷新界面之坎坷路

    项目需要一个硬件检测功能,需要用到界面刷新,刚开始想用个定时器,对检测过的硬设定时添加后刷新界面. 但是很遗憾,定时器并不能进行刷新.后台检测List数据里面已经添加了很多了很多数据了,就是不能显示到 ...

  5. 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分)

    在上一篇文章 深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分) 中我们发现 Dispatcher.Invoke 方法内部是靠 Dispatcher.Pu ...

  6. WPF 键盘全局接收消息

    1.========================================================================== 在c#中怎样禁用鼠标左键的使用,其实我们可以通 ...

  7. 基于 WPF + Modern UI 的 公司OA小助手 开发总结

    前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会发现自己的不足. 公司每天都要在OA系统上上班点击签到,下班点击签退 ...

  8. 学习Modern UI for WPF

    这两天断断续续的学了学Modern UI for WPF 没啥学习笔记呵呵,来自大牛王春明的博客园 http://www.cnblogs.com/wangchunming/category/34288 ...

  9. [转][小结][三种方法]实现WPF不规则窗体

    实现WPF不规则窗体的三种常用的方法如下: 1.使用Blend等工具绘制一个不规则xaml,然后作为窗体的背景.这个可以参考xiaowei0705的这篇博文:WPF制作不规则的窗体 . 2.给wind ...

  10. WPF:跨应用程序会话保持和还原应用程序范围的属性

    所谓的wpf夸应用程序员会话保持和还原.其实就是将多个应用程序都用的资源保存到一个独立的文件存储系统中.这个应用程序退出的时候将数据写入文件中,其他应用程序使用的时候可以去读取这个文件 这个地方用到了 ...

最新文章

  1. 【机器视觉】 global算子
  2. html中的盒子设置时间设置,CSS中的间距设置与盒子模型
  3. 流程管理软件如何适应变化
  4. vs怎么写html5页面,怎么使用vscode写html5
  5. 【华为云技术分享】云图说 | 初识云耀云服务器,打造“极优、极简”的云上体验
  6. java 重力脚本_Java中非常简单的脚本解析器
  7. 用Entlib的配置程序块遇到的问题
  8. asp.net中使用excel类导出Excel文件,并导出到web客户端中遇到的问题
  9. 计算ex值 c语言编译,C语言常用的数学符号.doc
  10. 路由器下一跳地址怎么判断_路由器的功能及工作原理
  11. Tomcat之—— linux/centos 解决Tomcat内存溢出
  12. B站视频下载与字幕下载转换
  13. osip和mysql_osip2/eXosip2调试笔记
  14. xmapp mysql启动失败 Attempting to start MySQL service...
  15. 分数阶微积分基本理论(课堂笔记1)
  16. C++使用模板重载vector的加减法实现矩阵向量加减法
  17. 【读书分享】《解忧杂货店》东野圭吾
  18. 计算机ip地址是指什么作用是什么,ip地址的作用是什么
  19. 斐迅路由器刷华硕固件
  20. PLUS模型教程五:多情景设置,附全套教程练习数据

热门文章

  1. 好用的dns服务器工具有哪些?
  2. Python绘图模块 -- turtle
  3. ONLYoffice在线编辑的接口测试
  4. 强化学习经典算法笔记(十七):A3C算法的PyTorch实现
  5. 将数字转换为中文大写(缩写)
  6. html表格收起展开,vue-table-element表格的全部展开和全部折叠
  7. 图像处理算法工程师面试题
  8. 字节跳动计算机视觉算法工程师面试题(秋招)
  9. 中兴ZXR10_5952E交换机配置SNMP
  10. python·文本分析