国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
WPF 渲染原理

對(duì)于開發(fā)者來說,WPF 中最主要的知識(shí)點(diǎn)就是渲染,因?yàn)?WPF 是一個(gè)界面框架。想用一篇博客就能告訴大家完整的 WPF 渲染原理是不可能的。本文向大家介紹從開發(fā)者執(zhí)行繪圖指令到在屏幕顯示的過程。本文是從一個(gè)全局的角度來看渲染的過程,在本文之后會(huì)補(bǔ)充很多博客來告訴大家渲染的細(xì)節(jié)。

從 WPF 畫圖像到屏幕顯示是比較復(fù)雜的,本渣也不敢說這就是 WPF 的做法,但是看了很多博客,好像都是這么說的,看了代碼,好像 WPF 是這樣寫的。

本文將會(huì)分為三個(gè)不同的層來講,第一層就是 WPF 的總體結(jié)構(gòu),第二層是消息循環(huán)相關(guān),第三層是講 dx 渲染到屏幕。

WPF 組成


因?yàn)?WPF 是一個(gè) UI 框架,作為一個(gè)框架最主要的是交互和顯示。本文只告訴大家渲染的原理。但是本文不會(huì)告訴大家任何關(guān)于渲染的算法,只是告訴大家渲染的過程如何從 WPF 元素顯示到屏幕。

下面的圖片來源 《WPF Architecture[1]。

WPF 有三個(gè)主要的模塊 PresentationFramework、PresentationCore 和基礎(chǔ)層。

在 WPF 最頂層,也就是給開發(fā)者使用的元素,元素的顯示就是使用 DrawingContext 告訴 WPF 需要如何渲染。最簡單的方法也就是繼承 FrameworkElement 然后重寫 OnRender 方法,通過 OnRender 方法畫出最基礎(chǔ)的界面。這就是在框架的頂層,在這的上面就不屬于底層框架的,如在顯示上面封裝的 Image 等。

那么在調(diào)用 OnRender 畫出內(nèi)容之后,是不是 WPF 會(huì)立刻顯示?如果大家有看 《WPF 使用 Direct2D1 畫圖入門[2]就會(huì)發(fā)現(xiàn),在渲染時(shí)調(diào)用 CompositionTarget.Rendering 才知道是什么時(shí)候渲染,因?yàn)?WPF 是分開渲染和交互的,實(shí)際的 OnRender 畫出的內(nèi)容的代碼是指導(dǎo)渲染,也就是告訴 WPF 如何渲染。那么 WPF 在什么時(shí)候渲染就是代碼不知道的。為什么 WPF 需要這樣做還需要從 WPF 的體系結(jié)構(gòu)開始說起。

我在下面偷了一張圖,圖片源自《Overview of Windows Presentation Foundation (WPF) Architecture[3],在 WPF 可以分為三層。第一層就是 WPF 的托管層,這一層的代碼都是托管代碼。第二層就是 WPF 的非托管層,包括剛才告訴大家的模塊。最后一層就是系統(tǒng)核心元素層。下面簡單介紹一下 WPF 的體系結(jié)構(gòu)。

如果覺得對(duì) WPF 的體系結(jié)構(gòu)已經(jīng)足夠了解,那么請(qǐng)?zhí)较乱还?jié)。

托管層

在托管層最重要的就是 Presentation Framework、Presentation Core 和 Window Base,很多小伙伴說的 WPF 只是包含這幾個(gè)模塊,因?yàn)槠渌哪K幾乎都不會(huì)知道。也就是基本使用的類都在下面三個(gè) dll 可以找到。

  • PresentationFramework.dll 提供最頂層封裝,包括應(yīng)用的窗口、控制的 Panel 和 Styles 這些類都是在這可以找到,此外還有交互的控制,動(dòng)畫和幾乎可以用到的界面的控件;

  • PresentationCore.dll 提供底層的 WPF 功能,如2d、3d和 geometry 這些類,在 PresentationCore 是對(duì) MIL 和托管中間封裝,包括提供了 UI Element 和 Visual 這些類,在顯示模塊包含視覺樹和顯示指令,也就是剛才說的 OnRender 重寫方法;

  • WindowsBase.dll 提供最底層的基礎(chǔ)類,包括調(diào)度對(duì)象 Dispatcher objects 和依賴屬性。

