转发地址:https://cloud.tencent.com/developer/article/1342244

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

先做一个例子让大家知道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>

这时点击按钮,可以看到动画有些卡,点击窗口拖动就可以看到动画正常。

wpf DoEvents 用法原理存在的坑推荐方法相关推荐

  1. python模块之HTMLParser之穆雪峰的案例(理解其用法原理)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python模块之HTMLParser之穆雪峰的案例(理解其用法原理) #http://www.cnblog ...

  2. JavaScript中foreach()用法及使用的坑

    JavaScript中foreach()用法及使用的坑 JavaScript中foreach是用于遍历数组的方法,将遍历到的元素传递给回调函数,遍历的数组不能是空的要有值. foreach 语法: [ ...

  3. python爬虫哪个选择器好用_Python网络爬虫四大选择器用法原理总结

    前几天小编连续写了四篇关于Python选择器的文章,分别用正则表达式.BeautifulSoup.Xpath.CSS选择器分别抓取京东网的商品信息.今天小编来给大家总结一下这四个选择器,让大家更加深刻 ...

  4. python语言变量随时命名随时赋值_Python变量及数据类型用法原理汇总

    变量(Variable)可以看成一个小箱子,专门用来"盛装"程序中的数据.每个变量都拥有独一无二的名字,通过变量的名字就能找到变量中的数据. 从底层看,程序中的数据最终都要放到内存 ...

  5. ALS算法原理和在音乐推荐上的应用

    ALS算法原理和在音乐推荐上的应用 ALS(Alternating least squares,交替最小二乘法)本来是一种数学上的优化方法,自从有人用它在Netflix大赛中使用于推荐系统,并获得冠军 ...

  6. 【TUM公开数据集RGBD-Benchmark工具evaluate_rpe.py参数用法原理解读】

    「零基础从零开始写VO视觉里程计」统计学.概率论.最小二乘.图优化(7-4) [TUM公开数据集RGBD-Benchmark工具associate.py参数用法原理解读] [TUM公开数据集RGBD- ...

  7. Vue--nextTick--作用/用法/原理

    原文网址:Vue--nextTick--作用/用法/原理_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Vue的nextTick的作用.用法.原理. 官网网址 API - Vue.js 作用 ...

  8. TransFM:基于因子分解机的序列推荐方法

    ▌概述 今天解读的论文是由 Rajiv Pasricha 和 Julian McAuley 两位大佬提出的发表在 RecSys18 上的,是 TransRec 和 FM 的结合版本.论文下载地址: h ...

  9. csrf漏洞防御方案_CSRF 漏洞原理详解及防御方法

    跨站请求伪造:攻击者可以劫持其他用户进行的一些请求,利用用户身份进行恶意操作. 例如:请求http://x.com/del.php?id=1是一个删除ID为1的账号,但是只有管理员才可以操作,如果攻击 ...

  10. 决策树算法原理以及决策树规则生成方法

    决策树算法原理以及决策树规则生成方法 决策树是一种可解释性较强的策略分析工具.creditmodel提供了分类回归树和条件推断树两种决策树生成和提取规则的方法. 每一个风险管理人员都应该掌握使用决策树 ...

最新文章

  1. js获取当前系统时间
  2. Nginx的配置文件nginx.conf详解
  3. Web前端_项目实践01_萌娃摄影网页(纯HTML+CSS静态页面)
  4. [批处理]使用Log.io监控日志变化
  5. Lambda标准格式
  6. 栈的顺序存储及实现(一)
  7. 华为将发布鲲鹏 920 芯片数据;三星 S10 自燃;Mageia 7 正式发布 | 极客头条
  8. 【C++】日期类+日期万年历+日期计算器
  9. 为 WE 打 Call!
  10. 短视频去水印解析二次运用--全网短视频解析去水印软件
  11. H3C交换机SNMP配置
  12. 计算机系统原理,实验三:bomblab,汇编详解
  13. 透过现象看本质,如何针对用户做好需求分析
  14. 小学生学程序---百变服装
  15. linux无root权限安装软件
  16. Unity 拖动UI物体(干货)
  17. VBA编程常用语句300句
  18. Host文件是什么?host文件有什么作用?
  19. sketch生成android布局,让 Sketch 小部件布局如此轻松:Compo
  20. 【STM32】在Keil5中创建一个工程模板-图文教程(超详细)

热门文章

  1. JS、H5调用手机相册摄像头以及文件夹
  2. NB-IOT 技术总结
  3. 上海电力大学本科毕业论文答辩PPT模板
  4. 极点五笔特殊符号输入方法
  5. 大一第一学期c语言考试题及答案,大一c语言试题及答案.doc
  6. 小米4A 32电视通过TTL方式ROOT的方法
  7. 微信小程序之直播功能使用详解
  8. postgresql数据库修改md5密码,口令认证
  9. 一键导出微信所有聊天记录的小工具
  10. 人工智能各层思维导图