使用 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.講故事 在分析的眾多dump中,經常會遇到各種奇葩的問題,僅通過dump這種快照形式還是有很多問題搞不定,而通過 perfview 這種粒度又太粗,很難找到問題之所在,真的很頭疼,比如本篇的 短命線程 問題,參考圖如下: 我們在 t2 時刻抓取的dump對查看 短命線程 毫無幫助,我根 ...
  • 在日常後端Api開發中,我們跟前端的溝通中,通常需要協商好入參的數據類型,和參數是通過什麼方式存在於請求中的,是表單(form)、請求體(body)、地址欄參數(query)、還是說通過請求頭(header)。 當協商好後,我們的介面又需要怎麼去接收這些數據呢?很多小伙伴可能上手就是直接寫一個實體, ...
  • 許多情況下我們需要用到攝像頭獲取圖像,進而處理圖像,這篇博文介紹利用pyqt5、OpenCV實現用電腦上連接的攝像頭拍照並保存照片。為了使用和後續開發方便,這裡利用pyqt5設計了個相機界面,後面將介紹如何實現,要點包括界面設計、邏輯實現及完整代碼。 ...
  • 思路分析 註冊頁面需要對用戶提交的數據進行校驗,並且需要對用戶輸入錯誤的地方進行提示! 所有我們需要使用forms組件搭建註冊頁面! 平時我們書寫form是組件的時候是在views.py裡面書寫的, 但是為了接耦合,我們需要將forms組件都單獨寫在一個地方,需要用的時候導入就行! 例如,在項目文件 ...
  • 思路分析 登錄頁面,我們還是採用ajax的方式提交用戶數據 唯一需要學習的是如何製作圖片驗證碼! 具體的登錄頁面效果圖如下: 如何製作圖片驗證碼 推導步驟1:在img標簽的src屬性里放上驗證碼的請求路徑 補充1.img的src屬性: 1.圖片路徑 2.url 3.圖片的二進位數據 補充2:字體樣式 ...
  • 哈嘍,兄弟們! 最近有許多小伙伴都在吐槽打工好難。 每天都是執行許多重覆的任務 例如閱讀新聞、發郵件、查看天氣、打開書簽、清理文件夾等等, 使用自動化腳本,就無需手動一次又一次地完成這些任務, 非常方便啊有木有?! 而在某種程度上,Python 就是自動化的代名詞。 今天就來和大家一起學習一下, 用 ...
  • 作者:IT王小二 博客:https://itwxe.com 前面小二介紹過使用Typora+PicGo+LskyPro打造舒適寫作環境,那時候需要使用水印功能,但是小二在升級LskyPro2.x版本發現有很多不如人意的東西,遂棄用LskyPro使用MinIO結合代碼實現自己需要的圖床功能,也適合以後 ...
  • OpenAI Gym是一款用於研發和比較強化學習演算法的工具包,本文主要介紹Gym模擬環境的功能和工具包的使用方法,並詳細介紹其中的經典控制問題中的倒立擺(CartPole-v0/1)問題。最後針對倒立擺問題如何建立控制模型並採用爬山演算法優化進行了介紹,並給出了相應的完整python代碼示例和解釋。要... ...
  • python爬蟲瀏覽器偽裝 #導入urllib.request模塊 import urllib.request #設置請求頭 headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, l ...
  • 前端代碼搭建 主要利用的是bootstrap3中js插件里的模態框版塊 <li><a href="" data-toggle="modal" data-target=".bs-example-modal-lg">修改密碼</a></li> <div class="modal fade bs-exam ...