非托管層

非托管層用來進(jìn)行高性能的 DX 渲染和連接非托管層。

  • milCore.dll 用來作為 WPF 的組合引擎,這是一個(gè)使用本地代碼編譯的庫,包含最主要的媒體集成層 Media Integration Layer (MIL) 的基礎(chǔ)支持,作用是封裝 Dx 的接口支持 2D 和 3D 的渲染,使用本地代碼編譯是為了獲得最好的性能,而且用在 WPF 上層和底層的 DirextX 和 User32 的接口之間。值的一說的是在 Windows Vista 的桌面窗口管理器(Desktop Windows Manager,DWM)就是使用 milCore.dll 渲染桌面的;

  • WindowsCodecs.dll 這是另一個(gè)底層的圖片支持代碼,用來支持 WPF 旋轉(zhuǎn)、放大圖片等,也是使用本地代碼編譯的,提供了很多圖片的加密解密,可以讓 WPF 把圖片畫在屏幕。

核心系統(tǒng)層

這一層就是系統(tǒng)的核心,如 User32、GDI、Device Drivers、顯卡等,這些組合在程序里是最底層的接口。

  • User32 提供內(nèi)存和進(jìn)程分割,這是一個(gè)通用的 API ,不止是給 WPF 使用,這個(gè)庫決定一個(gè)元素可以在屏幕的哪里顯示,也就是窗口顯示的最底層的代碼就在這。但是這個(gè)代碼只提供讓窗口在哪里顯示,如何顯示就需要下面的代碼;

  • DirectX 這就是 WPF 渲染的最底層的庫,可以渲染 WPF 的幾乎所有控件,需要注意 WPF 使用的是 Dx9 或 Dx12 fl9 沒有充分使用 Dx 進(jìn)行畫出現(xiàn)代的窗口;

  • GDI 這個(gè)代碼依賴顯卡,是進(jìn)行 CPU 渲染的接口,提供了繪制原語和提高質(zhì)量;

  • CLR 因?yàn)?WPF 現(xiàn)在還運(yùn)行在 dotnet framework,所以需要使用運(yùn)行時(shí),其提供了普通的類和方法,用來方便開發(fā)者,大概有 30 萬 API 可以使用;

  • Device Drivers 設(shè)備驅(qū)動(dòng)程序是在系統(tǒng)特殊的情況下用來驅(qū)動(dòng)一些硬件。

在下面使用到的代碼實(shí)際都是從最上層拿到的,只有最上層的代碼,微軟才會(huì)開放。而關(guān)鍵的 milCore 代碼我還拿不到,只能通過 WinDbg 拿到調(diào)用的堆棧。現(xiàn)在還沒有完全知道 milCore 的過程,所以本文也無法告訴大家。

本文的順序是從消息調(diào)度到開發(fā)者使用 OnRender 方法給繪制原語,再到如何把繪制原語給渲染線程的過程。從渲染線程調(diào)用 milCore,在通過 milCore 調(diào)用 DirectX 的過程就先簡單說過。從 DirectX 繪制完成到屏幕顯示的過程也是簡單告訴大家。

消息循環(huán)


在 WPF 中也是使用消息循環(huán),因?yàn)樵谥暗暮芏喑绦蚨际切枰约簩懴⒀h(huán)才可以收到用戶的交互,這里消息循環(huán)就是 Windows 會(huì)向 WPF 發(fā)送一些消息,而且 WPF 也可以給自己發(fā)消息,通過消息就可以判斷當(dāng)前軟件需要做哪些。

