UWP Button添加圓角陰影(三)

来源:https://www.cnblogs.com/blue-fire/archive/2018/10/10/9768780.html
-Advertisement-
Play Games

原文: "UWP Button添加圓角陰影(三)" Composition DropShadow是CompositionAPI中的東西,使用Storyboard設置某個屬性,就是頻繁的觸發put_xxx()方法,效率遠遠不如使用CompositionAnimation。 Composition對象的 ...


原文:UWP Button添加圓角陰影(三)

Composition

DropShadow是CompositionAPI中的東西,使用Storyboard設置某個屬性,就是頻繁的觸發put_xxx()方法,效率遠遠不如使用CompositionAnimation。
Composition對象的基類CompositionObject擁有一個屬性叫ImplicitAnimations,可以通過他實現累死css的transition的效果,也就是對應屬性修改的時候,平滑的過渡過去。
可以從DropShadowPanel的源代碼中看到,DropShadow是設置在ShadowElement上的ChildVisual。
相關內容可以查閱將可視化層與 XAML 結合使用 - ElementCompositionPreview.SetElementChildVisual 方法
而我們要做的,是把整個構造過程倒過來,通過VisualTreeHelper,從DropShadow中拿到ShadowElement,然後獲取他的ChildVisual和Shadow,將ImplicitAnimations設置到Shadow上。
下麵貼代碼:

public static class DropShadowPanelHelper
{
    public static bool GetIsTransitionEnable(DropShadowPanel obj)
    {
        return (bool)obj.GetValue(IsTransitionEnableProperty);
    }

    public static void SetIsTransitionEnable(DropShadowPanel obj, bool value)
    {
        obj.SetValue(IsTransitionEnableProperty, value);
    }

    public static readonly DependencyProperty IsTransitionEnableProperty =
        DependencyProperty.RegisterAttached("IsTransitionEnable", typeof(bool), typeof(DropShadowPanelHelper), new PropertyMetadata(false, IsTransitionEnablePropertyChanged));


    private static void IsTransitionEnablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != e.OldValue)
        {
            if (d is DropShadowPanel sender)
            {
                //嘗試獲取ShadowElement,如果為空,可能是DropShadowPanel還沒有ApplyTemplate,註冊DropShadowPanel的Loaded事件,在Loaded中再獲取一次。
                var shadowElement = sender.FindDescendantByName("ShadowElement") as Border;
                if (shadowElement != null)
                {
                    SetImplicitAnimation(shadowElement, (bool)e.NewValue);
                }
                else
                {
                    sender.Loaded += DropShadowPanel_Loaded;
                }
            }
        }
    }

    private static void DropShadowPanel_Loaded(object sender, RoutedEventArgs e)
    {
        var dropShadowPanel = (DropShadowPanel)sender;
        dropShadowPanel.Loaded -= DropShadowPanel_Loaded;
        var shadowElement = dropShadowPanel.FindDescendantByName("ShadowElement") as Border;
        if (shadowElement != null)
        {
            SetImplicitAnimation(shadowElement, GetIsTransitionEnable(dropShadowPanel));
        }
    }

    private static void SetImplicitAnimation(FrameworkElement element, bool IsEnable)
    {
        if (ElementCompositionPreview.GetElementChildVisual(element) is SpriteVisual shadowVisual &&
            shadowVisual.Shadow is DropShadow shadow)
        {
            if (IsEnable)
            {
                //獲取合成器
                var compositor = shadowVisual.Compositor;
              
                //創建ImplicitAnimationCollection
                var imp = compositor.CreateImplicitAnimationCollection();

                //創建BlurRadius動畫,註意不要忘記設置Duration和Target
                var bluran = compositor.CreateScalarKeyFrameAnimation();
                //插入一個表達式關鍵幀,幀在進度為1的時候,值是最終值
                bluran.InsertExpressionKeyFrame(1f, "this.FinalValue");
                bluran.Duration = TimeSpan.FromSeconds(0.2d);
                bluran.Target = "BlurRadius";

                //創建Offset動畫
                var offsetan = compositor.CreateVector3KeyFrameAnimation();
                offsetan.InsertExpressionKeyFrame(1f, "this.FinalValue");
                offsetan.Duration = TimeSpan.FromSeconds(0.2d);
                offsetan.Target = "Offset";

                //創建Opacity動畫
                var opacityan = compositor.CreateScalarKeyFrameAnimation();
                opacityan.InsertExpressionKeyFrame(1f, "this.FinalValue");
                opacityan.Duration = TimeSpan.FromSeconds(0.2d);
                opacityan.Target = "Opacity";

                //ImplicitAnimationCollection是IDictionary,每個子項都要是KeyFrame動畫,子項的Key和動畫的Target要一樣。
                ImplictAnimationCollection
                imp[bluran.Target] = bluran;
                imp[offsetan.Target] = offsetan;
                imp[opacityan.Target] = opacityan;

                //給shadow設置ImplicitAnimations
                shadow.ImplicitAnimations = imp;
            }
            else
            {
                var imp = shadow.ImplicitAnimations;
                shadow.ImplicitAnimations = null;
                if (imp != null)
                {
                    imp.Dispose();
                    imp = null;
                }
            }
        }
    }

}

