[UWP]理解ControlTemplate中的VisualTransition

来源:https://www.cnblogs.com/dino623/archive/2018/03/21/VisualTransition.html
-Advertisement-
Play Games

1. 前言 VisualTransition是控制項模板中的重要組成部分,無論是自定義控制項或者修改控制項樣式都會接觸到VisualTransition。明明這麼重要,博客園上好像都沒多少關於VisualTransition的主題。 2. 什麼是VisualTransition VisualTransit ...


1. 前言

VisualTransition是控制項模板中的重要組成部分,無論是自定義控制項或者修改控制項樣式都會接觸到VisualTransition。明明這麼重要,博客園上好像都沒多少關於VisualTransition的主題。

2. 什麼是VisualTransition

VisualTransition動畫定義VisualState之前切換時的過渡行為,包括過渡時間和過渡動畫。

VisualTransition的類定義如下:

[ContentProperty(Name = "Storyboard")]
public class VisualTransition : DependencyObject, IVisualTransition
{
    public VisualTransition();

    // 摘要:
    //     獲取或設置要轉換為的 Windows.UI.Xaml.VisualState 的名稱。
    public string To { get; set; }

    //
    // 摘要:
    //     獲取或設置在發生轉換時運行的 Windows.UI.Xaml.Media.Animation.Storyboard。
    public Storyboard Storyboard { get; set; }

    //
    // 摘要:
    //     獲取或設置應用於生成的動畫的緩動函數。
    public EasingFunctionBase GeneratedEasingFunction { get; set; }

    //
    // 摘要:
    //     獲取或設置從一種狀態轉換到另一種狀態所花的時間,以及任何隱式過渡動畫應作為過渡行為的一部分運行的時間
    public Duration GeneratedDuration { get; set; }

    //
    // 摘要:
    //     獲取或設置要轉換的 Windows.UI.Xaml.VisualState 的名稱。
    public string From { get; set; }
}

3.為什麼使用VisualTransition

雖然自WPF4以來VisualTransition一直都存在,但很多人還是習慣這樣寫VisualState:

<VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal" />
    <VisualState x:Name="PointerOver">
        <Storyboard>
            <DoubleAnimation  Storyboard.TargetProperty="Opacity"
                              Storyboard.TargetName="PointOverElement"
                              Duration="0"
                              To="1" />
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Pressed">
        <Storyboard>
            <DoubleAnimation  Storyboard.TargetProperty="Opacity"
                              Storyboard.TargetName="PressElement"
                              Duration="0"
                              To="1" />
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Disabled" />
</VisualStateGroup>

正確的做法應該是這樣:

<VisualStateGroup x:Name="CommonStates">
    <VisualStateGroup.Transitions>
        <VisualTransition To="PointerOver">
            <Storyboard>
                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
                                               Storyboard.TargetName="PointOverElement">
                    <EasingDoubleKeyFrame KeyTime="0"
                                          Value="0" />
                    <EasingDoubleKeyFrame KeyTime="0:0:2"
                                          Value="1">
                        <EasingDoubleKeyFrame.EasingFunction>
                            <CubicEase EasingMode="EaseOut" />
                        </EasingDoubleKeyFrame.EasingFunction>
                    </EasingDoubleKeyFrame>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </VisualTransition>
        <VisualTransition To="Pressed">
            <Storyboard>
                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
                                               Storyboard.TargetName="PressElement">
                    <EasingDoubleKeyFrame KeyTime="0"
                                          Value="0" />
                    <EasingDoubleKeyFrame KeyTime="0:0:2"
                                          Value="1">
                        <EasingDoubleKeyFrame.EasingFunction>
                            <CubicEase EasingMode="EaseOut" />
                        </EasingDoubleKeyFrame.EasingFunction>
                    </EasingDoubleKeyFrame>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </VisualTransition>
        <VisualTransition To="Disabled">
            <Storyboard Completed="Storyboard_Completed"></Storyboard>
        </VisualTransition>
    </VisualStateGroup.Transitions>
    <VisualState x:Name="Normal" />
    <VisualState x:Name="PointerOver">
        <Storyboard>
            <DoubleAnimation  Storyboard.TargetProperty="Opacity"
                              Storyboard.TargetName="PointOverElement"
                              Duration="0"
                              To="1" />
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Pressed">
        <Storyboard>
            <DoubleAnimation  Storyboard.TargetProperty="Opacity"
                              Storyboard.TargetName="PressElement"
                              Duration="0"
                              To="1" />
        </Storyboard>
    </VisualState>
    <VisualState x:Name="Disabled" />
