c#,使用WPF的Adorner實現iPhone上新郵件或消息提示效果----實現(一)


一、背景介紹

首先,讓我們看一下iPhone上的新郵件提示效果。

在郵件圖標的右上角會出現未讀的新郵件數量,蘋果的這種設計即簡潔又精致,而且相當的實用。

那么經典的效果當然要用我們的實際行動來膜拜!^_^

 

二、最終效果預覽

    在該篇文章的最后分享了代碼,^_^。

   

 

三、實現分解

    結構采用自定義按鈕+自定義裝飾件(Adorner)。

    裝飾件顧名思義就是用來作裝飾用的,好處就是:我們以前都是自己寫個控件然后在控件上繪制所有的效果,

而現在有了它,我們可以將一些效果獨立出來做成一種裝飾件,重用在其他想使用該效果的控件上,增強了

效果的解耦和重用。

 

1、自定義按鈕(PromptableButton)

   接下來我們編寫一個繼承自Button類的PromptableButton。

internal class PromptableButton : Button {
    //省略...
}

 

   該按鈕需要一個提示數量(PromptCount)屬性用於最終的右上角提示顯示用,做成依賴屬性是為了綁定給用作顯示的XAML代碼。

   CoercePromptCountCallback用於限制PromptCount不能小於0。

        public int PromptCount {
            get { return (int)GetValue(PromptCountProperty); }
            set { SetValue(PromptCountProperty, value); }
        }

        public static readonly DependencyProperty PromptCountProperty =
            DependencyProperty.Register("PromptCount", typeof(int), typeof(PromptableButton), 
            new FrameworkPropertyMetadata(0, new PropertyChangedCallback(PromptCountChangedCallBack),
new CoerceValueCallback(CoercePromptCountCallback))); private static object CoercePromptCountCallback(DependencyObject d, object value) { int promptCount = (int)value; promptCount = Math.Max(0, promptCount); return promptCount; }

 

    該按鈕還需要一個封面圖片,同樣做成依賴屬性綁定給界面。

        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));

 

    因為是自定義按鈕,我們需要為該按鈕提供一個皮膚,皮膚寫在Themes/Generic.xaml資源文件中。

    為了應用該皮膚,我們如此做:

       static PromptableButton() {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PromptableButton), new FrameworkPropertyMetadata(typeof(PromptableButton)));
        }

    

    接下來,看看皮膚的實現。

    其中的PART_CoverImage的Source綁定PromptableButton的CoverImageSource屬性,用來顯示封面圖片。

    下面的觸發器的作用是當按下按鈕后對按鈕作模糊效果。

    <Style TargetType="{x:Type local:PromptableButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:PromptableButton}">
                    <Grid>
                        <Image Name="PART_CoverImage" Stretch="Fill" Source="{Binding RelativeSource={RelativeSource TemplatedParent},Path=CoverImageSource}">
                            <Image.Effect>
                                <BlurEffect x:Name="effect" Radius="0"/>
                            </Image.Effect>
                        </Image>
                    </Grid>
                    
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsPressed" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                              Storyboard.TargetName="effect"
                                              Storyboard.TargetProperty="Radius"
                                              From="0" To="5" Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>

                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                          Storyboard.TargetName="effect"
                                          Storyboard.TargetProperty="Radius"
                                          From="5" To="0" Duration="0:0:0.2" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    自定義按鈕(PromptableButton)完成。

2、自定義裝飾件(PromptAdorner)

    我們一般都會在Adorner的OnRender方法里實現裝飾件的繪制,如果繪制的話需要監聽PromptableButton的PromptCount

屬性的變化,這樣作就顯得比較麻煩,依賴屬性本身就是用作綁定的最好的對象,為什們我們不能使用XAML來作繪制,並且綁定

PromptCount!

    為此,專門做一個負責繪制PromptCount的自定義控件(PromptChrome),為這個控件做一個皮膚,在此皮膚內綁定

PromptCount,並且將這個控件作為裝飾件(PromptAdorner)的子控件,說白了就是PromptAdorner負責裝飾,裝飾些什么東

西則交給PromptChrome來完成。

    首先,讓我們看看PromptAdorner的代碼。

    其目的就是管理子控件PromptChrome,並通過ArrangeOverride方法進行布局。

    注意其中的_chrome.DataContext = adornedElement;這句話使PromptableButton成為PromptChrome的上下文,

使PromptCount被PromptChrome界面上的元素綁定。

 

    internal class PromptAdorner : Adorner {

        protected override int VisualChildrenCount {
            get { return 1; }
        }

        public PromptAdorner(UIElement adornedElement)
            : base(adornedElement) {

            _chrome = new PromptChrome();
            _chrome.DataContext = adornedElement;
            this.AddVisualChild(_chrome);
        }

        protected override Visual GetVisualChild(int index) {
            return _chrome;
        }

        protected override Size ArrangeOverride(Size arrangeBounds) {
            _chrome.Arrange(new Rect(arrangeBounds));
            return arrangeBounds;
        }
        
        PromptChrome _chrome;
    }

 

    接下來,我們看一下PromptChrome的代碼。

    在靜態構造中應用皮膚。

    在ArrangeOverride布局方法中,將自己放置到右上角。

    internal class PromptChrome : Control {
        static PromptChrome() {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PromptChrome), new FrameworkPropertyMetadata(typeof(PromptChrome)));
        }     

        protected override Size ArrangeOverride(Size arrangeBounds) {

            this.Width = 34;
            this.Height = 34;

            this.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            this.VerticalAlignment = System.Windows.VerticalAlignment.Top;

            TranslateTransform tt = new TranslateTransform();
            tt.X = 10;
            tt.Y = -10;
            this.RenderTransform = tt;

            return base.ArrangeOverride(arrangeBounds);
        }
    }

 

     接下來是PromptChrome的皮膚。

     提示效果的繪制,綁定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=PromptCount}" 
                                   x:Name="label"
                                   Foreground="White"
                                   FontWeight="Bold"
                                   FontSize="14"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"/>
                        </Viewbox>
                    </Grid>
                    
                    <ControlTemplate.Triggers>
                        <!--使用數據觸發器,當PromptCount為0時,隱藏提示-->
                        <DataTrigger Binding="{Binding Path=PromptCount}" Value="0">
                            <Setter TargetName="container" Property="Visibility" Value="Hidden"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

 

四、代碼

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

 


免責聲明!

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



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