使用 Buffered Paint API 繪製帶有淡入淡出動畫的控制項

来源:https://www.cnblogs.com/firespeed/archive/2022/09/19/16709026.html
-Advertisement-
Play Games

使用 Buffered Paint API 繪製帶有淡入淡出動畫的控制項 發表於2011 年 10 月 23 日 Windows 窗體提供了許多機制來構建與操作系統風格相匹配的專業自定義 UI 控制項;通過結合視覺風格渲染器、系統顏色/畫筆、ControlPaint類等,可以在用戶代碼中重現大多數標準 ...


使用 Buffered Paint API 繪製帶有淡入淡出動畫的控制項

發表於2011 年 10 月 23 日 

Windows 窗體提供了許多機制來構建與操作系統風格相匹配的專業自定義 UI 控制項;通過結合視覺風格渲染器、系統顏色/畫筆、ControlPaint類等,可以在用戶代碼中重現大多數標準 Windows 控制項。

然而,在托管代碼中很難重新創建內置控制項的一個方面:從 Windows Vista 開始,許多控制項(例如ButtonComboBoxTextBox等)在狀態之間轉換時使用淡入淡出動畫,例如作為焦點,滑鼠懸停和按鈕按下。在內部,這些動畫由緩衝的繪製 API(uxtheme.dll的一部分,負責視覺樣式的庫)處理。

大多數開發人員會滿足於瞬時的視覺狀態變化,但對於受過訓練的眼睛來說,缺乏平滑過渡確實可以使自定義控制項從內置控制項中脫穎而出。好消息是,雖然沒有用於緩衝繪畫的托管 API,但使用 PInvoke 相對容易利用。

緩衝繪畫 API - 基礎知識

Imports

[DllImport("uxtheme")]
static extern IntPtr BufferedPaintInit();

[DllImport("uxtheme")]
static extern IntPtr BufferedPaintUnInit();

[DllImport("uxtheme")]
static extern IntPtr BeginBufferedAnimation(
    IntPtr hwnd,
    IntPtr hdcTarget,
    ref Rectangle rcTarget,
    BP_BUFFERFORMAT dwFormat,
    IntPtr pPaintParams,
    ref BP_ANIMATIONPARAMS pAnimationParams,
    out IntPtr phdcFrom,
    out IntPtr phdcTo
);

[DllImport("uxtheme")]
static extern IntPtr EndBufferedAnimation(IntPtr hbpAnimation, bool fUpdateTarget);

