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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...