WPF性能優化之延遲加載(解決頁面卡頓問題)

文章目錄

  • 前言
  • 一. 基礎知識回顧
  • 二. 問題分析
  • 三. 解決方案
    • 1. 新建一個名為DeferredContentHost的控件。
    • 2. 在DeferredContentHost控件中定義一個名為Content的object類型的依賴屬性,用于承載要加載的子控件。
    • 3. 在DeferredContentHost控件中定義一個名為Skeleton的object類型的依賴屬性,用于在子控件加載前顯示骨架屏效果(使用加載效果也可以)。
    • 4. 在DeferredContentHost控件Loaded時顯示骨架屏。
    • 5. 在DeferredContentHost控件顯示骨架屏后執行Dispatcher.BeginInvoke(),將子控件顯示的代碼添加到Dispatcher消息隊列。
  • 四. 運行效果
    • 4.1 未優化的效果
    • 4.2 優化后的效果


前言

長久以來,WPF的性能一直為人所詬病,其中很大一個原因就是因為WPF在開發過程中,稍有不慎就會阻塞UI線程,導致操作卡頓,甚至頁面停止響應。它的原因當然是多方面的,我們今天只討論比較常見的情況,并給出解決方案,讓您開發的軟件盡量減少卡頓。


一. 基礎知識回顧

我們都知道WPF是單線程模型,所有UI元素必須由創建它們的線程直接操作,并且該線程還負責處理用戶輸入(鼠標、鍵盤)、渲染界面、執行事件處理程序、管理布局和動畫等工作。所以如果UI線程一旦被阻塞,就會導致災難性后果,反應到界面上就是卡頓,鼠標無法操作,也不響應鍵盤輸入。

二. 問題分析

當加載一個Window時會執行一系列操作,其中最重要的操作就是布局的測量(Measure )與排列(Arrange),布局系統會從Window的根元素開始沿可視化樹逐個調用子級控件的Measure與Arrange方法,以確認頁面上的每個控件被渲染到正確的位置。在理想的情況下這種模式可以工作得很好,但是在實際項目中我們往往會在頁面上嵌套數量龐大的子控件來實現功能,當要渲染的控件數量達到UI線程處理的瓶頸上限或是在布局計算中耗時過多,這時就可能會導致UI線程被阻塞。

三. 解決方案

既然UI線程不能無限制處理所有請求,那我們給它排個隊,一個一個處理不就可以解決這個問題了。在WPF中所有控件都繼承自DispatcherObject類,DispatcherObject類中有一個名為Dispatcher的屬性,Dispatcher就是管理UI線程消息隊列的核心。我們只需要將控件的加載任務送入Dispatcher,讓它在合適的時機執行就可以了。以下是實現的過程:

1. 新建一個名為DeferredContentHost的控件。

2. 在DeferredContentHost控件中定義一個名為Content的object類型的依賴屬性,用于承載要加載的子控件。

3. 在DeferredContentHost控件中定義一個名為Skeleton的object類型的依賴屬性,用于在子控件加載前顯示骨架屏效果(使用加載效果也可以)。

4. 在DeferredContentHost控件Loaded時顯示骨架屏。

5. 在DeferredContentHost控件顯示骨架屏后執行Dispatcher.BeginInvoke(),將子控件顯示的代碼添加到Dispatcher消息隊列。

以上代碼的核心在于Dispatcher.BeginInvoke(DispatcherPriority priority, Delegate method)方法中的priority參數,該參數用于指定method委托在消息隊列中的執行優先級,以下為DispatcherPriority枚舉的所有值:
在這里插入圖片描述
從上圖可以看出,為了不影響數據綁定、界面渲染、用戶輸入等操作,我們應該選擇盡量低的優先級來執行子控件顯示的代碼。這里我們使用ContextIdle。以下是完整的代碼:

[ContentProperty("Content")]
public class DeferredContentHost : FrameworkElement
{#region Fieldsprivate ContentControl _container = new ContentControl();#endregion#region Methodspublic DeferredContentHost(){this.AddVisualChild(_container);this.Loaded += DeferredContentHost_Loaded;}private void DeferredContentHost_Loaded(object sender, RoutedEventArgs e){if (IsInDesignMode){this._container.Content = this.Content;}else{this._container.Content = this.Skeleton;this.Dispatcher.BeginInvoke((Action)(() => this._container.Content = this.Content), System.Windows.Threading.DispatcherPriority.ContextIdle);}}protected override Visual GetVisualChild(int index){return _container;}protected override Size MeasureOverride(Size availableSize){_container.Measure(availableSize);if (availableSize.Width == double.PositiveInfinity || availableSize.Height == double.PositiveInfinity){return _container.DesiredSize;}return availableSize;}protected override Size ArrangeOverride(Size finalSize){_container.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));return base.ArrangeOverride(finalSize);}#endregion#region Propertiesprotected override int VisualChildrenCount => 1;protected bool IsInDesignMode { get => DesignerProperties.GetIsInDesignMode(this); }public object Content{get { return (object)GetValue(ContentProperty); }set { SetValue(ContentProperty, value); }}// Using a DependencyProperty as the backing store for UIElement.  This enables animation, styling, binding, etc...public static readonly DependencyProperty ContentProperty =DependencyProperty.Register("Content", typeof(object), typeof(DeferredContentHost));public object Skeleton{get { return (object)GetValue(SkeletonProperty); }set { SetValue(SkeletonProperty, value); }}// Using a DependencyProperty as the backing store for Skeleton.  This enables animation, styling, binding, etc...public static readonly DependencyProperty SkeletonProperty =DependencyProperty.Register("Skeleton", typeof(object), typeof(DeferredContentHost));#endregion
}

四. 運行效果

我們用大圖片來模擬阻塞UI線程的情況,下面是兩種效果對比。

4.1 未優化的效果

在這里插入圖片描述

<DataTemplate x:Key="item1"><Grid><Grid.RowDefinitions><RowDefinition Height="*" /><RowDefinition Height="30" /></Grid.RowDefinitions><Image Margin="5" Source="{Binding FullName}" /><TextBlockGrid.Row="1"Margin="5,0,5,5"HorizontalAlignment="Center"VerticalAlignment="Center"Text="{Binding Name}"TextTrimming="WordEllipsis" /></Grid>
</DataTemplate>

一次加載所有大圖片,界面停止響應,文本框無法輸入文字。

4.2 優化后的效果

在這里插入圖片描述

<DataTemplate x:Key="item2"><controls:DeferredContentHost><controls:DeferredContentHost.Skeleton><controls:Skeleton><controls:SkeletonGroup Orientation="Vertical"><controls:SkeletonItemHeight="*"Margin="5"RadiusX="5"RadiusY="5" /><controls:SkeletonItemWidth="120"Height="30"Margin="5,0,5,5"HorizontalAlignment="Center"RadiusX="5"RadiusY="5" /></controls:SkeletonGroup></controls:Skeleton></controls:DeferredContentHost.Skeleton><Grid><Grid.RowDefinitions><RowDefinition Height="*" /><RowDefinition Height="30" /></Grid.RowDefinitions><Image Margin="5" Source="{Binding FullName}" /><TextBlockGrid.Row="1"Margin="5,0,5,5"HorizontalAlignment="Center"VerticalAlignment="Center"Text="{Binding Name}"TextTrimming="WordEllipsis" /></Grid></controls:DeferredContentHost>
</DataTemplate>

使用DeferredContentHost控件的延遲加載效果,加載過程文本框可以輸入文字,界面可以正常響應鼠標操作。


技術交流
QQ群:661224882
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/906839.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/906839.shtml
英文地址,請注明出處:http://en.pswp.cn/news/906839.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VLM-MPC:自動駕駛中模型預測控制器增強視覺-語言模型

《VLM-MPC: Model Predictive Controller Augmented Vision Language Model for Autonomous Driving》2024年8月發表&#xff0c;來自威斯康星大學的論文。 受視覺語言模型&#xff08;VLM&#xff09;的緊急推理能力及其提高自動駕駛系統可理解性的潛力的啟發&#xff0c;本文…

