• c#,使用WPF的Adorner实现iPhone上新邮件或消息提示效果实现(二)


    一、背景介绍

        在上一篇《c#,使用WPF的Adorner实现iPhone上新邮件或消息提示效果----实现(一)》中,我们通过PromptableButton,PromptAdorner,PromptChrome实现提示效果,其中PromptableButton提供PromptCount代表提示数量供PromptChrome绑定显示,PromptChrome负责显示提示效果,而PromptAdorner负责呈现PromptChrome。

        但是,这里有个问题,为了要使用PromptAdorner这个装饰件,难道所有的控件都要像PromptableButton控件一样提供PromptCount供装饰件绑定? 如果这样做,如果样装饰一个图片控件,那我们又要继承重写一个?如果直接要在常用控件上直接使用装饰件不就行不通了? 如果不能解决这3个问题,那么耦合度还是太高,通用性也不强。

        基于上面的问题,我们采用依赖属性中另外一个利器--“附加属性”,比如:我们要在Canvas内放置一个按钮并且指定其位置时我们都会写<Button Canvas.Left="100" Canvas.Top="100"/>,这里的Canvas.Left就是附加属性的实际使用。以此参考方法,我们也通过编写附件属性来改进目前的问题。

    二、改进分解说明

        1、改进PromptButton。

        该自定义按钮内不再拥有PromptCount属性,因为我们要把依赖属性挪挪地方了。

    View Code
        internal class PromptableButton : Button {
    
            public ImageSource CoverImageSource {
                get { return (ImageSource)GetValue(CoverImageSourceProperty); }
                set { SetValue(CoverImageSourceProperty, value); }
            }
    
            public static readonly DependencyProperty CoverImageSourceProperty =
                DependencyProperty.Register("CoverImageSource", typeof(ImageSource), typeof(PromptableButton), new UIPropertyMetadata(null));
    
    
            public PromptableButton() {
                
            }
            
            static PromptableButton() {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(PromptableButton), new FrameworkPropertyMetadata(typeof(PromptableButton)));
            }
        }

        2、改进PromptAdorner。

        我们需要两个依赖属性:IsPromptEnabled,PromptCount。

        IsPromptEnabled用来控制装饰件是否可用,PromptCount从PromptButton处挪过来。

        这两个依赖属性的定义时,使用DependencyProperty.RegisterAttached方法注册,说明这是附加属性,并且采用两个静态方法(Get/Set)作为附加属性的访问器。

    public static readonly DependencyProperty PromptCountProperty =
                DependencyProperty.RegisterAttached("PromptCount", typeof(int), typeof(PromptAdorner),
                new FrameworkPropertyMetadata(0, new PropertyChangedCallback(PromptCountChangedCallBack), new CoerceValueCallback(CoercePromptCountCallback)));
    
            public static int GetPromptCount(DependencyObject obj) {
                return (int)obj.GetValue(PromptCountProperty);
            }
    
            public static void SetPromptCount(DependencyObject obj, int value) {
                obj.SetValue(PromptCountProperty, value);
            }
    
    
            public static readonly DependencyProperty IsPromptEnabledProperty =
                DependencyProperty.RegisterAttached("IsPromptEnabled", typeof(bool), typeof(PromptAdorner),
                new FrameworkPropertyMetadata(false, new PropertyChangedCallback(IsPromptEnabledChangedCallBack), null));
    
            public static bool GetIsPromptEnabled(DependencyObject obj) {
                return (bool)obj.GetValue(IsPromptEnabledProperty);
            }
    
            public static void SetIsPromptEnabled(DependencyObject obj, bool value) {
                obj.SetValue(IsPromptEnabledProperty, value);
            }
    

        接下来我们在IsPromptEnabledChangedCallBack方法中实现装饰件的创建和移除,该方法会在IsPromptEnabled属性变化时调用:

            public static void IsPromptEnabledChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) {
                var source = d as FrameworkElement;
    
                bool isEnabled = (bool)e.NewValue;
                if (isEnabled) {
                    //装饰件可用,添加装饰件
    
                    AdornerLayer layer = AdornerLayer.GetAdornerLayer(source);
                    if (layer != null) {
                        //能够获取装饰层,说明已经load过了,直接生成装饰件
                        var adorner = new PromptAdorner(source);
                        layer.Add(adorner);
                    }
                    else {
                        //layer为null,说明还未load过(整个可视化树中没有装饰层的情况不考虑)
                        //在控件的loaded事件内生成装饰件
                        source.Loaded += (s1, e1) => {
                            var adorner = new PromptAdorner(source);
                            AdornerLayer.GetAdornerLayer(source).Add(adorner);
                        };
                    }
                }
                else {
                    //装饰件不可用,移除装饰件
                    AdornerLayer layer = AdornerLayer.GetAdornerLayer(source);
                    if (layer != null) {
                        Adorner[] AllAdorners = layer.GetAdorners(source);
                        if (AllAdorners != null) {
                            IEnumerable<Adorner> desAdorners = AllAdorners.Where(p => p is PromptAdorner);
                            if (desAdorners != null && desAdorners.Count() > 0) {
                                desAdorners.ToList().ForEach(p => layer.Remove(p));
                            }
                        }
                    }
                }
            }

        3、PromtpChrome的改进。

        主要修改该自定义控件描述皮肤的XAML中,对PromptCount的绑定写法。

        这里的绑定使用了附加属性绑定的写法,列如:

    <Label Content="{Binding Path=(local:PromptAdorner.PromptCount)}" .... />

        <Style TargetType="{x:Type local:PromptChrome}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:PromptChrome}">
                        <Grid x:Name="container">
                            <!--最外圈的白色圆框,并对其作阴影效果-->
                            <Ellipse Fill="White">
                                <Ellipse.Effect>
                                    <DropShadowEffect BlurRadius="6" 
                                                      ShadowDepth="6" 
                                                      Opacity="0.8"
                                                      Direction="270" 
                                                      RenderingBias="Performance"/>
                                </Ellipse.Effect>
                            </Ellipse>
                            
                            <!--内部的上半圆-->
                            <Ellipse Margin="3">
                                <Ellipse.Fill>
                                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                        <GradientStop Offset="0" Color="#FFF4AEB1"/>
                                        <GradientStop Offset="0.5" Color="#FFE3313A"/>
                                        <GradientStop Offset="1" Color="#FFE3313A"/>
                                    </LinearGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
    
                            <!--内部的下半圆,通过采用Exclude模式合并上下两个圆来完成-->
                            <Path  HorizontalAlignment="Center" VerticalAlignment="Center">
                                <Path.Data>
                                    <CombinedGeometry GeometryCombineMode="Exclude" >
                                        <CombinedGeometry.Geometry1>
                                            <EllipseGeometry Center="14 14"  RadiusX="14" RadiusY="14" />
                                        </CombinedGeometry.Geometry1>
                                        
                                        <CombinedGeometry.Geometry2>
                                            <EllipseGeometry Center="14 0"  RadiusX="18" RadiusY="14"/>
                                        </CombinedGeometry.Geometry2>
                                    </CombinedGeometry>
                                </Path.Data>
                                
                                <Path.Fill>
                                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                        <GradientStop Offset="0" Color="#FFDF151F"/>
                                        <GradientStop Offset="1" Color="#FFBA0004"/>
                                    </LinearGradientBrush>
                                </Path.Fill>
                            </Path>
                            
                            <Viewbox Stretch="Uniform" >
                                <!--绑定上文中的PromptCount属性-->
                                <Label Content="{Binding Path=(local:PromptAdorner.PromptCount)}" 
                                       x:Name="label"
                                       Foreground="White"
                                       FontWeight="Bold"
                                       FontSize="14"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"/>
                            </Viewbox>
                        </Grid>
                        
                        <ControlTemplate.Triggers>
                            <!--使用数据触发器,当PromptCount为0时,隐藏提示-->
                            <DataTrigger Binding="{Binding Path=(local:PromptAdorner.PromptCount)}" Value="0">
                                <Setter TargetName="container" Property="Visibility" Value="Hidden"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    三、使用

        在原先PromptableButton的XAML代码使用处,我们增加了两个依赖属性用来控制和显示装饰件。

    <local:PromptableButton x:Name="button1" Grid.Row="1" CoverImageSource="001.png" Width="128" Height="128" 
                                    local:PromptAdorner.IsPromptEnabled="True"
                                    local:PromptAdorner.PromptCount="0"/>

       我们也可以直接在一个常用控件上使用带提示效果的装饰件。

    <Button x:Name="button2" Grid.Row="4" Width="150" Height="60" Content="按钮2" 
                                   local:PromptAdorner.IsPromptEnabled="True"
     
                                   local:PromptAdorner.PromptCount
    ="1"//>

        至此,改进完成,改进后的装饰件已具备松耦合,通用性的能力。

        其余内容详见源代码。

    四、代码

    http://download.csdn.net/download/kongxh_1981/9161579

  • 相关阅读:
    [六、页面跳转]24实现对视图显示和消失事件的监听
    [七、项目实战]1创建一个非常漂亮的复合动画
    [七、项目实战]2实现一个从顶部滑入的吐司窗口
    [六、页面跳转]29通过路由在Swift UI中实现组件化开发
    [六、页面跳转]25使用Swift UI管理生命周期
    [六、页面跳转]26使用@UIApplicationDelegateAdaptor获得AppDelegate
    [七、项目实战]5给页面的内容添加动画效果
    [七、项目实战]3快速实现一个侧滑菜单
    [六、页面跳转]28在页面跳转时显示或隐藏底部的标签栏
    HTTP Host 头攻击,这是什么鬼?
  • 原文地址:https://www.cnblogs.com/kongxianghai/p/2593357.html
Copyright © 2020-2023  润新知