</VisualStateGroup>

可以看到VisualState中的Storyboard只用於定義VisualState的最終可視狀態,而在VIsualState間轉換時用戶看到的是VisualTransition 中定義的Storyboard。但這樣的話兩處的Storyboard不就重覆了?帶著這個疑問很多年,微軟終於給出了另一種方案VisualState.Setters:

<VisualStateGroup x:Name="CommonStates">
    <VisualStateGroup.Transitions>
        ...
    </VisualStateGroup.Transitions>
    <VisualState x:Name="Normal" />
    <VisualState x:Name="PointerOver">
        <VisualState.Setters>
            <Setter Target="PointOverElement.(UIElement.Opacity)"
                    Value="1" />
        </VisualState.Setters>
    </VisualState>
    <VisualState x:Name="Pressed">
        <VisualState.Setters>
            <Setter Target="PressElement.(UIElement.Opacity)"
                    Value="1" />
        </VisualState.Setters>
    </VisualState>
    <VisualState x:Name="Disabled" />
</VisualStateGroup>

這樣VisualState的做法就十分清晰明瞭:

  • 代碼使用VisualStateManager控制控制項當前的VisualState;
  • VisualState.Setters定義這個VisualState最終在UI上如何呈現;
  • VisualState間的過渡動畫由VisualTransition定義;

4. 怎麼使用VisualTransition

4.1 隱式轉換

不使用Storyboard的VisualTransition稱為隱式轉換:

<VisualStateGroup.Transitions >
    <VisualTransition GeneratedDuration="0:0:3"/>
</VisualStateGroup.Transitions>

如上面這段XAML中的VisualTransition ,它指定VisualStateGroup中所有VisualState之間的過渡時間都是3秒,在這3秒中VisualState中的Double、Point和Color使用預設的線性插值方式進行動畫轉換。而其它值,如Visibility,則不可以使用隱式轉換。

這段XAML在Blend中對應“狀態”面板里VisualStateGroup的“預設過渡”。

隱式轉換可以進一步設置其它屬性,如以下XAML:

<VisualStateGroup.Transitions>
    <VisualTransition To="PointerOver"
                      GeneratedDuration="0:0:3">
        <VisualTransition.GeneratedEasingFunction>
            <ExponentialEase EasingMode="EaseOut" />
        </VisualTransition.GeneratedEasingFunction>
    </VisualTransition>
    <VisualTransition From="PointerOver"
                      To="Pressed"
                      GeneratedDuration="0:0:3">
        <VisualTransition.GeneratedEasingFunction>
            <ExponentialEase EasingMode="EaseOut" />
        </VisualTransition.GeneratedEasingFunction>
    </VisualTransition>
</VisualStateGroup.Transitions>

這段XAML中VisualTransition指定了以下三種屬性:

  • From和To,轉換的舊狀態和新狀態,可以單獨指定。

  • 動畫的緩動函數。

4.2 使用Storyboard

當隱式轉換不能滿足需求,可以使用Storyboard指定轉換的動畫。這時Storyboard不需要設置FillBehavior="HoldEnd",因為Storyboard結束後將保持VisualState設置的最終狀態。

<VisualTransition To="PointerOver">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                       Storyboard.TargetName="PointOverElement">
            <DiscreteObjectKeyFrame KeyTime="0">
                <DiscreteObjectKeyFrame.Value>
                    <Visibility>Visible</Visibility>
                </DiscreteObjectKeyFrame.Value>
            </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
                                       Storyboard.TargetName="PointOverElement">
            <EasingDoubleKeyFrame KeyTime="0"
                                  Value="0" />
            <EasingDoubleKeyFrame KeyTime="0:0:2"
                                  Value="1">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CubicEase EasingMode="EaseOut" />
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</VisualTransition>
<VisualTransition To="Pressed">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
                                       Storyboard.TargetName="PressElement">
            <DiscreteObjectKeyFrame KeyTime="0">
                <DiscreteObjectKeyFrame.Value>
                    <Visibility>Visible</Visibility>
                </DiscreteObjectKeyFrame.Value>
            </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
                                       Storyboard.TargetName="PressElement">
            <EasingDoubleKeyFrame KeyTime="0"
                                  Value="0" />
            <EasingDoubleKeyFrame KeyTime="0:0:2"
                                  Value="1">
                <EasingDoubleKeyFrame.EasingFunction>
                    <CubicEase EasingMode="EaseOut" />
                </EasingDoubleKeyFrame.EasingFunction>
            </EasingDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</VisualTransition>