處理消息的最主要的類是 HwndSubclass,在創(chuàng)建應(yīng)用時(shí)會(huì)執(zhí)行 Attach 函數(shù),這個(gè)函數(shù)請(qǐng)看代碼:

internal IntPtr Attach(IntPtr hwnd){ // 忽略代碼 return this.CriticalAttach(hwnd);}

從上面代碼可以看到主要的是 CriticalAttach 函數(shù),請(qǐng)看代碼:

internal IntPtr CriticalAttach(IntPtr hwnd){  // 忽略代碼  NativeMethods.WndProc newWndProc = new NativeMethods.WndProc(this.SubclassWndProc); // 創(chuàng)建處理消息  IntPtr windowLongPtr = UnsafeNativeMethods.GetWindowLongPtr(new HandleRef((object) this, hwnd), -4);  this.HookWindowProc(hwnd, newWndProc, windowLongPtr);  return (IntPtr) this._gcHandle;}

這里 SubclassWndProc 就是主要處理消息,但是在上面代碼是 HookWindowProc 注冊(cè)。在 GetWindowLongPtr 大家也許都很熟悉,拿到這個(gè)函數(shù)就是拿到窗口,下面來看一下 HookWindowProc 代碼:

private void HookWindowProc(IntPtr hwnd, NativeMethods.WndProc newWndProc, IntPtr oldWndProc){ _hwndAttached = hwnd; _hwndHandleRef = new HandleRef(null,_hwndAttached); _bond = Bond.Attached; _attachedWndProc = newWndProc; _oldWndProc = oldWndProc; // 這有下面的代碼才是獲得消息 IntPtr oldWndProc2 = (IntPtr)UnsafeNativeMethods.CriticalSetWindowLong(_hwndHandleRef, NativeMethods.GWL_WNDPROC, _attachedWndProc); // 跟蹤這個(gè)窗口,可以在 CLR 關(guān)閉,撤掉管理的窗口代碼 ManagedWndProcTracker.TrackHwndSubclass(this, _hwndAttached);}

里 NativeMethods.GWL_WNDPROC 的值是 -4,在 CriticalSetWindowLong 是這樣寫:

internal static IntPtr CriticalSetWindowLong(HandleRef hWnd, int nIndex, NativeMethods.WndProc dwNewLong){  int errorCode;  IntPtr retVal;    retVal = NativeMethodsSetLastError.SetWindowLongPtrWndProc(hWnd, nIndex, dwNewLong);  errorCode = Marshal.GetLastWin32Error();    return retVal;}

所以核心就是 SetWindowLongPtrWndProc 調(diào)用 SetWindowLongWrapper 設(shè)置獲得消息。

那么消息是如何作為事件的?

internal IntPtr SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam){ // 忽略的代碼
// 獲得當(dāng)前線程的 dispatcher 也就是不是主線程也可以處理 Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread); // 忽略的代碼
DispatcherOperationCallbackParameter param = _paramDispatcherCallbackOperation; _paramDispatcherCallbackOperation = null; param.hwnd = hwnd; param.msg = msg; param.wParam = wParam; param.lParam = lParam; // 插入處理的消息 object result = dispatcher.Invoke( DispatcherPriority.Send, _dispatcherOperationCallback, param); var retval = param.retVal; return retval;}

也就是調(diào)用的 SubclassWndProc 實(shí)際上就是封裝消息,然后調(diào)用 dispatcher 執(zhí)行。那么 _dispatcherOperationCallback 有是如何做的?實(shí)際上這個(gè)也是調(diào)用這個(gè)弱引用委托,請(qǐng)看代碼,下面的代碼是去掉判斷參數(shù):

private object DispatcherCallbackOperation(object o){  DispatcherOperationCallbackParameter param = (DispatcherOperationCallbackParameter)o;  param.handled = false;  param.retVal = IntPtr.Zero; // 把返回值設(shè)置為 空,把是否處理設(shè)置為 false ,在下面調(diào)用函數(shù),如果可以處理就設(shè)置 param.handled 為 true  HwndWrapperHook hook = _hook.Target as HwndWrapperHook;  param.retVal = hook(param.hwnd, param.msg, param.wParam, param.lParam, ref param.handled);  return param;}

那么這個(gè) Handle 有什么用?這個(gè)屬性就是在為 false 時(shí)會(huì)在 SubclassWndProc 使用下面代碼:

// 如果窗口無法處理這個(gè)消息,把他發(fā)到下一個(gè)鏈,因?yàn)?Windows 消息是可以傳到下一個(gè)窗口if(!handled){  retval = CallOldWindowProc(oldWndProc, hwnd, message, wParam, lParam);}

那么這個(gè)處理 _hook 是怎么傳過來的,這個(gè)_hook 是一個(gè)弱引用,具體的傳送過來需要從 Dispatcher 開始說,請(qǐng)看下面。

Dispatcher 調(diào)度


關(guān)于調(diào)度需要從 PushFrame 開始說,請(qǐng)看《深入了解 WPF Dispatcher 的工作原理(PushFrame 部分) - walterlv[4],walterlv大佬已經(jīng)告訴大家底層的原理,我就可以不再這里詳細(xì)告訴大家。

在 Dispatcher 的構(gòu)造可以看到下面代碼:

MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();

創(chuàng)建 MessageOnlyHwndWrapper 可以收到消息,并且把消息放在 Dispatcher 用來處理,那么 MessageOnlyHwndWrapper 為什么可以收到消息? 可以看到 MessageOnlyHwndWrapper 的代碼很簡單:

internal class MessageOnlyHwndWrapper : HwndWrapper{ public MessageOnlyHwndWrapper() : base(0, 0, 0, 0, 0, 0, 0, '', NativeMethods.HWND_MESSAGE, null) { }}

就需要看一下 HwndWrapper 類的代碼,這個(gè)類的構(gòu)造函數(shù)的關(guān)鍵代碼:

public HwndWrapper()//太多參數(shù),這里就不寫了{  _wndProc = new SecurityCriticalData<HwndWrapperHook>(new HwndWrapperHook(WndProc));   // 還記得剛才說的消息,就是在這里創(chuàng)建 HwndSubclass 可以直接拿到消息  HwndSubclass hwndSubclass = new HwndSubclass(_wndProc.Value);}

所以在上面說的 _hook 弱引用就是 傳入的_wndProc ,那么這個(gè) _wndProc 的關(guān)鍵代碼就是WndProc,在 HwndWrapper 的函數(shù):       

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){ IntPtr result = IntPtr.Zero; WindowMessage message = (WindowMessage)msg; // 遍歷所有的 _hooks 處理消息 foreach(HwndWrapperHook hook in _hooks.Value) { result = hook(hwnd, msg, wParam, lParam, ref handled); if(handled) { break; } } return result;}

所以這個(gè)方法也就是沒有做具體處理,是通過AddHook函數(shù)添加的處理:

public void AddHook(HwndWrapperHook hook){  //VerifyAccess();  if(_hooks == null)  {    _hooks = new SecurityCriticalDataClass<WeakReferenceList>(new WeakReferenceList());  }    _hooks.Value.Insert(0, hook);}

因?yàn)槔④浀拇a,所以才在 Dispatcher 的構(gòu)造函數(shù)調(diào)用 AddHook ,也就是在構(gòu)造函數(shù)創(chuàng)建了 MessageOnlyHwndWrapper 在這個(gè)類初始化,具體處理是在初始化之后才加上,所以可以看到這個(gè)類有很多沒有用的代碼,因?yàn)槌跏蓟瘎?chuàng)建的值都是空,在創(chuàng)建之后才加上:

private Dispatcher(){ MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper(); _hook = new HwndWrapperHook(WndProcHook);  window.AddHook(_hook);}

最核心的處理消息就是 Dispatcher 的 WndProcHook,這個(gè)方法實(shí)際上只是調(diào)用 ProcessQueue 方法。

在 SubclassWndProc 調(diào)用的dispatcher.Invoke( DispatcherPriority.Send, _dispatcherOperationCallback, param)就是調(diào)用WndProcHook 函數(shù)傳入?yún)?shù)。

