Export (0) Print
Expand All

How to: Create a New Control by Creating a ControlTemplate

Silverlight

The following example creates a custom control, NumericUpDown, that displays a numeric value that a user can increase or decrease by clicking the control's buttons. The example is described in detail in Creating a New Control by Creating a ControlTemplate.

Run this sample


<!--This is the contents of the themes/generic.xaml file.-->
<ResourceDictionary  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:src="clr-namespace:NumericUpDownCustomControl;assembly=NumericUpDownCustomControl"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    >
  <Style TargetType="src:NumericUpDown">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="src:NumericUpDown">
          <Grid  Margin="3" 
                Background="{TemplateBinding Background}">

            <vsm:VisualStateManager.VisualStateGroups>


              <vsm:VisualStateGroup x:Name="ValueStates">

                <!--Make the Value property red when it is negative.-->
                <vsm:VisualState x:Name="Negative">
                  <Storyboard>
                    <ColorAnimation To="Red"
                                    Storyboard.TargetName="TextBlock" 
                                    Storyboard.TargetProperty="(Foreground).(SolidBruch.Color)"/>
                  </Storyboard>

                </vsm:VisualState>

                <!--Return the control to its initial state by
                    return the TextBlock's Foreground to its 
                    original color.-->
                <vsm:VisualState x:Name="Positive"/>
              </vsm:VisualStateGroup>

              <vsm:VisualStateGroup x:Name="FocusStates">

                <!--Add a focus rectangle to highlight the entire control
                    when it has focus.-->
                <vsm:VisualState x:Name="Focused">
                  <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual" 
                                                   Storyboard.TargetProperty="Visibility" Duration="0">
                      <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                          <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                      </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                  </Storyboard>
                </vsm:VisualState>

                <!--Return the control to its initial state by
                    hiding the focus rectangle.-->
                <vsm:VisualState x:Name="Unfocused"/>
              </vsm:VisualStateGroup>

            </vsm:VisualStateManager.VisualStateGroups>

            <Grid>
              <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
              </Grid.RowDefinitions>
              <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
              </Grid.ColumnDefinitions>

              <Border BorderThickness="1" BorderBrush="Gray" 
                    Margin="7,2,2,2" Grid.RowSpan="2" 
                    Background="#E0FFFFFF"
                    VerticalAlignment="Center" 
                    HorizontalAlignment="Stretch">
                <TextBlock x:Name="TextBlock" TextAlignment="Center" Padding="5"
                           Foreground="{TemplateBinding Foreground}"/>

              </Border>

              <RepeatButton Content="Up" Margin="2,5,5,0"
                          x:Name="UpButton"
                          Grid.Column="1" Grid.Row="0"/>
              <RepeatButton Content="Down" Margin="2,0,5,5"
                          x:Name="DownButton"
                          Grid.Column="1" Grid.Row="1"/>

              <Rectangle Name="FocusVisual" Grid.ColumnSpan="2" Grid.RowSpan="2" 
                       Stroke="Black" StrokeThickness="1"  
                       Visibility="Collapsed"/>
            </Grid>

          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>



using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace NumericUpDownCustomControl
{
    [TemplatePart(Name = "TextElement", Type = typeof(TextBlock))]
    [TemplatePart(Name = "UpButtonElement", Type = typeof(RepeatButton))]
    [TemplatePart(Name = "DownButtonElement", Type = typeof(RepeatButton))]
    [TemplateVisualState(Name = "Positive", GroupName = "ValueStates")]
    [TemplateVisualState(Name = "Negative", GroupName = "ValueStates")]
    [TemplateVisualState(Name = "Focused", GroupName = "FocusedStates")]
    [TemplateVisualState(Name = "Unfocused", GroupName = "FocusedStates")]
    public class NumericUpDown : Control
    {
        public NumericUpDown()
        {
            DefaultStyleKey = typeof(NumericUpDown);
            this.IsTabStop = true;
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
                            new PropertyMetadata(new PropertyChangedCallback(ValueChangedCallback)));


        private static void ValueChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            NumericUpDown ctl = (NumericUpDown)obj;
            int newValue = (int)args.NewValue;

            // Update the TextElement to the new value.
            if (ctl.TextElement != null)
            {
                ctl.TextElement.Text = newValue.ToString();
            }

            // Call UpdateStates because the Value might have caused the
            // control to change ValueStates.
            ctl.UpdateStates(true);

            // Raise the ValueChanged event so applications can be alerted
            // when Value changes.
            ValueChangedEventArgs e = new ValueChangedEventArgs(newValue);
            ctl.OnValueChanged(e);
        }


        public event ValueChangedEventHandler ValueChanged;

        protected virtual void OnValueChanged(ValueChangedEventArgs e)
        {
            ValueChangedEventHandler handler = ValueChanged;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void UpdateStates(bool useTransitions)
        {
            if (Value >= 0)
            {
                VisualStateManager.GoToState(this, "Positive", useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, "Negative", useTransitions);
            }

            if (isFocused)
            {
                VisualStateManager.GoToState(this, "Focused", useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, "Unfocused", useTransitions);
            }

        }

        public int Value
        {
            get
            {
                return (int)GetValue(ValueProperty);
            }

            set
            {
                SetValue(ValueProperty, value);

            }
        }


        public override void OnApplyTemplate()
        {
            UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
            DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
            TextElement = GetTemplateChild("TextBlock") as TextBlock;

            UpdateStates(false);
        }

        private TextBlock textElement;

        private TextBlock TextElement
        {
            get { return textElement; }

            set
            {
                textElement = value;

                if (textElement != null)
                {
                    textElement.Text = Value.ToString();
                    textElement.IsHitTestVisible = true;
                }
            }

        }

        private RepeatButton downButtonElement;

        private RepeatButton DownButtonElement
        {
            get
            {
                return downButtonElement;
            }

            set
            {
                if (downButtonElement != null)
                {
                    downButtonElement.Click -=
                        new RoutedEventHandler(downButtonElement_Click);
                }
                downButtonElement = value;

                if (downButtonElement != null)
                {
                    downButtonElement.Click +=
                        new RoutedEventHandler(downButtonElement_Click);
                }
            }
        }

        void downButtonElement_Click(object sender, RoutedEventArgs e)
        {
            Value--;
        }

        private RepeatButton upButtonElement;

        private RepeatButton UpButtonElement
        {
            get
            {
                return upButtonElement;
            }

            set
            {
                if (upButtonElement != null)
                {
                    upButtonElement.Click -=
                        new RoutedEventHandler(upButtonElement_Click);
                }
                upButtonElement = value;

                if (upButtonElement != null)
                {
                    upButtonElement.Click +=
                        new RoutedEventHandler(upButtonElement_Click);
                }
            }
        }

        void upButtonElement_Click(object sender, RoutedEventArgs e)
        {
            Value++;
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            Focus();
        }

        bool isFocused;

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            isFocused = true;
            UpdateStates(true);
        }

        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            isFocused = false;
            UpdateStates(true);
        }

    }


    public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e);

    public class ValueChangedEventArgs : EventArgs
    {
        private int _value;

        public ValueChangedEventArgs(int num)
        {
            _value = num;
        }

        public int Value
        {
            get { return _value; }
        }
    }
}


Community Additions

ADD
Show:
© 2014 Microsoft