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

原文地址:https://www.cnblogs.com/kongxianghai/p/2593357.html