一、背景介紹
在上一篇《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
