Windows Phone自定義控件 ProgressRing


  • 前言

    Windows Phone為開發者提供了很多原生控件,但在很多場景下我們需要對默認的功能或樣式做一定的修改才能滿足我們的需求,自定義控件應運而生。本文通過以自定義控件進度環(ProgressRing)為例,向大家介紹Windows Phone中如何創建和使用自定義控件。

     1、控件基類

     通常自定義控件繼承自ControlItemsControlContentControl等。

     Control:代表使用ControlTemplate來定義樣式的UI控件的基類。

System.Object
  System.Windows.DependencyObject
    System.Windows.UIElement
      System.Windows.FrameworkElement
        System.Windows.Controls.Control
View Code

     ItemsControl:代表一個可以用於表現一個集合對象的控件。

System.Object
  System.Windows.DependencyObject
    System.Windows.UIElement
      System.Windows.FrameworkElement
        System.Windows.Controls.Control
          System.Windows.Controls.ItemsControl
View Code

     ContentControl:代表一個具有單獨塊級內容元素的控件。比如像Button,CheckBox,ScrollViewer都直接或間接的繼承於它。

System.Object
  System.Windows.DependencyObject
    System.Windows.UIElement
      System.Windows.FrameworkElement
        System.Windows.Controls.Control
          System.Windows.Controls.ContentControl
View Code

     2、創建自定義控件

     下面我們就來創建一個繼承自Control的用戶控件ProgressRing的類。

namespace WindowsPhone.Controls
{
    public class ProgressRing : Control
    {
        public ProgressRing()
        {
            DefaultStyleKey = typeof(ProgressRing);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }

        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        public static readonly DependencyProperty IsActiveProperty =
            DependencyProperty.Register("IsActive", typeof(bool), typeof(ProgressRing), new PropertyMetadata(false, new PropertyChangedCallback(IsActiveChanged)));