如果收到了畫窗口的消息,就會(huì)把這個(gè)消息發(fā)送給DWM,通過DWM把窗口畫的內(nèi)容畫到屏幕。

RenderContent


在使用繪制原語之后,最底層的函數(shù)是 UIElement.RenderContent ,請(qǐng)看代碼:

internal override void RenderContent(RenderContext ctx, bool isOnChannel){  DUCE.Channel channel = ctx.Channel;
DUCE.IResource drawingContent = (DUCE.IResource) this._drawingContent; drawingContent.AddRefOnChannel(channel);
DUCE.CompositionNode.SetContent(this._proxy.GetHandle(channel), drawingContent.GetHandle(channel), channel); this.SetFlags(channel, true, VisualProxyFlags.IsContentConnected);}

通過 DUCE.CompositionNode.SetContent 調(diào)用 channel.SendCommand 就可以發(fā)送到渲染線程:  

internal static unsafe void SetContent(DUCE.ResourceHandle hCompositionNode, DUCE.ResourceHandle hContent, DUCE.Channel channel){ DUCE.MILCMD_VISUAL_SETCONTENT visualSetcontent; visualSetcontent.Type = MILCMD.MilCmdVisualSetContent; visualSetcontent.Handle = hCompositionNode; visualSetcontent.hContent = hContent; // 通過下面代碼發(fā)送到另一個(gè)線程 channel.SendCommand((byte*) &visualSetcontent, sizeof (DUCE.MILCMD_VISUAL_SETCONTENT));}

那么這個(gè)渲染是如何觸發(fā)的?實(shí)際上是在 Dispatcher.ProcessQueue 調(diào)用,在上面已經(jīng)有告訴大家 ProcessQueue 是在 WndProcHook 觸發(fā)的:

private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){   if(message == _msgProcessQueue)  {    ProcessQueue();  }}