推薦系統里真的存在“反饋循環”嗎?

推薦系統里真的存在“反饋循環”嗎&#xff1f; 許多人說&#xff0c;推薦算法不過是把用戶早已存在的興趣挖掘出來&#xff0c;你本來就愛聽流行歌、買潮牌玩具&#xff0c;系統只是在合適的時間把它們端到你面前&#xff0c;再怎么迭代&#xff0c;算法也改變不了人的天性&a…

代碼混淆技術的還原案例

案例一 eval 混淆 特征 &#xff1a; 反常的 eval 連接了一堆數據 練習網站 https://scrape.center/ spa9 這個案例 基本的還原方法 但是這個代碼還是非常的模糊不好看 優化一下 &#xff1a; 當然還有更快捷的方法 &#xff1a; 好用的 js混淆還原的 web &#xf…

鴻蒙Flutter實戰:22-混合開發詳解-2-Har包模式引入

以 Har 包的方式加載到 HarmonyOS 工程 創建工作 創建一個根目錄 mkdir ohos_flutter_module_demo這個目錄用于存放 flutter 項目和鴻蒙項目。 創建 Flutter 模塊 首先創建一個 Flutter 模塊&#xff0c;我們選擇與 ohos_app 項目同級目錄 flutter create --templatemodu…

Go核心特性與并發編程

Go核心特性與并發編程 1. 結構體與方法&#xff08;擴展&#xff09; 高級結構體特性 // 嵌套結構體與匿名字段 type Employee struct {Person // 匿名嵌入Department stringsalary float64 // 私有字段 }// 構造函數模式 func NewPerson(name string, age int) *Pe…

Java 函數式接口(Functional Interface)

一、理論說明 1. 函數式接口的定義 Java 函數式接口是一種特殊的接口&#xff0c;它只包含一個抽象方法&#xff08;Single Abstract Method, SAM&#xff09;&#xff0c;但可以包含多個默認方法或靜態方法。函數式接口是 Java 8 引入 Lambda 表達式的基礎&#xff0c;通過函…

【python代碼】一些小實驗

目錄 1. 測試Resnet50 ONNX模型的推理速度 1. 測試Resnet50 ONNX模型的推理速度 ############################### # 導出resnet50 模型 # 測試onnx模型推理 cpu 和 GPU 的對比 ###############################import time import numpy as np import onnxruntime as ort im…

5.Java 面向對象編程入門:類與對象的創建和使用?

在現實生活中&#xff0c;我們常常會接觸到各種各樣的對象&#xff0c;比如一輛汽車、一個學生、一部手機等。這些對象都具有各自的屬性和行為。例如&#xff0c;汽車有顏色、品牌、型號等屬性&#xff0c;還有啟動、加速、剎車等行為&#xff1b;學生有姓名、年齡、學號等屬性…

從開發者角度看數據庫架構進化史:JDBC - 中間件 - TiDB

作者&#xff1a; Lucien-盧西恩 原文來源&#xff1a; https://tidb.net/blog/e7034d1b Java 應用開發技術發展歷程 在業務開發早期&#xff0c;用 Java 借助 JDBC 進行數據庫操作&#xff0c;雖能實現基本交互&#xff0c;但需手動管理連接、編寫大量 SQL 及處理結果集&a…

工業智能網關建立烤漆設備故障預警及遠程診斷系統

一、項目背景 烤漆房是汽車、機械、家具等工業領域廣泛應用的設備&#xff0c;主要用于產品的表面涂裝。傳統的烤漆房控制柜采用本地控制方式&#xff0c;操作人員需在現場進行參數設置和設備控制&#xff0c;且存在設備智能化程度低、數據孤島、設備維護成本高以及依靠傳統人…

故障率預測:基于LSTM的GPU集群硬件健康監測系統(附Prometheus監控模板)

一、GPU集群健康監測的挑戰與價值 在大規模深度學習訓練場景下&#xff0c;GPU集群的硬件故障率顯著高于傳統計算設備。根據2023年MLCommons統計&#xff0c;配備8卡A100的服務器平均故障間隔時間&#xff08;MTBF&#xff09;僅為1426小時&#xff0c;其中顯存故障占比達38%&…

