WPF中UI线程频繁操作造成卡顿的处理

问题描述

有些时候,在项目开发中可能需要在某一很短时间内频繁操作UI控件,如以循环方式中瞬间向UI界面上添加1000张图片。这个任务场景有2个特点:

(1)所完成的任务是耗时任务------要频繁读取图片和加载图片。

(2)加载图片是UI操作,无法在非UI线程中完成。

解决办法:

能否每加载一个图片更新一下UI界面?而不是1000张图片读取完毕后一次性附加到界面上显示。

预备知识:

(1)WPF中Invoke和BeginInvoke方法,参考链接:http://www.cnblogs.com/Z-King/archive/2011/11/03/2234337.html

(2)Dispatcher的认识,参考链接:http://blog.csdn.net/albert528108/article/details/51503955

(3)DispatcherPriority的认识,参考链接:https://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcherpriority.aspx

(4)依赖属性、INotifyPropertyChanged、ObservableCollection相关知识。

作者:hhp895

链接:https://www.jianshu.com/p/1d19514bccea

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

分析:

(1)所有UI的操作必须在UI线程上进行,无法在子线程中进行。

(2)线程上的操作又由Dispatcher分为不同的优先级。如果不希望UI上出现卡顿的情况,就必须将UI线程的图片加载(render)操作的优先级别降到UI线程上输入(input)操作的优先级之下。也就是input操作优先于图片呈现,界面就不会出现卡死状态。

(3)所有操作必须异步进行,这样不会堵塞线程。为了不让1000张图片同时加载到界面上,这里逐步调用BeginInvoke方法,每一次只比上一次多加载一张图片。何时终止,采用递归方式反复调用直到读完图片为止。

做法一:

未逐步加载,一次加载图片的做法。代码:

复制代码
 private void Btn_Click(object sender, RoutedEventArgs e)
       {
              strings = loadDir(@"G:\BaiduYunDownload\风景图片壁纸\风景图片壁纸100张");

              lb.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
             {
                     lb.ItemsSource = loadDir(@"G:\BaiduYunDownload\风景图片壁纸\风景图片壁纸100张");
             }));
      }

效果图如下:

所有图片明显一次性出现,按钮点击后直接卡住。等所有图片都出现后,按钮的卡住状态恢复正常。

做法二:

图片一张张逐步加载,每次多增加一张图片。代码:

复制代码
 int i;
        List<String> strings;
        ObservableCollection<String> strs = new ObservableCollection<string>();
        private void Btn_Click(object sender, RoutedEventArgs e)
        {
            strings = loadDir(@"G:\BaiduYunDownload\风景图片壁纸\风景图片壁纸100张");
            strs.Clear();
            i = 0;
            lb.ItemsSource = strs;
            lb.Dispatcher.BeginInvoke(DispatcherPriority.Background, new AddItemDelegate(addItem));
        }

        private delegate void AddItemDelegate();
        private void addItem()
        {
            if (i < strings.Count)
            {
                strs.Add(strings[i++]);
                lb.Dispatcher.BeginInvoke(DispatcherPriority.Background,new AddItemDelegate(addItem));
            }
        }
        private List<String> loadDir(string dirpath)
        {
            List<String> strs = new List<string>();
            if (Directory.Exists(dirpath))
            {
                foreach (var item in new DirectoryInfo(dirpath).GetFiles("*.jpg"))
                {
                    strs.Add(item.FullName);
                }
            }
            return strs;
        }

效果图如下:

图片逐步加载,每次比上次多增加一张。按钮点击后立即恢复原来状态,UI无卡顿。

做法三:

做法背景

为了避免窗口UI操作卡顿,在Windows Form开发年代,微软就提出一个解决方案,在处理UI刷新时,使用Application.DoEvents()立即更新界面。参考msdn链接https://msdn.microsoft.com/zh-cn/library/system.windows.forms.application.doevents.aspx

预备知识

(1)在WPF中如何改造实现DoEvents()方法。动态绘制心电图,参考链接:https://social.msdn.microsoft.com/Forums/zh-CN/febcee07-dc8b-44b4-8c0a-246daffdbe2b/wpf-?forum=wpfzhchs

(2)网上早有大师对该种做法进行了深度分析------《从Dispatcher.PushFrame()说起》http://www.cnblogs.com/loveis715/archive/2012/01/11/2319976.html

分析

实际上都是通过DoEvents()方法立即实现UI的重绘,而不是等所有图片加载完毕一次性显示,那样UI界面就卡住了。

代码:

复制代码
 int i;
        List<String> strings;
        ObservableCollection<String> strs = new ObservableCollection<string>();
        private void Btn_Click(object sender, RoutedEventArgs e)
        {
            strings = loadDir(@"G:\BaiduYunDownload\风景图片壁纸\风景图片壁纸100张");
            strs.Clear();

            lb.ItemsSource = strs;

            for (int j = 0; j < strings.Count; j++)
            {
                strs.Add(strings[j]);
                DoEvents();
            }
        }
        public void DoEvents()
        {
            DispatcherFrame frame = new DispatcherFrame();
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
                new DispatcherOperationCallback(ExitFrames), frame);
            Dispatcher.PushFrame(frame);
        }
        public object ExitFrames(object f)
        {
            ((DispatcherFrame)f).Continue = false;
            return null;
        }

效果

效果与做法二一样。

相关推荐
人工智能AI技术1 小时前
【C#程序员入门AI】本地大模型落地:用Ollama+C#在本地运行Llama 3/Phi-3,无需云端
人工智能·c#
北极糊的狐3 小时前
光标放在引入的xxx组件行(import { xxx } from ‘element-ui‘;)出现标黄,显示报错:无法解析符号 ‘xxx‘ 解决办法
ui
子春一4 小时前
Flutter for OpenHarmony:构建一个 Flutter 习惯打卡应用,深入解析周视图交互、连续打卡逻辑与状态驱动 UI
flutter·ui·交互
我的xiaodoujiao5 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 44--将自动化测试结果自动推送至钉钉工作群聊
前端·python·测试工具·ui·pytest
MyBFuture5 小时前
C#数组详解:一维二维与交错数组
开发语言·windows·c#·visual studio·vision pro
LcVong5 小时前
WPF MediaPlayer获取网络视频流当前帧并展示图片完整范例
网络·wpf
UI设计兰亭妙微6 小时前
UI 设计新范式:从国际案例看体验与商业的融合之道
人工智能·ui·b端设计
子春一6 小时前
Flutter for OpenHarmony:构建一个 Flutter 天气卡片组件,深入解析动态 UI、响应式布局与语义化设计
javascript·flutter·ui
雨季6666 小时前
Flutter 三端应用实战:OpenHarmony “极简文本行数统计器”
开发语言·前端·flutter·ui·交互
布兰妮甜6 小时前
Photoshop中通过图层混合模式实现图像元素透明度渐变过渡的完整指南
人工智能·ui·生活·photoshop·文化