使用 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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...