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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM