Windows Phone爲開發者提供了不少原生控件,但在不少場景下咱們須要對默認的功能或樣式作必定的修改才能知足咱們的需求,自定義控件應運而生。本文經過以自定義控件進度環(ProgressRing)爲例,向你們介紹Windows Phone中如何建立和使用自定義控件。ide
一、控件基類動畫
一般自定義控件繼承自Control、ItemsControl、ContentControl等。spa
Control:表明使用ControlTemplate來定義樣式的UI控件的基類。ssr
System.Object
System.Windows.DependencyObject
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Control
ItemsControl:表明一個能夠用於表現一個集合對象的控件。code
System.Object
System.Windows.DependencyObject
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Control
System.Windows.Controls.ItemsControl
ContentControl:表明一個具備單獨塊級內容元素的控件。好比像Button,CheckBox,ScrollViewer都直接或間接的繼承於它。orm
System.Object
System.Windows.DependencyObject
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Control
System.Windows.Controls.ContentControl
二、建立自定義控件xml
下面咱們就來建立一個繼承自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"/> 的形式來設置背景色,由於Background是DependencyProperty屬性,可是你不能對通常的屬性進行一樣的操做,由於他們不是DependencyProperty屬性。blog
DefaultStyleKey表明默認樣式,若要爲繼承自 Control 的控件提供默認的 Style,請將 DefaultStyleKey 屬性設置爲相同類型的 TargetType 屬性。 若是您沒有設置 DefaultStyleKey,則將使用基類的默認樣式。 例如,若是稱爲 NewButton 的控件繼承自 Button,要使用新的默認 Style,請將 DefaultStyleKey 設置爲類型 NewButton。 若是您沒有設置 DefaultStyleKey,則會將 Style 用於 Button。 繼承
在一些複雜的場景中,好比你想要獲取ControlTemplate中某個對象的實例,就有必要override OnApplyTemplate方法。這個方法在控件展現在屏幕前被調用,在這種場景下OnApplyTemplate比Loaded事件更適合來調整由template建立的visual tree,由於Loaded事件可能會在頁面應用模板以前被調用,因此可能沒法獲取到ControlTemplate中的對象的實例,也就可能沒法完成以前調整模板的功能。
三、建立默認模板
接下來就該建立自定義控件的默認樣式。
<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>
這個步驟定義了咱們自定義控件的樣子,注意,ControlTemplate是很是重要的屬性,它是多種UI Element的組合,決定着咱們控件的visual tree的結構和狀態。
TemplateBinding用來鏈接模板中的屬性的值和自定義控件中屬性的值。TemplateBinding僅支持由模板產生的FrameworkElements,它的數據源引用會指向模板中的父級元素。TemplateBinding最主要的用途是內置在模板中綁定模板化元素的屬性。
四、使用自定義控件
首先在頁面引入自定義控件的命名空間。
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>