這里 _msgProcessQueue 就是在 Dispatcher 靜態(tài)構(gòu)造函數(shù)的自定義消息:

static Dispatcher(){ _msgProcessQueue = UnsafeNativeMethods.RegisterWindowMessage('DispatcherProcessQueue'); // 下面還有很多代碼}

那么這個(gè)消息是怎么發(fā)送的?在UIElement.InvalidateVisual函數(shù)會(huì)調(diào)用MediaContext.PostRender這里就發(fā)送自定義消息,于是就可以開始渲染。

在消息循環(huán)是會(huì)不斷獲取消息,這里說的渲染是包括兩個(gè)方面,一個(gè)是 WPF 把內(nèi)容畫到窗口,也就是上面說的自定義消息,還有另一個(gè)就是把窗口內(nèi)容畫在屏幕。這兩個(gè)都是依靠 Windows 消息,只是第一個(gè)消息是 WPF 自己發(fā)給自己,也就是自己玩的。從 Dispatcher 拿到自定義的消息,就開始執(zhí)行視覺樹的對(duì)象,調(diào)用對(duì)應(yīng)的繪制,這里是收集到繪制原語,也就是告訴顯卡可以怎么畫。在底層是通過 System.Windows.Media.Composition.DUCE 的 Channel 把數(shù)據(jù)發(fā)送到渲染線程,渲染線程就是使用 Dx 進(jìn)行繪制。在 Dx 畫是使用 milCore 從渲染線程連接到 Dx 畫出來的。

在渲染線程收集到的都是繪制原語,繪制原語就是在 Visual 底層調(diào)用的DrawingContext 傳入的方法。

這一部分沒有完全跟源代碼,如果有哪些地方和實(shí)際不相同,請(qǐng)告訴我。

渲染線程拿到了繪制原語就可以進(jìn)行繪制,繪制的過程需要進(jìn)行處理圖片和一些基礎(chǔ)形狀,大概的過程請(qǐng)看下面:

這時(shí)到了 Dx 才會(huì)使用顯卡進(jìn)行渲染,并且繪制的是窗口指針。也就是窗口繪制完成在屏幕還是無法看到的。

在繪制的時(shí)候需要使用 MIL 解碼一些圖片和一些形狀才可以用到 dx 進(jìn)行渲染。

在窗口畫完之后,會(huì)通過 WM_PAINT 告訴 DWM 可以畫出窗口。但是現(xiàn)代的應(yīng)用是不需要在窗口刷新的過程通過 windows 消息發(fā)送到 DWM 才進(jìn)行窗口刷新。只有在窗口存在部分不可見,如窗口有一部分在屏幕之外,從屏幕外移到屏幕內(nèi),或者窗口最小化到顯示才需要通過 windows 告訴 DWM 刷新。

那么這里的 DWM 是什么?請(qǐng)看下面。

桌面窗口管理


在 Windows 系統(tǒng),很重要的就是 DWM(Desktop Window Manager),它可以把窗口畫到屏幕,并且支持窗口做半透明和其他的對(duì)其他窗口的視覺處理。

需要知道,發(fā)送 WM_PAINT 消息只能通過系統(tǒng)發(fā)送而不能通過應(yīng)用發(fā)送,也就是上面說的通過 WM_PAINT 告訴 DWM 可以畫出窗口,不是軟件主動(dòng)告訴系統(tǒng),而是系統(tǒng)會(huì)不斷刷新。

對(duì)于不可見的窗口,在 DWM 是不會(huì)發(fā)送 WM_PAINT 到這個(gè)窗口。

詳細(xì)的 WM_PAINT 請(qǐng)看《 WMPAINT詳解和WMERASEBKGND - CSDN博客[5]

如果系統(tǒng)沒有發(fā)送 WM_PAINT 到應(yīng)用,屏幕怎么知道窗口需要刷新?實(shí)際通過《Drawing Without the WM_PAINT Message[6]的方法就可以做到。

在 Windows 8 之后就無法手動(dòng)設(shè)置關(guān)閉 DWM 的合成,只有在 windows 7 和以前的系統(tǒng)才可以設(shè)置關(guān)閉合成。通過 DWM 合成技術(shù)可以將每個(gè)繪制的窗口認(rèn)為是一個(gè)位圖,通過對(duì)位圖處理添加陰影等,做出好看界面。

更多請(qǐng)看 《Desktop Window Manager[7]

從控件畫出來到屏幕顯示


雖然上面寫了很多,但是好多小伙伴都不會(huì)仔細(xì)去看,所以本渣就在這里重新把過程說一遍。需要知道,這里說的省略了很多細(xì)節(jié),上面的也是有很多細(xì)節(jié)沒有告訴大家。

在渲染的時(shí)候,是需要通過多個(gè)方式把渲染的任務(wù)放在 Dispather 里,在 WPF 應(yīng)用內(nèi)是可以通過 InvalidateVisual 的方法通知,而系統(tǒng)也在不斷發(fā)送消息告訴一個(gè)應(yīng)用開始渲染。

在 Dispatcher 收到消息之后就可以把渲染任務(wù)放在隊(duì)列,按照優(yōu)先級(jí)一個(gè)個(gè)出隊(duì)。

這時(shí)在 Dispatcher 內(nèi)部通過渲染的調(diào)用就會(huì)通過 DispatcherOperation 執(zhí)行對(duì)應(yīng)的任務(wù)。

渲染的任務(wù)流程如下:

mscorlib.dll! System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) ->WindowsBase.dll! System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)->PresentationFramework.dll! System.Windows.Window.ShowHelper(object booleanBox)

