WPF架构分析
<?xml version="1.0" encoding="UTF-8"?> 1.DisptcherObject提供了線程和并發(fā)模型,實(shí)現(xiàn)了消息系統(tǒng)。 2.DependencyObject提供了更改通知,實(shí)現(xiàn)了綁定,樣式。3.Visual是托管API和非托管API(milcore)的之間的關(guān)鍵點(diǎn)。4.UIElement定義了Layout,Input和Events等核心子系統(tǒng)。Measure讓一個(gè)組件來(lái)決定自己想要的size,而Arrange讓父組件放置子組件并決定子組件的最終size。5.WPF的外觀和行為總共有3個(gè)模型:數(shù)據(jù)模型(Properties),交互模型(Commands,Events),顯示模型(Template)。6.不同應(yīng)用程序域間的WPF可以通過(guò)INativeHandleContract來(lái)實(shí)現(xiàn)WPF AddIn。7.WPF定義了三個(gè)呈現(xiàn)曾:
提高方法:盡量使用最有效的Panel,功能越強(qiáng)大的Panel性能成本也就越高;?更新而不替換RenderTransform;?從上到下生成邏輯樹。2)二維圖形和圖像處理提高方法:Drawing對(duì)象比Shape對(duì)象結(jié)構(gòu)簡(jiǎn)單,性能更為優(yōu)越,但是不是從FrameworkElement集成,使用時(shí)注意;?使用StreamGeometry而不是PathGeometry;如果需要顯示縮略圖,盡量使用小版本的圖像,或者請(qǐng)求WPF將圖像解碼為縮略圖大小,或者請(qǐng)求WPF加載縮略圖大小。始終將圖像解碼為所需的大小而不是默認(rèn)大小。3)行為提高方法:注冊(cè)DependencyObject時(shí),盡量提供默認(rèn)值和PropertyMetadata,而不是放在以后賦值。凍結(jié)Freezable會(huì)改善程序性能,不再需要因維護(hù)更改通知而消耗資源。使用VirtualizingStackPanel而不是StackPanel,注意:這樣子資源蘇不可見時(shí)即被移除,這時(shí)無(wú)法訪問(wèn)不可見的子元素。盡可能使用靜態(tài)資源,只在必須得情況下使用動(dòng)態(tài)資源。避免在FlowDocument使用TextBlock,而應(yīng)該使用Run。避免在TextBlock里使用Run來(lái)設(shè)置文本屬性。避免執(zhí)行到Lable.Content屬性的數(shù)據(jù)綁定。 如果數(shù)據(jù)源頻繁更新,應(yīng)用TextBlock.Text替換。?4)數(shù)據(jù)綁定提高方法:性能從高到低:DependencyObject 〉INotifyPropertyChanged ?如果綁定到較大的CLR對(duì)象,考慮將對(duì)象拆分成多個(gè)具有少量屬性的CLR對(duì)象將IList(而非IEnumerable)綁定到ItemsCount5)控件提高方法:設(shè)置?IsVirtualizing?為true,?VirtualizationMode?為Recycling,??IsDeferredScrollingEnabled為true。IsVirtualizing代表是否UI虛擬化,當(dāng)設(shè)置為true時(shí),表示只有當(dāng)數(shù)據(jù)項(xiàng)在屏幕上可見時(shí)才會(huì)在內(nèi)存中創(chuàng)建存儲(chǔ)。?VirtualizationMode代表是否容器回收,正常情況下ItemsControl會(huì)為滾動(dòng)到視圖中的每個(gè)item創(chuàng)建項(xiàng)容器,并銷毀滾動(dòng)到視圖之外的每個(gè)item的項(xiàng)容器。通過(guò)Recycling,可以讓控件能夠?qū)F(xiàn)有項(xiàng)容器重復(fù)利用于不同的數(shù)據(jù)項(xiàng)。IsDeferredScrollingEnabled代表是否延長(zhǎng)滾動(dòng),正常情況下當(dāng)用戶拖到滾動(dòng)條上的滑塊時(shí),內(nèi)容視圖會(huì)不會(huì)不斷更新。當(dāng)設(shè)置為true, 表示只有當(dāng)用戶松開滾動(dòng)條時(shí),內(nèi)容才會(huì)更新。
6)其他盡量使用畫筆的不透明度(Opacity),而不是使用元素的Opacity,修改元素的Opacity會(huì)導(dǎo)致WPF創(chuàng)建臨時(shí)圖標(biāo)。配置“WPF字體緩存服務(wù)”從手動(dòng)為自動(dòng)(延遲啟動(dòng)),這個(gè)服務(wù)如果沒有啟動(dòng),會(huì)隨著第一個(gè)WPF程序啟動(dòng)時(shí)啟動(dòng)。這樣導(dǎo)致第一個(gè)WPF的初始化事件很長(zhǎng)。
9. WPF線程模型典型的wpf程序有2個(gè)線程,一個(gè)用于負(fù)責(zé)渲染,一個(gè)用于管理UI。UI線程把工作項(xiàng)排序到Dispatcher對(duì)象中,Dispatcher對(duì)象根據(jù)工作項(xiàng)的優(yōu)先級(jí)選擇執(zhí)行,直到全部執(zhí)行。每個(gè)UI線程必須至少有一個(gè)Dispatcher,每個(gè)Dispatcher必須在一個(gè)線程里工作。?
可以通過(guò)CheckAccess來(lái)判斷線程是否可以訪問(wèn)DispatcherObject。原理是:大多數(shù)類繼承于DispatcherObject,DispatcherObject在構(gòu)造時(shí)把當(dāng)前運(yùn)行線程的Dispatcher引用存儲(chǔ)。當(dāng)訪問(wèn)DispatcherObject時(shí),檢查當(dāng)前線程關(guān)聯(lián)的Dispatcher于構(gòu)造中存儲(chǔ)的Dispatcher進(jìn)行比較,如果相同返回True,不同返回False。
要構(gòu)建響應(yīng)速度快、且用戶友好的應(yīng)用程序,訣竅是減小工作項(xiàng),以最大限度地提高 Dispatcher 吞吐量。 這樣,工作項(xiàng)將永遠(yuǎn)不會(huì)因?yàn)樵?Dispatcher 隊(duì)列中等待處理而失效。 輸入與響應(yīng)之間的任何可察覺的延遲都會(huì)使用戶不快。
嵌套的消息泵:比如MessageBox,我們調(diào)用show后需要用戶單擊“OK"才能返回。MessageBox創(chuàng)建的窗口必須要由一個(gè)消息泵才能進(jìn)行交互。我們?cè)诘却脩魡螕簟監(jiān)K“時(shí)原始應(yīng)用程序窗口不響應(yīng)用戶輸入。具體實(shí)現(xiàn): WPF使用一種嵌套的消息處理系統(tǒng), Dispatcher類包含一個(gè)PushFrame的特殊方法, 該方法存儲(chǔ)應(yīng)用程序的當(dāng)前執(zhí)行點(diǎn),然后開始一個(gè)新的消息泵,當(dāng)嵌套的消息泵執(zhí)行結(jié)束時(shí),執(zhí)行將在最初的PushFrame調(diào)用之后繼續(xù)。PushFrame內(nèi)部實(shí)現(xiàn)類似Win32中GetMessage、TranslateMessage、DispatchMessage的消息泵。 (通過(guò)這個(gè)原理,我們可以實(shí)現(xiàn)自己的MessageBox系統(tǒng))Application.Run內(nèi)部調(diào)用Dispatcher.Run(),Dispatcher.Run()內(nèi)部調(diào)用了Dispatcher.PushFrame(..), 實(shí)現(xiàn)了一個(gè)Win32的消息泵。
DispatcherOperation對(duì)象用于與Dispatcher隊(duì)列上的Delegate進(jìn)行交互,例如更改委托的優(yōu)先級(jí)、從事件隊(duì)列中移除委托、等待委托返回、獲取委托執(zhí)行之后返回的值。
Application.DoEvents方法:處理當(dāng)前Dispatcher消息隊(duì)列里的所有windows消息。關(guān)于BeginInvoke和Invoke,向關(guān)聯(lián)的Dispatcher的隊(duì)列中插入同步或者異步工作項(xiàng)。
10.Weak Event Pattern傳統(tǒng)的偵聽事件可能導(dǎo)致內(nèi)存泄漏,source.SomeEvent += new SomeEventHandler(MyEventHandler) 。這是因?yàn)闉槭录刺砑邮录幚沓绦驎r(shí),會(huì)創(chuàng)建一個(gè)事件源到事件偵聽器(所謂事件偵聽器,就是Delegate對(duì)象)的強(qiáng)引用。這樣事件偵聽器就會(huì)有生命周期,該生命周期和事件源的生命周期有關(guān), 除非顯式的移除了事件處理程序,?source.SomeEvent -= new SomeEventHandler(MyEventHandler) 。當(dāng)事件源從可視樹中移除時(shí),事件偵聽器還是有生命周期,但此時(shí)事件處理程序不會(huì)被調(diào)用,也就是說(shuō)造成了無(wú)用的事件偵聽器的還一直存在。這種情況下就需要弱事件模式。 ? ? ?實(shí)現(xiàn)弱事件模式,有三種方式:使用系統(tǒng)已有的(CollectionChangedEventManager,PropertyChangedEventManager等等); 使用泛型弱事件管理器(WeakEventManager<TEventSource, TeventArgs>, 注意有性能損失); 繼承WeakEventManager類,實(shí)現(xiàn)自定義弱事件管理器。
例如:?
class SomeEventWeakEventManager : WeakEventManager{
private SomeEventWeakEventManager(){
}
/// <summary>/// Add a handler for the given source's event./// </summary>public static void AddHandler(EventSource source,?EventHandler<SomeEventEventArgs> handler){if (source == null)throw new ArgumentNullException("source");if (handler == null)throw new ArgumentNullException("handler");
CurrentManager.ProtectedAddHandler(source, handler);}
/// <summary>/// Remove a handler for the given source's event./// </summary>public static void RemoveHandler(EventSource source,?EventHandler<SomeEventEventArgs> handler){if (source == null)throw new ArgumentNullException("source");if (handler == null)throw new ArgumentNullException("handler");
CurrentManager.ProtectedRemoveHandler(source, handler);}
/// <summary>/// Get the event manager for the current thread./// </summary>private static SomeEventWeakEventManager CurrentManager{get{Type managerType = typeof(SomeEventWeakEventManager);SomeEventWeakEventManager manager =?(SomeEventWeakEventManager)GetCurrentManager(managerType);
// at first use, create and register a new managerif (manager == null){manager = new SomeEventWeakEventManager();SetCurrentManager(managerType, manager);}
return manager;}}
/// <summary>/// Return a new list to hold listeners to the event./// </summary>protected override ListenerList NewListenerList(){return new ListenerList<SomeEventEventArgs>();}
/// <summary>/// Listen to the given source for the event./// </summary>protected override void StartListening(object source){EventSource typedSource = (EventSource)source;typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);}
/// <summary>/// Stop listening to the given source for the event./// </summary>protected override void StopListening(object source){EventSource typedSource = (EventSource)source;typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);}
/// <summary>/// Event handler for the SomeEvent event./// </summary>void OnSomeEvent(object sender, SomeEventEventArgs e){DeliverEvent(sender, e);}}
使用方法:?將source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);source.SomeEvent -= new SomeEventEventHandler(OnSome);
替換為??
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
參考:http://msdn.microsoft.com/zh-cn/library/ms750441.aspx? ?WPF架構(gòu)http://msdn.microsoft.com/zh-cn/library/aa970683.aspx? ?優(yōu)化WPF應(yīng)用程序性能http://msdn.microsoft.com/en-us/library/ms741870.aspx?? WPF線程模型http://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcheroperation.aspx?DispatcherOperation類http://msdn.microsoft.com/zh-cn/library/system.windows.forms.application.doevents.aspx?Application.DoEvents 方法http://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcher.pushframe.aspx?Dispatcher.PushFrame 方法
http://msdn.microsoft.com/zh-cn/library/vstudio/aa970850(v=vs.90).aspx?WeakEvent模式http://msdn.microsoft.com/zh-cn/library/ms404247.aspx弱引用
呈現(xiàn)層 0?無(wú)圖形硬件加速。?所有圖形功能都使用軟件加速。?DirectX 版本級(jí)別低于 9.0。
呈現(xiàn)層 1?某些圖形功能使用圖形硬件加速。?DirectX 版本級(jí)別高于或等于 9.0。
呈現(xiàn)層 2?大多數(shù)圖形功能都使用圖形硬件加速。?DirectX 版本級(jí)別高于或等于 9.0。
提高方法:盡量使用最有效的Panel,功能越強(qiáng)大的Panel性能成本也就越高;?更新而不替換RenderTransform;?從上到下生成邏輯樹。2)二維圖形和圖像處理提高方法:Drawing對(duì)象比Shape對(duì)象結(jié)構(gòu)簡(jiǎn)單,性能更為優(yōu)越,但是不是從FrameworkElement集成,使用時(shí)注意;?使用StreamGeometry而不是PathGeometry;如果需要顯示縮略圖,盡量使用小版本的圖像,或者請(qǐng)求WPF將圖像解碼為縮略圖大小,或者請(qǐng)求WPF加載縮略圖大小。始終將圖像解碼為所需的大小而不是默認(rèn)大小。3)行為提高方法:注冊(cè)DependencyObject時(shí),盡量提供默認(rèn)值和PropertyMetadata,而不是放在以后賦值。凍結(jié)Freezable會(huì)改善程序性能,不再需要因維護(hù)更改通知而消耗資源。使用VirtualizingStackPanel而不是StackPanel,注意:這樣子資源蘇不可見時(shí)即被移除,這時(shí)無(wú)法訪問(wèn)不可見的子元素。盡可能使用靜態(tài)資源,只在必須得情況下使用動(dòng)態(tài)資源。避免在FlowDocument使用TextBlock,而應(yīng)該使用Run。避免在TextBlock里使用Run來(lái)設(shè)置文本屬性。避免執(zhí)行到Lable.Content屬性的數(shù)據(jù)綁定。 如果數(shù)據(jù)源頻繁更新,應(yīng)用TextBlock.Text替換。?4)數(shù)據(jù)綁定提高方法:性能從高到低:DependencyObject 〉INotifyPropertyChanged ?如果綁定到較大的CLR對(duì)象,考慮將對(duì)象拆分成多個(gè)具有少量屬性的CLR對(duì)象將IList(而非IEnumerable)綁定到ItemsCount5)控件提高方法:設(shè)置?IsVirtualizing?為true,?VirtualizationMode?為Recycling,??IsDeferredScrollingEnabled為true。IsVirtualizing代表是否UI虛擬化,當(dāng)設(shè)置為true時(shí),表示只有當(dāng)數(shù)據(jù)項(xiàng)在屏幕上可見時(shí)才會(huì)在內(nèi)存中創(chuàng)建存儲(chǔ)。?VirtualizationMode代表是否容器回收,正常情況下ItemsControl會(huì)為滾動(dòng)到視圖中的每個(gè)item創(chuàng)建項(xiàng)容器,并銷毀滾動(dòng)到視圖之外的每個(gè)item的項(xiàng)容器。通過(guò)Recycling,可以讓控件能夠?qū)F(xiàn)有項(xiàng)容器重復(fù)利用于不同的數(shù)據(jù)項(xiàng)。IsDeferredScrollingEnabled代表是否延長(zhǎng)滾動(dòng),正常情況下當(dāng)用戶拖到滾動(dòng)條上的滑塊時(shí),內(nèi)容視圖會(huì)不會(huì)不斷更新。當(dāng)設(shè)置為true, 表示只有當(dāng)用戶松開滾動(dòng)條時(shí),內(nèi)容才會(huì)更新。
6)其他盡量使用畫筆的不透明度(Opacity),而不是使用元素的Opacity,修改元素的Opacity會(huì)導(dǎo)致WPF創(chuàng)建臨時(shí)圖標(biāo)。配置“WPF字體緩存服務(wù)”從手動(dòng)為自動(dòng)(延遲啟動(dòng)),這個(gè)服務(wù)如果沒有啟動(dòng),會(huì)隨著第一個(gè)WPF程序啟動(dòng)時(shí)啟動(dòng)。這樣導(dǎo)致第一個(gè)WPF的初始化事件很長(zhǎng)。
9. WPF線程模型典型的wpf程序有2個(gè)線程,一個(gè)用于負(fù)責(zé)渲染,一個(gè)用于管理UI。UI線程把工作項(xiàng)排序到Dispatcher對(duì)象中,Dispatcher對(duì)象根據(jù)工作項(xiàng)的優(yōu)先級(jí)選擇執(zhí)行,直到全部執(zhí)行。每個(gè)UI線程必須至少有一個(gè)Dispatcher,每個(gè)Dispatcher必須在一個(gè)線程里工作。?
可以通過(guò)CheckAccess來(lái)判斷線程是否可以訪問(wèn)DispatcherObject。原理是:大多數(shù)類繼承于DispatcherObject,DispatcherObject在構(gòu)造時(shí)把當(dāng)前運(yùn)行線程的Dispatcher引用存儲(chǔ)。當(dāng)訪問(wèn)DispatcherObject時(shí),檢查當(dāng)前線程關(guān)聯(lián)的Dispatcher于構(gòu)造中存儲(chǔ)的Dispatcher進(jìn)行比較,如果相同返回True,不同返回False。
要構(gòu)建響應(yīng)速度快、且用戶友好的應(yīng)用程序,訣竅是減小工作項(xiàng),以最大限度地提高 Dispatcher 吞吐量。 這樣,工作項(xiàng)將永遠(yuǎn)不會(huì)因?yàn)樵?Dispatcher 隊(duì)列中等待處理而失效。 輸入與響應(yīng)之間的任何可察覺的延遲都會(huì)使用戶不快。
嵌套的消息泵:比如MessageBox,我們調(diào)用show后需要用戶單擊“OK"才能返回。MessageBox創(chuàng)建的窗口必須要由一個(gè)消息泵才能進(jìn)行交互。我們?cè)诘却脩魡螕簟監(jiān)K“時(shí)原始應(yīng)用程序窗口不響應(yīng)用戶輸入。具體實(shí)現(xiàn): WPF使用一種嵌套的消息處理系統(tǒng), Dispatcher類包含一個(gè)PushFrame的特殊方法, 該方法存儲(chǔ)應(yīng)用程序的當(dāng)前執(zhí)行點(diǎn),然后開始一個(gè)新的消息泵,當(dāng)嵌套的消息泵執(zhí)行結(jié)束時(shí),執(zhí)行將在最初的PushFrame調(diào)用之后繼續(xù)。PushFrame內(nèi)部實(shí)現(xiàn)類似Win32中GetMessage、TranslateMessage、DispatchMessage的消息泵。 (通過(guò)這個(gè)原理,我們可以實(shí)現(xiàn)自己的MessageBox系統(tǒng))Application.Run內(nèi)部調(diào)用Dispatcher.Run(),Dispatcher.Run()內(nèi)部調(diào)用了Dispatcher.PushFrame(..), 實(shí)現(xiàn)了一個(gè)Win32的消息泵。
DispatcherOperation對(duì)象用于與Dispatcher隊(duì)列上的Delegate進(jìn)行交互,例如更改委托的優(yōu)先級(jí)、從事件隊(duì)列中移除委托、等待委托返回、獲取委托執(zhí)行之后返回的值。
Application.DoEvents方法:處理當(dāng)前Dispatcher消息隊(duì)列里的所有windows消息。關(guān)于BeginInvoke和Invoke,向關(guān)聯(lián)的Dispatcher的隊(duì)列中插入同步或者異步工作項(xiàng)。
10.Weak Event Pattern傳統(tǒng)的偵聽事件可能導(dǎo)致內(nèi)存泄漏,source.SomeEvent += new SomeEventHandler(MyEventHandler) 。這是因?yàn)闉槭录刺砑邮录幚沓绦驎r(shí),會(huì)創(chuàng)建一個(gè)事件源到事件偵聽器(所謂事件偵聽器,就是Delegate對(duì)象)的強(qiáng)引用。這樣事件偵聽器就會(huì)有生命周期,該生命周期和事件源的生命周期有關(guān), 除非顯式的移除了事件處理程序,?source.SomeEvent -= new SomeEventHandler(MyEventHandler) 。當(dāng)事件源從可視樹中移除時(shí),事件偵聽器還是有生命周期,但此時(shí)事件處理程序不會(huì)被調(diào)用,也就是說(shuō)造成了無(wú)用的事件偵聽器的還一直存在。這種情況下就需要弱事件模式。 ? ? ?實(shí)現(xiàn)弱事件模式,有三種方式:使用系統(tǒng)已有的(CollectionChangedEventManager,PropertyChangedEventManager等等); 使用泛型弱事件管理器(WeakEventManager<TEventSource, TeventArgs>, 注意有性能損失); 繼承WeakEventManager類,實(shí)現(xiàn)自定義弱事件管理器。
例如:?
class SomeEventWeakEventManager : WeakEventManager{
private SomeEventWeakEventManager(){
}
/// <summary>/// Add a handler for the given source's event./// </summary>public static void AddHandler(EventSource source,?EventHandler<SomeEventEventArgs> handler){if (source == null)throw new ArgumentNullException("source");if (handler == null)throw new ArgumentNullException("handler");
CurrentManager.ProtectedAddHandler(source, handler);}
/// <summary>/// Remove a handler for the given source's event./// </summary>public static void RemoveHandler(EventSource source,?EventHandler<SomeEventEventArgs> handler){if (source == null)throw new ArgumentNullException("source");if (handler == null)throw new ArgumentNullException("handler");
CurrentManager.ProtectedRemoveHandler(source, handler);}
/// <summary>/// Get the event manager for the current thread./// </summary>private static SomeEventWeakEventManager CurrentManager{get{Type managerType = typeof(SomeEventWeakEventManager);SomeEventWeakEventManager manager =?(SomeEventWeakEventManager)GetCurrentManager(managerType);
// at first use, create and register a new managerif (manager == null){manager = new SomeEventWeakEventManager();SetCurrentManager(managerType, manager);}
return manager;}}
/// <summary>/// Return a new list to hold listeners to the event./// </summary>protected override ListenerList NewListenerList(){return new ListenerList<SomeEventEventArgs>();}
/// <summary>/// Listen to the given source for the event./// </summary>protected override void StartListening(object source){EventSource typedSource = (EventSource)source;typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);}
/// <summary>/// Stop listening to the given source for the event./// </summary>protected override void StopListening(object source){EventSource typedSource = (EventSource)source;typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);}
/// <summary>/// Event handler for the SomeEvent event./// </summary>void OnSomeEvent(object sender, SomeEventEventArgs e){DeliverEvent(sender, e);}}
使用方法:?將source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);source.SomeEvent -= new SomeEventEventHandler(OnSome);
替換為??
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
參考:http://msdn.microsoft.com/zh-cn/library/ms750441.aspx? ?WPF架構(gòu)http://msdn.microsoft.com/zh-cn/library/aa970683.aspx? ?優(yōu)化WPF應(yīng)用程序性能http://msdn.microsoft.com/en-us/library/ms741870.aspx?? WPF線程模型http://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcheroperation.aspx?DispatcherOperation類http://msdn.microsoft.com/zh-cn/library/system.windows.forms.application.doevents.aspx?Application.DoEvents 方法http://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcher.pushframe.aspx?Dispatcher.PushFrame 方法
http://msdn.microsoft.com/zh-cn/library/vstudio/aa970850(v=vs.90).aspx?WeakEvent模式http://msdn.microsoft.com/zh-cn/library/ms404247.aspx弱引用
轉(zhuǎn)載于:https://blog.51cto.com/muzizongheng/1333025
總結(jié)
- 上一篇: mac中插入带圆圈数字序号①②③
- 下一篇: 【java设计模式之Command(菜单