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://files.cnblogs.com/kongxianghai/Test008.rar

 

你可能感兴趣的:(iPhone)