UWP Button添加圆角阴影(三)

日期 2018-10-10 默认分类,C#,UWP 作者 叫我蓝火火 共3评论

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>

启用方式就是中的xmlns:helper="using:TestApp1.Helpers" helper:DropShadowPanelHelper.IsTransitionEnable="True"
VisualState中有两种写法,第一种是写在Setter中,优点是写的少,缺点是不能控制起始时间。
第二种是写在Storyboard中,Duration一定要是0,因为真正的动画我们定义在ImplicitAnimation中了,这里的Animation只是用来触发值修改,不需要插值。
最终效果有点小掉帧,是DropShadow的问题,17763中的ThemeShadow应该会有改善。。。

已有 3 条评论
  1. Faster and Easier than the SBA, http://Business-Capital-Advisors.com can get your business a loan for $2K-350,000 With low-credit and no collateral.

    Use our fast form to Find Out exactly how much you can get, No-Cost:

    http://Business-Capital-Advisors.com

    If you've been established for at least 1 year you are already pre-qualified. Our Fast service means funding can be completed within 48hrs. Terms are specific for each business so I suggest applying to find out exactly how much you can get.

    This is a free service from a qualified lender and the approval will be based on the annual revenue of your business. Funds have no Restrictions, allowing you to use the whole amount in any way including bills, taxes, hiring, marketing, expansion, or Absolutely Any Other expense.

    There are limited SBA and private funds available so please apply now if interested,

    Click Here: http://Business-Capital-Advisors.com

    Have a great day,
    The Business Capital Advisors Team

    unsubscribe here - http://business-capital-advisors.com/r.php?url=blog.ultrabluefire.cn&id=e163

  2. 我心依旧 我心依旧

    好!

如果有错误请告诉我,谢谢各位大佬