wpf DoEvents 用法原理存在的坑推荐方法
转发地址: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.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>
这时点击按钮,可以看到动画有些卡,点击窗口拖动就可以看到动画正常。
wpf DoEvents 用法原理存在的坑推荐方法相关推荐
- python模块之HTMLParser之穆雪峰的案例(理解其用法原理)
# -*- coding: utf-8 -*- #python 27 #xiaodeng #python模块之HTMLParser之穆雪峰的案例(理解其用法原理) #http://www.cnblog ...
- JavaScript中foreach()用法及使用的坑
JavaScript中foreach()用法及使用的坑 JavaScript中foreach是用于遍历数组的方法,将遍历到的元素传递给回调函数,遍历的数组不能是空的要有值. foreach 语法: [ ...
- python爬虫哪个选择器好用_Python网络爬虫四大选择器用法原理总结
前几天小编连续写了四篇关于Python选择器的文章,分别用正则表达式.BeautifulSoup.Xpath.CSS选择器分别抓取京东网的商品信息.今天小编来给大家总结一下这四个选择器,让大家更加深刻 ...
- python语言变量随时命名随时赋值_Python变量及数据类型用法原理汇总
变量(Variable)可以看成一个小箱子,专门用来"盛装"程序中的数据.每个变量都拥有独一无二的名字,通过变量的名字就能找到变量中的数据. 从底层看,程序中的数据最终都要放到内存 ...
- ALS算法原理和在音乐推荐上的应用
ALS算法原理和在音乐推荐上的应用 ALS(Alternating least squares,交替最小二乘法)本来是一种数学上的优化方法,自从有人用它在Netflix大赛中使用于推荐系统,并获得冠军 ...
- 【TUM公开数据集RGBD-Benchmark工具evaluate_rpe.py参数用法原理解读】
「零基础从零开始写VO视觉里程计」统计学.概率论.最小二乘.图优化(7-4) [TUM公开数据集RGBD-Benchmark工具associate.py参数用法原理解读] [TUM公开数据集RGBD- ...
- Vue--nextTick--作用/用法/原理
原文网址:Vue--nextTick--作用/用法/原理_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Vue的nextTick的作用.用法.原理. 官网网址 API - Vue.js 作用 ...
- TransFM:基于因子分解机的序列推荐方法
▌概述 今天解读的论文是由 Rajiv Pasricha 和 Julian McAuley 两位大佬提出的发表在 RecSys18 上的,是 TransRec 和 FM 的结合版本.论文下载地址: h ...
- csrf漏洞防御方案_CSRF 漏洞原理详解及防御方法
跨站请求伪造:攻击者可以劫持其他用户进行的一些请求,利用用户身份进行恶意操作. 例如:请求http://x.com/del.php?id=1是一个删除ID为1的账号,但是只有管理员才可以操作,如果攻击 ...
- 决策树算法原理以及决策树规则生成方法
决策树算法原理以及决策树规则生成方法 决策树是一种可解释性较强的策略分析工具.creditmodel提供了分类回归树和条件推断树两种决策树生成和提取规则的方法. 每一个风险管理人员都应该掌握使用决策树 ...
最新文章
- js获取当前系统时间
- Nginx的配置文件nginx.conf详解
- Web前端_项目实践01_萌娃摄影网页(纯HTML+CSS静态页面)
- [批处理]使用Log.io监控日志变化
- Lambda标准格式
- 栈的顺序存储及实现(一)
- 华为将发布鲲鹏 920 芯片数据;三星 S10 自燃;Mageia 7 正式发布 | 极客头条
- 【C++】日期类+日期万年历+日期计算器
- 为 WE 打 Call!
- 短视频去水印解析二次运用--全网短视频解析去水印软件
- H3C交换机SNMP配置
- 计算机系统原理,实验三:bomblab,汇编详解
- 透过现象看本质,如何针对用户做好需求分析
- 小学生学程序---百变服装
- linux无root权限安装软件
- Unity 拖动UI物体(干货)
- VBA编程常用语句300句
- Host文件是什么?host文件有什么作用?
- sketch生成android布局,让 Sketch 小部件布局如此轻松:Compo
- 【STM32】在Keil5中创建一个工程模板-图文教程(超详细)