表達式關鍵幀的關鍵字相關的內容可以查閱:ExpressionAnimation Class - Expression Keywords
最後的Xaml是這樣的:

<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle">
    <Setter Property="Background" Value="#007acc" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderBrush" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Padding" Value="20,10,20,10" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontWeight" Value="Normal" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="UseSystemFocusVisuals" Value="True" />
    <Setter Property="FocusVisualMargin" Value="-3" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid x:Name="RootGrid">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="PointerOver">
                                <VisualState.Setters>
                                    <Setter Target="Shadow.OffsetY" Value="2" />
                                    <Setter Target="Shadow.BlurRadius" Value="8" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="OffsetY" To="3" Duration="0" />
                                    <DoubleAnimation Storyboard.TargetName="Shadow" Storyboard.TargetProperty="BlurRadius" To="12" Duration="0" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <control:DropShadowPanel x:Name="Shadow" 
                                             xmlns:control="using:Microsoft.Toolkit.Uwp.UI.Controls" 
                                             xmlns:helper="using:TestApp1.Helpers"
                                             HorizontalContentAlignment="Stretch" 
                                             helper:DropShadowPanelHelper.IsTransitionEnable="True"
                                             BlurRadius="5" OffsetX="1" OffsetY="1" Color="Black">
                        <Rectangle x:Name="Background" Fill="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RadiusX="5" RadiusY="5" />
                    </control:DropShadowPanel>
                    <ContentPresenter x:Name="ContentPresenter"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        Content="{TemplateBinding Content}"
                        ContentTransitions="{TemplateBinding ContentTransitions}"
                        ContentTemplate="{TemplateBinding ContentTemplate}"
                        Padding="{TemplateBinding Padding}"
                        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                        AutomationProperties.AccessibilityView="Raw" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

啟用方式就是


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

-Advertisement-
Play Games
更多相關文章
  • 示例: 什麼是對象  《JAVA編程思想》對於對象的定義是:將問題空間中的元素以及它們在方案空間的表示物稱作“對象”。  1. 問題空間:實際解決的問題模型;  2. 方案空間: 電腦(機器模型)。  實際的問題在電腦(機器模型)中的表示稱為對象。在上面示 ...
  • [TOC] Dubbo入門 Editor:SimpleWu Dubbo是 阿裡巴巴公司開源的一個高性能優秀的服務框架使得應用可通過高性能的 RPC 實現服務的輸出和輸入功能,可以和 Spring框架無縫集成。 背景 隨著互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架 ...
  • 經過上一篇教程的學習,我們知道對象將它的狀態存在域中。然而,Java中也使用了“變數”這個術語。在這一篇教程中,我們將會討論它們之間的關係,以及變數命名的規則和慣例,基本數據類型以及它們的預設值和字面量。 ...
  • JVM學習筆記1:Java虛擬機記憶體模型 學習JVM,Java虛擬機對理解Java程式執行過程和Java程式性能調優具有很大幫助。本系列博客旨在由淺到深學習並理解JVM。參考閱讀:《深入理解Java虛擬機 JVM高級特性和最佳實踐》。這個書寫的非常好,推薦有條件的讀者買一本來閱讀,網上也有電子版的。 ...
  • 在JDK1.8後,對HashMap源碼進行了更改,引入了紅黑樹。在這之前,HashMap實際上就是就是數組+鏈表的結構,由於HashMap是一張哈希表,其會產生哈希衝突,為瞭解決哈希衝突,HashMap採用了開鏈法,即對於用對象hashCode值計算哈希表數組下表時,當出現相同情況時,會在相同的地方 ...
  • 二叉樹中和為某一值的路徑: 輸入一顆二叉樹的跟節點和一個整數,列印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(註意: 在返回值的list中,數組長度大的數組靠前) 思路: 1.二叉樹的前序遍歷,中左右順序 2.把目標值target傳... ...
  • ORM-SqlRepoEx 是 .Net平臺下相容.NET Standard 2.0,一個實現以Lambda表達式轉轉換標準SQL語句,使用強類型操作數據的輕量級ORM工具,在減少魔法字串同時,通過靈活的Lambda表達式組合,實現業務數據查詢的多樣性。 ...
  • 不知不覺orleans就發佈到2.1版本的,但是說也奇怪orleans越是完善我發現園子相關的博客就越少,大概是大佬都在美滋滋用在生產環境,不屑於玩demo了吧。 但是小弟不才還是只會玩demo,所以只能簡單的介紹介紹2.1版本的新玩法了。 1.新建一個asp.net core的webapi項目,然 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...