        private static void IsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            var pr = (ProgressRing)d;
            var isActive = (bool)args.NewValue;
        }
    }
}

      通過DependencyProperty IsActiveProperty來代表進度環的狀態,DependencyProperty和普通的屬性的區別為,DependencyProperty屬性可以為值表達式、數據綁定、動畫和屬性更改通知提供支持。舉個例子,如果你聲明了一個Style,你可以通過 <Setter Property="Background" Value="Red"/> 的形式來設置背景色,因為BackgroundDependencyProperty屬性,但是你不能對一般的屬性進行同樣的操作,因為他們不是DependencyProperty屬性。

      DefaultStyleKey代表默認樣式,若要為繼承自 Control 的控件提供默認的 Style,請將 DefaultStyleKey 屬性設置為相同類型的 TargetType 屬性。 如果您沒有設置 DefaultStyleKey,則將使用基類的默認樣式。 例如,如果稱為 NewButton 的控件繼承自 Button,要使用新的默認 Style,請將 DefaultStyleKey 設置為類型 NewButton。 如果您沒有設置 DefaultStyleKey,則會將 Style 用於 Button

      在一些復雜的場景中,比如你想要獲取ControlTemplate中某個對象的實例,就有必要override OnApplyTemplate方法。這個方法在控件展示在屏幕前被調用,在這種場景下OnApplyTemplateLoaded事件更適合來調整由template創建的visual tree,因為Loaded事件可能會在頁面應用模板之前被調用,所以可能無法獲取到ControlTemplate中的對象的實例,也就可能無法完成之前調整模板的功能。

      3、創建默認模板

      接下來就該創建自定義控件的默認樣式。

            <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}" />
            <Setter Property="IsHitTestVisible" Value="False" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="MinHeight" Value="20" />
            <Setter Property="MinWidth" Value="20" />
            <Setter Property="IsTabStop" Value="False" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="controls:ProgressRing">
                        <Border x:Name="ProgressRingRoot" Background="{TemplateBinding Background}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    BorderBrush="{TemplateBinding BorderBrush}">
                            <Border.Resources>
                                <Style x:Key="ProgressRingEllipseStyle" TargetType="Ellipse">
                                    <Setter Property="Opacity" Value="0" />
                                    <Setter Property="HorizontalAlignment" Value="Left" />
                                    <Setter Property="VerticalAlignment" Value="Top" />
                                </Style>
                            </Border.Resources>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="SizeStates">
                                    <VisualState x:Name="Large">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Duration="0"
                                                Storyboard.TargetName="SixthCircle"
                                                Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Small" />
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ActiveStates">
                                    <VisualState x:Name="Inactive" />
                                    <VisualState x:Name="Active">
                                        <Storyboard RepeatBehavior="Forever">
                                            <ObjectAnimationUsingKeyFrames Duration="0"
                                                Storyboard.TargetName="Ring"
                                                Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E1"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="0">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E2"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="00:00:00.167">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E3"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="00:00:00.334">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E4"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="00:00:00.501">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E5"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="00:00:00.668">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E6"
                                                Storyboard.TargetProperty="Opacity"
                                                BeginTime="00:00:00.835">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                                <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E1R"
                                                BeginTime="0"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-110" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="10" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="93"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="205" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="357" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="439"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="585" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E2R"
                                                BeginTime="00:00:00.167"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-116" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="4" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="87"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="199" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="351" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="433"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="579" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E3R"
                                                BeginTime="00:00:00.334"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-122" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-2" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="81"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="193" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="345" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="427"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="573" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E4R"
                                                BeginTime="00:00:00.501"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-128" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-8" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="187" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="339" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="421"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="567" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E5R"
                                                BeginTime="00:00:00.668"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-134" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-14" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="69"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="181" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="331" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="415"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="561" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames
                                                Storyboard.TargetName="E6R"
                                                BeginTime="00:00:00.835"
                                                Storyboard.TargetProperty="Angle">
                                                <SplineDoubleKeyFrame KeyTime="0" Value="-140" KeySpline="0.13,0.21,0.1,0.7"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-20" KeySpline="0.02,0.33,0.38,0.77"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="63"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="175" KeySpline="0.57,0.17,0.95,0.75"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="325" KeySpline="0,0.19,0.07,0.72"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="409"/>
                                                <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="555" KeySpline="0,0,0.95,0.37"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid x:Name="Ring"
                                Margin="{TemplateBinding Padding}"
                                MaxWidth="{Binding TemplateSettings.MaxSideLength, RelativeSource={RelativeSource TemplatedParent}}"
                                MaxHeight="{Binding TemplateSettings.MaxSideLength, RelativeSource={RelativeSource TemplatedParent}}"
                                Visibility="Collapsed"
                                RenderTransformOrigin=".5,.5"
                                FlowDirection="LeftToRight">
                                <Canvas RenderTransformOrigin=".5,.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E1R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E1"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                                <Canvas RenderTransformOrigin=".5,.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E2R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E2"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                                <Canvas RenderTransformOrigin=".5,.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E3R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E3"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                                <Canvas RenderTransformOrigin=".5,.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E4R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E4"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                                <Canvas RenderTransformOrigin=".5,.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E5R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E5"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                                <Canvas RenderTransformOrigin=".5,.5"
                                    Visibility="Collapsed"
                                    x:Name="SixthCircle">
                                    <Canvas.RenderTransform>
                                        <RotateTransform x:Name="E6R" />
                                    </Canvas.RenderTransform>
                                    <Ellipse
                                        x:Name="E6"
                                        Style="{StaticResource ProgressRingEllipseStyle}"
                                        Width="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Height="{Binding TemplateSettings.EllipseDiameter, RelativeSource={RelativeSource TemplatedParent}}"
                                        Margin="{Binding TemplateSettings.EllipseOffset, RelativeSource={RelativeSource TemplatedParent}}"
                                        Fill="{TemplateBinding Foreground}"/>
                                </Canvas>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
View Code

      這個步驟定義了我們自定義控件的樣子,注意,ControlTemplate是非常重要的屬性,它是多種UI Element的組合,決定着我們控件的visual tree的結構和狀態。

      TemplateBinding用來連接模板中的屬性的值和自定義控件中屬性的值。TemplateBinding僅支持由模板產生的FrameworkElements,它的數據源引用會指向模板中的父級元素。TemplateBinding最主要的用途是內置在模板中綁定模板化元素的屬性。

       4、使用自定義控件

      首先在頁面引入自定義控件的命名空間。

xmlns:controls="clr-namespace:WindowsPhone.Controls"

      再在頁面添加自定義控件即可。

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <controls:ProgressRing Width="70" 
                          IsActive="True" Height="70"/>
</Grid>

代碼下載

 


免責聲明!

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



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