Vue 樣式不一致問題全面分析與解決方案

文章目錄 1. 問題概述1.1 問題表現1.2 問題影響 2. 根本原因分析2.1 Vue 的渲染機制與樣式加載時機2.2 Scoped CSS 的工作原理2.3 CSS 模塊化與作用域隔離2.4 樣式加載順序問題2.5 熱重載(HMR)與樣式更新 3. 解決方案3.1 確保樣式加載順序3.1.1 預加載關鍵 CSS3.1.2 控制全局樣…

[免費]微信小程序寵物醫院管理系統(uni-app+SpringBoot后端+Vue管理端)【論文+源碼+SQL腳本】

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;看到一個不錯的微信小程序寵物醫院管理系統(uni-appSpringBoot后端Vue管理端)&#xff0c;分享下哈。 項目視頻演示 【免費】微信小程序寵物醫院管理系統(uni-appSpringBoot后端Vue管理端) Java畢業設計_嗶哩嗶哩_bilibi…

測試總結(一)

一、測試流程 參與需求評審-制定測試計劃-編寫測試用例-用例評審-冒煙測試-測試執行-缺陷管理-預發驗收測試-發布線上-線上回歸-線上觀察-項目總結 二、測試用例設計方法 等價類劃分&#xff08;處理有效/無效輸入&#xff09; 邊界值分析&#xff08;臨界值測試&#xff09…

SAP-ABAP:ABAP異常處理與SAP現代技術融合—— 面向云原生、微服務與低代碼場景的創新實踐

專題三&#xff1a;ABAP異常處理與SAP現代技術融合 —— 面向云原生、微服務與低代碼場景的創新實踐 一、SAP技術演進與異常處理的挑戰 隨著SAP技術棧向云端、微服務化和低代碼方向演進&#xff0c;異常處理面臨新場景&#xff1a; Fiori UX敏感度&#xff1a;用戶期望前端友…

DC-DC電路的自舉電容電路原理

在以往的電子產品設計中&#xff0c;我們經常會選型 DCDC 芯片&#xff0c;在選型過程中經常遇到有些DC-DC電路中需要用到自舉電容&#xff0c;本文主要分析自舉電容在DC-DC電路中的原理。 無論同步或者異步整流&#xff0c;經常會看到一個自舉電容&#xff0c;常并聯在DC-DC的…

android studio 開啟無線調試

1、在工具的模擬器點擊下后&#xff0c;會出現下面菜單&#xff1a; 選擇Pair Devices Using Wi-Fi 發現一直在轉圈&#xff0c;并不會連接上&#xff0c;之前在android12的時候&#xff0c;發現一連就上了&#xff0c;現在換成了android14&#xff0c;連不上了。 2、選擇用命令…

Go基礎語法與控制結構

Go基礎語法與控制結構 1. 環境配置與Hello World&#xff08;擴展&#xff09; 安裝指南 # 驗證安裝成功 $ go version # 設置模塊代理&#xff08;中國用戶推薦&#xff09; $ go env -w GOPROXYhttps://goproxy.cn,direct程序解剖 package main // 程序入口包聲明import…

QGIS新手教程:兩種方法創建點圖層(手動添加 + 表格導入),支持經緯度定位與查找

&#x1f30d;QGIS新手教程&#xff1a;兩種方法創建點圖層&#xff08;手動添加 表格導入&#xff09;&#xff0c;支持經緯度定位與查找 本文將手把手教你在 QGIS 中通過兩種方法創建點圖層&#xff0c;并結合經緯度定位、拾取坐標、查找屬性等功能&#xff0c;快速掌握從地…

Doris ClickHouse Greenplum 對比

1. 核心架構對比 Doris &#xff08;https://doris.apache.org/&#xff09; MPP架構 列式存儲支持實時更新&#xff08;Unique Key模型&#xff09;向量化執行引擎兼容MySQL協議 ClickHouse &#xff08;https://clickhouse.com/&#xff09; 分布式列式存儲MergeTree存儲引…