2018-8-10-wpf-DoEvents-
| title | author | date | CreateTime | categories |
|---|---|---|---|---|
|
wpf DoEvents
|
lindexi
|
2018-08-10 19:16:51 +0800
|
2018-2-13 17:23:3 +0800
|
WPF doevents 性能优化
|
如果在执行一段卡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>
这时点击按钮,可以看到动画有些卡,点击窗口拖动就可以看到动画正常。
2018-8-10-wpf-DoEvents-的更多相关文章
- wpf DoEvents
原文:wpf DoEvents 如果在执行一段卡UI的代码,这时如何让UI响应.如果存在代码需要获得依赖属性,那么代码就需要在UI线程执行,但是这时就会卡UI,为了让UI响应,所以就需要使用DoEve ...
- 申请Office 365一年免费的开发者账号攻略(2018年10月份版本)
要进行Office 365开发,当然需要有完整的Office 365环境才可以.为了便于广大开发人员快速地启动这项工作,微软官方给所有开发人员提供了免费的一年开发者账号 那么如何申请Office ...
- IntelliJ IDEA 最新激活码(截止到2018年10月14日)
IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYX ...
- 新手C#SQL Server使用记录2018.08.10
主键(PrimaryKey):主键就是每个数据行(记录)的唯一标识,不会有重复值的列(字段)才能当做主键.一个表可以没有主键,但是这样会很难处理表,因此一般情况表都要设置主键. 主键有两张选用策略,分 ...
- 01 mybatis框架整体概况(2018.7.10)-
01 mybatis框架整体概况(2018.7.10)- F:\廖雪峰 JavaEE 企业级分布式高级架构师课程\廖雪峰JavaEE一期\第一课(2018.7.10) maven用的是3.39的版本 ...
- 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H)
目录 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H) 竞赛事件相关 竞赛链接 竞赛题目 总结 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H) 竞赛事件相关 竞赛 ...
- 富士康的盈利秒杀99%的A股公司:3星|《三联生活周刊》2018年10期
三联生活周刊·最美的数学:天才为何成群到来(2018年10期) 本期专题是数学和成都,我都跳过去没看.其他内容也还有点意思. 总体评价3星. 以下是本期一些内容的摘抄,#号后面是kindle电子版中的 ...
- Burn Down Chart(2018.6.4~2018.6.10)
Burn Down Chart (2018.6.4~2018.6.10) 娄雨禛[前端部分] 曾子轩[后端部分+燃尽图] 前端 1. 娄雨禛+李鑫 1)在总工程中完成跳转,实现图片显示,并发布到Git ...
- Java分布式互联网架构/微服务/高性能/springboot/springcloud 2018年10月17日直播内容
2018年10月17日直播内容 大规模并发必备的消息中间件技术ActiveMq 网盘链接: https://pan.baidu.com/s/1GlxsZ2JnrvX- YN16-S7lQw 提取码: ...
- 【福州活动】| "福州首届.NET开源社区线下技术交流会"(2018.11.10)
活动介绍 微软爱开源,已是尽人皆知的事实.自从收购全球最大的开源社区 GitHub 之后,微软依旧使 GitHub 保持独立运营,并且通过此项举措,微软本身已经成为最大的社区服务者. .NET Cor ...
随机推荐
- 2018-8-29-Roslyn-静态分析
title author date CreateTime categories Roslyn 静态分析 lindexi 2018-08-29 09:10:19 +0800 2018-03-13 14: ...
- 2019-8-31-matlab-画图
title author date CreateTime categories matlab 画图 lindexi 2019-08-31 16:55:59 +0800 2018-2-13 17:23: ...
- git安装和使用方法url
1.如何在ubuntu下使用Github? https://blog.csdn.net/tina_ttl/article/details/51326684 https://segmentfault.c ...
- ES6(阮一峰)对象的扩展
1.属性的简洁表示法 允许直接写入变量和函数,作为对象的属性和方法. const foo = 'bar'; const baz = {foo};//等同于 const baz = {foo : &qu ...
- 【JavaWeb项目】一个众筹网站的开发(七)后台用户菜单
mvn命令不能运行: jar-war-pom之间是可以直接写,优先找这个工程,而不是仓库的位置 pom-pom子父关系,需要去仓库中找,我们需要使用<relativePath>../pro ...
- luoguP2148 [SDOI2009]E&D [sg函数][组合游戏]
题目描述 小E 与小W 进行一项名为“E&D”游戏. 游戏的规则如下: 桌子上有2n 堆石子,编号为1..2n.其中,为了方便起见,我们将第2k-1 堆与第2k 堆 (1 ≤ k ≤ n)视为 ...
- Android中如何搭建一个WebServer
今天终于把老大交代的任务搞完了,感觉收获挺多的,所以就写一篇来记录一下吧,首先还是来看一下,老大们的需求 需求: 希望移动端的用户标识(IMEI)和HTML页面的用户标识(Cookie)连接起来,其中 ...
- HDU 4866 Shooting 题解:主席树
这题的主要的坑点就是他给你的射击目标有重合的部分,如果你向这些重合的部分射击的话要考虑两种情况: 射击目标数量 ≥ 重合数量 : 全加上 射击目标数量 ≤ 重合数量 : 只加距离*射击目标数量 然而这 ...
- 管理员技术(一):装机预备技能、安装一台RHEL7虚拟机、使用RHEL7图形桌面、Linux命令行基本操作
一.装机预备技能 问题: 本例要求安装一台可用的KVM服务器: 1> RHEL与CentOS系统有什么关联? 2> 第2块SCSI硬盘的第3个逻辑分区,Linux如何表 ...
- Jquery轻量级插件--操作URL
调用: > "?action=view§ion=info&id=123&debug&testy[]=true&testy[]=false ...