最后也就是顯示的最近的代碼。

總的渲染結(jié)構(gòu)請(qǐng)看下圖:

渲染需要經(jīng)過消息循環(huán)和 Dispatcher 循環(huán),也就是渲染的方法不是直接通過控件調(diào)用渲染。控件是通過發(fā)送消息經(jīng)過消息循環(huán)再調(diào)用到控件的 OnRender 方法。再 OnRender 方法里,經(jīng)過 Drawing 方法輸出繪制原語到渲染線程。渲染線程經(jīng)過 MIL 和 Dx 渲染界面到窗口。屏幕管理更新窗口讓用戶在屏幕可以看到。

關(guān)于渲染性能請(qǐng)看《WPF Drawing Performance[8]

參考資料


[1] 《WPF Architecture 》https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/wpf-architecture

[2] 《WPF 使用 Direct2D1 畫圖入門》https://blog.csdn.net/lindexi_gd/article/details/80387784

[3] Overview of Windows Presentation Foundation (WPF) Architecture》https://www.c-sharpcorner.com/UploadFile/819f33/overview-of-windows-presentation-foundation-wpf-architectu/

[4] 《深入了解 WPF Dispatcher 的工作原理(PushFrame 部分) - walterlv》https://walterlv.github.io/post/dotnet/2017/09/26/dispatcher-push-frame.html