5. 為什麼有時候VisualTransition沒有生效

ControlTemplate在VisualState之間切換是靠下麵這個函數控制的:

//
// 摘要:
//     通過按名稱請求新的 Windows.UI.Xaml.VisualState 來在兩個狀態之間轉換控制項。
//
// 參數:
//   control:
//     要進行狀態過渡的控制項。
//
//   stateName:
//     要過渡到的狀態。
//
//   useTransitions:
//     如果使用 Windows.UI.Xaml.VisualTransition 在各狀態之間轉換,則為 **true**。 如果跳過使用轉換並直接轉到請求的狀態,則為
//     **false**。 預設值為 **false**。
//
// 返回結果:
//     如果控制項成功轉換到新狀態或者已經在使用該狀態,則為 **true**;否則為 **false**。
public static bool GoToState(Control control, string stateName, bool useTransitions);

如果useTransitions這個參數為false,則VisualState之間切換時不會使用VisualTransition。在控制項載入模板時(即調用OnApplyTemplate()函數時)通常會這樣做,因為控制項在呈現時通常都不需要做動畫。

另外,VisualStateManager.GoToState不會使控制項重覆進入某個狀態,即如果控制項已處於PointerOver的VisualState,再次調用VisualStateManager.GoToState(this, PointerOverState, useTransitions)不會觸發任何操作,也不會重覆觸發動畫。

6. 結語

除了VisualState.Setters,這篇文章的內容基本和WPF通用。

上次被批評寫得太複雜了,這次本來寫了很多,為了文章簡單易懂刪了一半,希望對理解VisualTransition有幫助。

7. 參考

VisualTransition Class (Windows)
VisualTransition Class (Windows.UI.Xaml) - UWP app developer Microsoft Docs

8. 源碼

AnimationTest


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

-Advertisement-
Play Games
更多相關文章
  • 顧名思義,HTML輔助方法(HTML Helper)就是用來輔助產生HTML之用,在開發View的時候一定會面對許多HTML標簽,處理這些HTML的工作非常繁瑣,為了降低View的複雜度,可以使用HTML輔助方法幫助你產生一些HTML標簽或內容,因這些HTML標簽都有固定標準的寫法,所以將其包裝成H ...
  • 一、前言 最近忙裡偷閑,做了一個部署資料庫及IIS網站站點的WPF應用程式工具。 二、內容 此工具的目的是: 最終樣式:(Check按鈕的作用是防止與本機已有的站點或程式池有衝突) View: View的後臺文件: ViewModel: ...
  • 一、瞭解SpringMVC運行流程及九大組件 1、SpringMVC 的運行流程 · 用戶發送請求至前端控制器DispatcherServlet · DispatcherServlet收到請求調用HandlerMapping處理器映射器。 · 處理器映射器根據請求url找到具體的處理器,生成處理器對 ...
  • 原文地址:http://www.2cto.com/kf/201202/121170.html 我們在做數據系統的時候,經常會用到模糊搜索,但是,資料庫提供的模糊搜索並不具備按照相關度進行排序的功能。 現在提供一個比較兩個字元串相似度的方法。通過計算出兩個字元串的相似度,就可以通過Linq在記憶體中對數 ...
  • 登錄功能實現起來有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,當然還有其他模式,今天主要探討一下在Asp.net core 2.0下實現以cookie登錄授權,與net freamwork框架下常用的開發方式有所不同的是以前開發不管是webform... ...
  • 概述 Toast Notification 在 UWP App 中有很重要的作用,能夠很大程度上增強 App 和用戶之間的溝通,比如運營推廣活動、版本更新、提醒類任務提示等等。Toast Notification 可以通過圖片、文字、按鈕等創建可適配、可交互的通知。 我們在 About Window ...
  • 多租戶技術或稱多重租賃技術,簡稱SaaS,是一種軟體架構技術,是實現如何在多用戶環境下共用相同的系統或程式組件,並且可確保各用戶間數據的隔離性。簡單講:在一臺伺服器上運行單個應用實例,它為多個租戶(客戶)提供服務。從定義中我們可以理解:多租戶是一種架構,目的是為了讓多用戶環境下使用同一套程式,且保證... ...
  • 第一步:安裝 安裝“System.Web.Optimization”:在中“NuGet”中搜索 安裝。 第二步:配置 配置“Views”目錄下的“web.config”文件增加“System.Web.Optimization”配置 第三部:寫代碼 在“App_Start”文件夾新建名叫“Bundle ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...