[DllImport("uxtheme")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool BufferedPaintRenderAnimation(IntPtr hwnd, IntPtr hdcTarget);

[DllImport("uxtheme")]
static extern IntPtr BufferedPaintStopAllAnimations(IntPtr hwnd);

 

用法

  • 您使用BufferedPaintInit / BufferedPaintUnInit初始化/結束會話(通常在控制項的生命周期內持續)
  • 你用BeginBufferedAnimation開始一個淡入淡出動畫,傳入一個 BP_ANIMATIONPARAMS結構來描述過渡。返回動畫句柄和兩個空點陣圖。
  • 您將開始幀和結束幀繪製到各自的點陣圖上(使用 GDI+,就像您正常繪製控制項一樣)並調用EndBufferedAnimation開始播放它。
  • 在播放動畫時,控制項的Paint事件將被觸發多次。通過檢查BufferedPaintRenderAnimation返回的值來檢查是否是由緩衝的繪製動畫觸發的如果是這樣,請不要自己繪製控制項。
 
void Control_Paint(object sender, PaintEventArgs e) {
    IntPtr hdc = e.Graphics.GetHdc();
    if (hdc != IntPtr.Zero) {
        // see if this paint was generated by a soft-fade animation
        if (!Interop.BufferedPaintRenderAnimation(Control.Handle, hdc)) {
            BP_ANIMATIONPARAMS animParams = new BP_ANIMATIONPARAMS();
            animParams.cbSize = Marshal.SizeOf(animParams);
            animParams.style = BP_ANIMATIONSTYLE.BPAS_LINEAR;

            // set duration according to state transition
            animParams.dwDuration = 125;

            // begin the animation
            Rectangle rc = Control.ClientRectangle;
            IntPtr hdcFrom, hdcTo;
            IntPtr hbpAnimation = Interop.BeginBufferedAnimation(
                Control.Handle,
                hdc,
                ref rc,
                BP_BUFFERFORMAT.BPBF_COMPATIBLEBITMAP,
                IntPtr.Zero,
                ref animParams,
                out hdcFrom,
                out hdcTo
            );

            if (hbpAnimation != IntPtr.Zero) {
                if (hdcFrom != IntPtr.Zero) /* paint start frame to hdcFrom */;
                if (hdcTo != IntPtr.Zero) /* paint end frame to hdcTo */;
                Interop.EndBufferedAnimation(hbpAnimation, true);
            }
            else {
                /* paint control normally */
            }
        }

        e.Graphics.ReleaseHdc(hdc);
    }
}

 

關於在 Windows 窗體控制項上使用緩衝繪製 API 的一些進一步說明:

  • 如果控制項是雙緩衝的(DoubleBuffered屬性設置為 true 或OptimizedDoubleBuffer樣式標誌設置),則不支持動畫。
  • 要減少閃爍,請重寫控制項的OnPaintBackground方法,並且不要調用基類方法。您可以在繪製控制項的其餘部分時手動繪製控制項的背景。
  • 每當調整控制項大小時,應使用BufferedPaintStopAllAnimations停止所有正在運行的動畫。

BufferedPainter – 一個簡化緩衝繪畫的托管類

緩衝繪畫涉及一定數量的樣板代碼。您需要向控制項的創建/處置事件添加代碼,使用特定模式覆蓋Paint事件,然後提供用於繪製控制項的替代方法(到屏幕或點陣圖)。

消除這種樣板代碼的一種方法是編寫一個從Control派生的基類,它提供了這個功能。然而,這有點限制,因為所有使用緩衝繪畫的自定義控制項都必須從這個類繼承。實際上,您可能希望在全新控制項中以及在繼承現有控制項(例如ComboBox)時使用緩衝繪製。出於這個原因,我編寫了一個獨立的類並附加到任何類型的控制項。

BufferedPainter是一個泛型類,它允許使用任何類型來表示控制項的視覺狀態;這可能是枚舉、整數甚至更複雜的類型。只要該類型提供Equals方法(或具有合適的預設實現),它就可以用於跟蹤狀態轉換。一個簡單的按鈕控制項可能具有三種狀態;正常,熱和推。BufferedPainter保存有關狀態更改和狀態之間動畫持續時間(如果需要)的信息。它存儲控制項的當前視覺狀態,覆蓋控制項的Paint事件並提供由用戶代碼處理的PaintVisualState事件。

它還提供了一種機制來簡化觸發控制項視覺狀態變化的過程;除了手動設置控制項的狀態(使用State屬性)之外,您還可以添加一個觸發器,該觸發器會根據條件(例如滑鼠懸停在控制項上)更改為特定狀態。這進一步減少了控制中所需的代碼量。條件可以特定於控制項範圍內的區域,並且錨定可用於在調整控制項大小時自動更新區域。

向控制項添加緩衝繪製支持非常簡單:

// using an enum type 'MyVisualStates' to describe the control's visual state
BufferedPainter<MyVisualStates> painter = new BufferedPainter<MyVisualStates>(/* control instance */);
painter.PaintVisualState += /* event handler which paints the control in a particular state */;

// describe the state transitions we want to animate
painter.AddTransition(MyVisualStates.Normal, MyVisualStates.Hot, 125); // fade in
painter.AddTransition(MyVisualStates.Hot, MyVisualStates.Pushed, 75);
painter.AddTransition(MyVisualStates.Hot, MyVisualStates.Normal, 250); // fade out

// describe what causes the control to change its visual state
painter.AddTrigger(VisualStateTriggerTypes.Hot, MyVisualStates.Hot); // mouse over
painter.AddTrigger(VisualStateTriggerTypes.Pushed, MyVisualStates.Pushed); // mouse down

 

BufferedPainting.zip

 

包括示例控制項

最後的話

緩衝繪製 API 填補了在托管代碼中編寫與 OS 風格相匹配的自定義控制項的最後一個空白,無論是在靜態外觀還是動畫方面。我希望您發現我的代碼對在您自己的自定義控制項中實現平滑過渡很有用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 耳返功能簡介 ZEGO Express SDK 提供了Flutter耳返和雙聲道的功能,在視頻直播、K歌、音頻錄製等場景下廣泛應用,開發者可根據實際業務場景需要設置,一套代碼可實現跨平臺音視頻耳返功能,節省開發成本。 實時音視頻的耳返作用就是在嘈雜的環境下,清楚地聽伴奏和自己的聲音,來鑒定自己有 ...
  • 最近在完成軟體體繫結構上機實驗時,遇到一個有點點小難度的選做題,題目信息如下: 利用套接字技術實現應用程式中對資料庫的訪問。應用程式只是利用套接字連接向伺服器發送一個查詢的條件,而伺服器負責對資料庫的查詢,然後伺服器再將查詢的結果利用建立的套接字返回給客戶端,如下圖所示。 本來吧,選做題,不太想做的 ...
  • 一、bean被創建的時間 考慮一個問題,我們都知道spring通過xml的配置創建bean,那麼bean是什麼時間被創建的呢?是在我們getBean()的時候創建的嗎? 我們來做一個測試: 1.首先建立一個User類: package com.jms.pojo; public class User ...
  • Microsoft Word 提供了許多易於使用的文檔操作工具,同時也提供了豐富的功能集供創建複雜的文檔使用。在使用的時候,你可能需要複製一個文檔裡面的內容到另一個文檔。複製的內容可支持包括文本、圖片、表格、超鏈接、書簽、批註、形狀、編號列表、腳註、章節附註等等在內的多種元素。 ...
  • 摘要:本文講述圖像金字塔知識,瞭解專門用於圖像向上採樣和向下採樣的pyrUp()和pyrDown()函數。 本文分享自華為雲社區《[Python圖像處理] 二十一.圖像金字塔之圖像向下取樣和向上取樣》,作者:eastmount。 一.圖像金字塔 圖像金字塔是指由一組圖像且不同分別率的子圖集合,它是圖 ...
  • 併發框架Disruptor 1. Disruptor概述 1.1 背景 ​ Disruptor是英國外匯交易公司LMAX開發的一個高性能隊列,研發的初衷是解決記憶體隊列的延遲問題(在性能測試中發現竟然與I/O操作處於同樣的數量級),基於Disruptor開發的系統單線程能支撐每秒600萬訂單,2010 ...
  • 很多人學編程經常是腦子一熱然後就去網上一搜資源就開始學習了,但學到了後面發現目前所學的東西並不是自己最喜歡的,好像自己更喜歡另一個技術,感覺自己學錯了,於是乎又去學習別的東西。 結果竹籃打水一場空,前面所付出的努力都白費了,甚至有人還花了錢買了課,這個實在是划不來。 所以在你學一門編程語言之前,一定 ...
  • 配置訪問介面 public IConfiguration _Config; public 類名 (IConfiguration Config) { _Config = Config; } 配置文件數據示例 { "AllowedHosts": "*", "Users": [ { "Id": "123" ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...