[5] WM_PAINT詳解和WM_ERASEBKGND - CSDN博客》https://blog.csdn.net/rankun1/article/details/50596634

[6] 《Drawing Without the WM_PAINT Message》https://docs.microsoft.com/en-us/windows/desktop/gdi/drawing-without-the-wm-paint-message

[7] 《Desktop Window Manager》https://docs.microsoft.com/en-us/windows/desktop/dwm/dwm-overview

[8] 《WPF Drawing Performance》http://kynosarges.org/WpfPerformance.html

[9] 《一站式WPF–Window(一) - 周永恒 - 博客園》https://www.cnblogs.com/Zhouyongh/archive/2009/11/30/1613628.html

[10] 《WPF起步(上) — WPF是如何把圖像畫到屏幕上 - CSDN博客》https://blog.csdn.net/eparg/article/details/1930357

[11] 《Sharing Message Loops Between Win32 and WPF》https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/sharing-message-loops-between-win32-and-wpf

[12] 《1.3 WPF體系結(jié)構(gòu) - 51CTO.COM》http://book.51cto.com/art/200908/145309.htm

[13] 《WM_PAINT message》https://docs.microsoft.com/zh-cn/windows/desktop/gdi/wm-paint

[14] 《Custom Window Frame Using DWM》https://docs.microsoft.com/en-us/windows/desktop/dwm/customframe

[15] 《Performance Considerations and Best Practices》https://docs.microsoft.com/en-us/windows/desktop/dwm/bestpractices-ovw

[16] 《Win32知識(shí)之窗口繪制窗口第一講》https://www.cnblogs.com/iBinary/p/9576439.html

[17] 《win32程序之窗口程序以及消息機(jī)制》https://www.cnblogs.com/iBinary/p/9580268.html

[18]《課件 WPF 渲染原理》https://r302.cc/p2m3ea

[19]《WPF 渲染相關(guān)》https://lindexi.gitee.io/post/%E6%B8%B2%E6%9F%93.html

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
[C# 開發(fā)技巧]如何防止程序多次運(yùn)行
C# WPF項(xiàng)目實(shí)戰(zhàn)
[c#] 通過 WIN32 API 實(shí)現(xiàn)嵌入程序窗體
C# Windows API應(yīng)用之GetDesktopWindow
C# 互操作性入門系列(二):使用平臺(tái)調(diào)用調(diào)用Win32 函數(shù)
在MFC中使用WPF技術(shù)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服