Erstellen eines Steuerelements mit einer anpassbaren DarstellungCreating a Control That Has a Customizable Appearance

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) bietet Ihnen die Möglichkeit zum Erstellen eines Steuerelements, dessen Darstellung angepasst werden kann.Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) gives you the ability to create a control whose appearance can be customized. Sie können z. B. Ändern der Darstellung von einem CheckBox über welche Einstellung tun, als Eigenschaften werden durch Erstellen eines neuen ControlTemplate.For example, you can change the appearance of a CheckBox beyond what setting properties will do by creating a new ControlTemplate. Die folgende Abbildung zeigt eine CheckBox , verwendet den Standardwert ControlTemplate und CheckBox , verwendet eine benutzerdefinierte ControlTemplate.The following illustration shows a CheckBox that uses a default ControlTemplate and a CheckBox that uses a custom ControlTemplate.

Kontrollkästchen mit der Standardsteuerelementvorlage. ein Kontrollkästchen, das die Standardsteuerelementvorlage verwendetA checkbox with the default control template. A CheckBox that uses the default control template

Kontrollkästchen mit einer benutzerdefinierten Steuerelementvorlage. ein Kontrollkästchen, das eine benutzerdefinierte Steuerelementvorlage verwendetA checkbox with a custom control template. A CheckBox that uses a custom control template

Befolgen Sie das Modell Teile und Zustände, wenn Sie ein Steuerelement erstellen, wird die Darstellung des Steuerelements angepasst werden.If you follow the parts and states model when you create a control, your control's appearance will be customizable. Designer-Tools wie Microsoft Expression Blend unterstützt das Modell Teile und Zustände aus, damit bei diesem Modell folgen, können Sie das Steuerelement kann in diese Arten von Anwendungen angepasst werden.Designer tools such as Microsoft Expression Blend support the parts and states model, so when you follow this model your control will be customizable in those types of applications. In diesem Thema wird erläutert, das Modell Teile und Zustände und es führen bei der Erstellung eigener Steuerelemente.This topic discusses the parts and states model and how to follow it when you create your own control. In diesem Thema wird ein Beispiel für ein benutzerdefiniertes Steuerelement NumericUpDown, um die Philosophie dieses Modells zu veranschaulichen.This topic uses an example of a custom control, NumericUpDown, to illustrate the philosophy of this model. Die NumericUpDown Steuerelement zeigt einen numerischen Wert, der ein Benutzer kann zu erhöhen oder verringern, indem Sie durch Klicken auf die Schaltflächen des Steuerelements.The NumericUpDown control displays a numeric value, which a user can increase or decrease by clicking on the control's buttons. Die folgende Abbildung zeigt die NumericUpDown -Steuerelement, das in diesem Thema erläutert wird.The following illustration shows the NumericUpDown control that is discussed in this topic.

Benutzerdefiniertes NumericUpDown-Steuerelement. ein benutzerdefiniertes NumericUpDown-SteuerelementNumericUpDown custom control. A custom NumericUpDown control

Dieses Thema enthält folgende Abschnitte:This topic contains the following sections:

VorraussetzungenPrerequisites

In diesem Thema wird davon ausgegangen, dass Sie wissen, wie zum Erstellen eines neuen ControlTemplate für ein vorhandenes Steuerelement an, wie die Elemente in einem Steuerelementvertrag sind, vertraut sind und Verstehen der Konzepte, die in beschriebenen Anpassen der Darstellung eines vorhandenen Steuerelements durch Erstellen einer ControlTemplate.This topic assumes that you know how to create a new ControlTemplate for an existing control, are familiar with what the elements on a control contract are, and understand the concepts discussed in Customizing the Appearance of an Existing Control by Creating a ControlTemplate.

Hinweis

Um ein Steuerelement zu erstellen, die dessen Darstellung angepasst werden können, müssen Sie ein Steuerelement, das von erbt erstellen die Control Klasse oder eine ihrer Unterklassen außer UserControl.To create a control that can have its appearance customized, you must create a control that inherits from the Control class or one of its subclasses other than UserControl. Ein Steuerelement, das von erbt UserControl ist ein Steuerelement, das schnell erstellt werden kann, aber es verwendet keine ControlTemplate und seine Darstellung nicht anpassen.A control that inherits from UserControl is a control that can be quickly created, but it does not use a ControlTemplate and you cannot customize its appearance.

Teile und Zustände-ModellParts and States Model

Das Modell Teile und Zustände gibt an, wie die visuelle Struktur und das visuelle Verhalten eines Steuerelements zu definieren.The parts and states model specifies how to define the visual structure and visual behavior of a control. Um das Modell Teile und Zustände zu folgen, sollten Sie Folgendes tun:To follow the parts and states model, you should do the following:

  • Definieren Sie die visuelle Struktur und das visuelle Verhalten in der ControlTemplate eines Steuerelements.Define the visual structure and visual behavior in the ControlTemplate of a control.

  • Führen Sie die bestimmte best Practices, wenn die Logik des Steuerelements mit Teilen der Steuerelementvorlage interagiert.Follow certain best practices when your control's logic interacts with parts of the control template.

  • Geben Sie einen Steuerelementvertrag angeben, was in enthalten sein sollen die ControlTemplate.Provide a control contract to specify what should be included in the ControlTemplate.

Wenn Sie definieren die visuelle Struktur und das visuelle Verhalten in der ControlTemplate eines Steuerelements, Anwendungsentwickler können ändern, die visuelle Struktur und das visuelle Verhalten des Steuerelements durch Erstellen eines neuen ControlTemplate statt Code zu schreiben.When you define the visual structure and visual behavior in the ControlTemplate of a control, application authors can change the visual structure and visual behavior of your control by creating a new ControlTemplate instead of writing code. Müssen Sie angeben, ein Steuerelementvertrag, der Anwendung weist Autoren die FrameworkElement Objekte und Zustände in definiert werden die ControlTemplate.You must provide a control contract that tells application authors which FrameworkElement objects and states should be defined in the ControlTemplate. Befolgen Sie einige bewährten Methoden beim Interagieren mit den Teilen in die ControlTemplate , damit das Steuerelement ordnungsgemäß ein unvollständiges behandelt ControlTemplate.You should follow some best practices when you interact with the parts in the ControlTemplate so that your control properly handles an incomplete ControlTemplate. Wenn Sie diese drei Grundsätzen folgen, Anwendungsentwickler werden zum Erstellen einer ControlTemplate für Ihr Steuerelement ebenso leicht wie können für die Steuerelemente, die im Lieferumfang WPFWPF.If you follow these three principles, application authors will be able to create a ControlTemplate for your control just as easily as they can for the controls that ship with WPFWPF. Im folgende Abschnitt werden alle diese Empfehlungen im Detail beschrieben.The following section explains each of these recommendations in detail.

Definieren die visuelle Struktur und das visuelle Verhalten eines Steuerelements in einer ControlTemplateDefining the Visual Structure and Visual Behavior of a Control in a ControlTemplate

Wenn Sie Ihr benutzerdefinierte Steuerelement erstellen, mit dem Modell Teile und Zustände, definieren Sie visuelle Struktur des Steuerelements und das visuelle Verhalten in seiner ControlTemplate anstelle von in ihrer Logik.When you create your custom control by using the parts and states model, you define the control's visual structure and visual behavior in its ControlTemplate instead of in its logic. Die visuelle Struktur eines Steuerelements ist die Zusammensetzung der FrameworkElement Objekte, die das Steuerelement bilden.The visual structure of a control is the composite of FrameworkElement objects that make up the control. Das visuelle Verhalten ist die Möglichkeit, die das Steuerelement, das angezeigt wird, wenn es in einem bestimmten Zustand befindet.The visual behavior is the way the control appears when it is in a certain state. Weitere Informationen zum Erstellen einer ControlTemplate die visuelle Struktur und das visuelle Verhalten eines Steuerelements, der angibt, finden Sie unter Anpassen der Darstellung eines vorhandenen Steuerelements durch Erstellen einer ControlTemplate.For more information about creating a ControlTemplate that specifies the visual structure and visual behavior of a control, see Customizing the Appearance of an Existing Control by Creating a ControlTemplate.

Im Beispiel für die NumericUpDown -Steuerelement, die visuelle Struktur enthält zwei RepeatButton Steuerelemente und ein TextBlock.In the example of the NumericUpDown control, the visual structure includes two RepeatButton controls and a TextBlock. Wenn Sie diese Steuerelemente im Code hinzufügen, die NumericUpDown Steuerelement – in seinem Konstruktor, z. B. – die Positionen der diese Steuerelemente unveränderbar sein würde.If you add these controls in the code of the NumericUpDown control--in its constructor, for example--the positions of those controls would be unalterable. Anstatt die visuelle Struktur und visuelle Verhalten des Steuerelements in seinem Code, definieren Sie ihn in das ControlTemplate.Instead of defining the control's visual structure and visual behavior in its code, you should define it in the ControlTemplate. Klicken Sie dann ein Anwendungsentwickler, um die Position der Schaltflächen anzupassen und TextBlock , und geben Sie das Verhalten tritt auf, wenn Value negativ ist da die ControlTemplate ersetzt werden.Then an application developer to customize the position of the buttons and TextBlock and specify what behavior occurs when Value is negative because the ControlTemplate can be replaced.

Das folgende Beispiel zeigt die visuelle Struktur des der NumericUpDown -Steuerelement, das enthält eine RepeatButton erhöhen Value, RepeatButton verringern Value, und ein TextBlock anzuzeigende Value.The following example shows the visual structure of the NumericUpDown control, which includes a RepeatButton to increase Value, a RepeatButton to decrease Value, and a TextBlock to display Value.

<ControlTemplate TargetType="src:NumericUpDown">
  <Grid  Margin="3" 
         Background="{TemplateBinding Background}">
    <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">

        <!--Bind the TextBlock to the Value property-->
        <TextBlock Name="TextBlock"
                   Width="60" TextAlignment="Right" Padding="5"
                   Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                     AncestorType={x:Type src:NumericUpDown}}, 
                     Path=Value}"/>
      </Border>

      <RepeatButton Content="Up" Margin="2,5,5,0"
        Name="UpButton"
        Grid.Column="1" Grid.Row="0"/>
      <RepeatButton Content="Down" Margin="2,0,5,5"
        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>

Das visuelle Verhalten von der NumericUpDown Steuerelement ist, dass der Wert in roter Schrift ist, wenn sie negativ ist.A visual behavior of the NumericUpDown control is that the value is in a red font if it is negative. Wenn Sie ändern die Foreground von der TextBlock in code die Value negativ ist, die NumericUpDown zeigen daraufhin immer einen roten negativen Wert.If you change the Foreground of the TextBlock in code when the Value is negative, the NumericUpDown will always show a red negative value. Geben Sie das visuelle Verhalten des Steuerelements in der ControlTemplate durch Hinzufügen von VisualState Objekte die ControlTemplate.You specify the visual behavior of the control in the ControlTemplate by adding VisualState objects to the ControlTemplate. Das folgende Beispiel zeigt die VisualState von Objekten für die Positive und Negative Zustände.The following example shows the VisualState objects for the Positive and Negative states. Positive und Negative sind sich gegenseitig ausschließende (das Steuerelement ist stets in genau einem der beiden), damit das Beispiel setzt die VisualState Objekte in einem einzelnen VisualStateGroup.Positive and Negative are mutually exclusive (the control is always in exactly one of the two), so the example puts the VisualState objects into a single VisualStateGroup. Wenn das Steuerelement wechselt in die Negative Zustand der Foreground von der TextBlock Rot.When the control goes into the Negative state, the Foreground of the TextBlock turns red. Wenn das Steuerelement ist der Positive Zustand der Foreground gibt, die auf den ursprünglichen Wert zurück.When the control is in the Positive state, the Foreground returns to its original value. Definieren von VisualState Objekte in einem ControlTemplate Weitere Informationen finden Sie unter Anpassen der Darstellung eines vorhandenen Steuerelements durch Erstellen einer ControlTemplate.Defining VisualState objects in a ControlTemplate is further discussed in Customizing the Appearance of an Existing Control by Creating a ControlTemplate.

Hinweis

Werden Sie sicher, dass die VisualStateGroups angefügte Eigenschaft im Stammelement FrameworkElement von der ControlTemplate.Be sure to set the VisualStateGroups attached property on the root FrameworkElement of the ControlTemplate.

<ControlTemplate TargetType="local:NumericUpDown">
  <Grid  Margin="3" 
         Background="{TemplateBinding Background}">

    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="ValueStates">

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

        </VisualState>

        <!--Return the TextBlock's Foreground to its 
            original color.-->
        <VisualState Name="Positive"/>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
  </Grid>
</ControlTemplate>

Verwendung von Teilen der ControlTemplate in CodeUsing Parts of the ControlTemplate in Code

Ein ControlTemplate Autor kann weglassen FrameworkElement oder VisualState Objekte, die absichtlich oder versehentlich, aber die Logik des Steuerelements möglicherweise die Teile ordnungsgemäß funktioniert.A ControlTemplate author might omit FrameworkElement or VisualState objects, either purposefully or by mistake, but your control's logic might need those parts to function properly. Teile und Zustände Modell gibt an, dass das Steuerelement gegen sollte eine ControlTemplate , die fehlt FrameworkElement oder VisualState Objekte.The parts and states model specifies that your control should be resilient to a ControlTemplate that is missing FrameworkElement or VisualState objects. Das Steuerelement sollte keine Ausnahme oder ein Bericht einen Fehler auslösen Wenn eine FrameworkElement, VisualState, oder VisualStateGroup fehlt in der ControlTemplate.Your control should not throw an exception or report an error if a FrameworkElement, VisualState, or VisualStateGroup is missing from the ControlTemplate. In diesem Abschnitt wird beschrieben, die empfohlenen Methoden für die Interaktion mit FrameworkElement Objekte und Verwalten von Zuständen.This section describes the recommended practices for interacting with FrameworkElement objects and managing states.

Erwarten Sie fehlende FrameworkElement-ObjekteAnticipate Missing FrameworkElement Objects

Beim Definieren von FrameworkElement Objekte in der ControlTemplate, die Logik des Steuerelements muss möglicherweise mit einigen von ihnen zu interagieren.When you define FrameworkElement objects in the ControlTemplate, your control's logic might need to interact with some of them. Z. B. die NumericUpDown Steuerelement abonniert der Schaltflächen Click Ereignis vergrößern oder verkleinern Value und legt sie fest der Text Eigenschaft der TextBlock zu Value.For example, the NumericUpDown control subscribes to the buttons' Click event to increase or decrease Value and sets the Text property of the TextBlock to Value. Wenn eine benutzerdefinierte ControlTemplate lässt die TextBlock oder Schaltflächen, es ist akzeptabel, dass das Steuerelement verloren, einen Teil der Funktionen hat, aber Sie achten darauf sollten, dass das Steuerelement keinen Fehler verursacht.If a custom ControlTemplate omits the TextBlock or buttons, it is acceptable that the control loses some of its functionality, but you should be sure that your control does not cause an error. Z. B. wenn ein ControlTemplate enthält nicht die Schaltflächen zum Ändern Value, NumericUpDown verliert, Funktionalität, aber eine Anwendung, verwendet der ControlTemplate wird weiterhin ausgeführt.For example, if a ControlTemplate does not contain the buttons to change Value, the NumericUpDown loses that functionality, but an application that uses the ControlTemplate will continue to run.

Die folgenden Methoden werden sichergestellt, dass das Steuerelement ordnungsgemäß mit fehlendem reagiert FrameworkElement Objekte:The following practices will ensure that your control responds properly to missing FrameworkElement objects:

  1. Legen Sie die x:Name Attribut für die einzelnen FrameworkElement , die Sie im Code verweisen müssen.Set the x:Name attribute for each FrameworkElement that you need to reference in code.

  2. Definieren Sie für jede private Eigenschaften FrameworkElement , die Sie benötigen für die Interaktion mit.Define private properties for each FrameworkElement that you need to interact with.

  3. Abonnieren und abbestellen von Ereignisse, die das Steuerelement behandelt den FrameworkElement Eigenschaft des set-Accessor.Subscribe to and unsubscribe from any events that your control handles in the FrameworkElement property's set accessor.

  4. Legen Sie die FrameworkElement Eigenschaften, die Sie definiert haben, in Schritt 2 in der OnApplyTemplate Methode.Set the FrameworkElement properties that you defined in step 2 in the OnApplyTemplate method. Dies ist der früheste Zeitpunkt, die die FrameworkElement in die ControlTemplate für das Steuerelement verfügbar ist.This is the earliest that the FrameworkElement in the ControlTemplate is available to the control. Verwenden der x:Name von der FrameworkElement zum Abrufen von der ControlTemplate.Use the x:Name of the FrameworkElement to get it from the ControlTemplate.

  5. Überprüfen Sie, ob die FrameworkElement nicht null vor dem Zugriff auf ihre Mitglieder.Check that the FrameworkElement is not null before accessing its members. Ist dies null, keinen Fehler melden.If it is null, do not report an error.

Die folgenden Beispiele zeigen die NumericUpDown Steuerelement interagiert mit FrameworkElement Objekte gemäß den Empfehlungen in der vorangehenden Liste aufgeführt.The following examples show how the NumericUpDown control interacts with FrameworkElement objects in accordance with the recommendations in the preceding list.

Im Beispiel, das die visuelle Struktur des definiert die NumericUpDown steuern, der ControlTemplate, RepeatButton erhöht sich dadurch Value hat seine x:Name -Attributsatz auf UpButton.In the example that defines the visual structure of the NumericUpDown control in the ControlTemplate, the RepeatButton that increases Value has its x:Name attribute set to UpButton. Das folgende Beispiel deklariert eine Eigenschaft namens UpButtonElement darstellt, die die RepeatButton , deklariert ist, der ControlTemplate.The following example declares a property called UpButtonElement that represents the RepeatButton that is declared in the ControlTemplate. Die set Accessor zuerst kündigt das Abonnement auf der Schaltfläche Click Ereignis wenn UpDownElement nicht null, dann legt die Eigenschaft fest, und klicken Sie dann abonniert die Click Ereignis.The set accessor first unsubscribes to the button's Click event if UpDownElement is not null, then it sets the property, and then it subscribes to the Click event. Es gibt auch eine Eigenschaft definiert, aber hier nicht angezeigt, für die anderen RepeatButtonnamens DownButtonElement.There is also a property defined, but not shown here, for the other RepeatButton, called DownButtonElement.

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);
        }
    }
}
Private m_upButtonElement As RepeatButton

Private Property UpButtonElement() As RepeatButton
    Get
        Return m_upButtonElement
    End Get

    Set(ByVal value As RepeatButton)
        If m_upButtonElement IsNot Nothing Then
            RemoveHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
        End If
        m_upButtonElement = value

        If m_upButtonElement IsNot Nothing Then
            AddHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
        End If
    End Set
End Property

Das folgende Beispiel zeigt die OnApplyTemplate für die NumericUpDown Steuerelement.The following example shows the OnApplyTemplate for the NumericUpDown control. Im Beispiel wird die GetTemplateChild -Methode zum Abrufen der FrameworkElement Objekte aus der ControlTemplate.The example uses the GetTemplateChild method to get the FrameworkElement objects from the ControlTemplate. Beachten Sie, die im Beispiel für schützt Fälle, in denen GetTemplateChild sucht nach einem FrameworkElement mit dem angegebenen Namen, die nicht den erwarteten Typ ist.Notice that the example guards against cases where GetTemplateChild finds a FrameworkElement with the specified name that is not of the expected type. Es ist auch eine bewährte Methode, die Elemente zu ignorieren, die dem angegebenen x:Name jedoch den falschen Typ.It is also a best practice to ignore elements that have the specified x:Name but are of the wrong type.

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

    UpdateStates(false);
}
Public Overloads Overrides Sub OnApplyTemplate()

    UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
    DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)

    UpdateStates(False)
End Sub

Befolgen Sie die Methoden, die in den vorherigen Beispielen angezeigt werden, Sie stellen Sie sicher, dass das Steuerelement weiter ausgeführt werden, wenn die ControlTemplate fehlt eine FrameworkElement.By following the practices that are shown in the previous examples, you ensure that your control will continue to run when the ControlTemplate is missing a FrameworkElement.

Verwenden der VisualStateManager Zustände verwaltenUse the VisualStateManager to Manage States

Die VisualStateManager verfolgt den Zuständen eines Steuerelements und führt die erforderliche Logik zum Übergang zwischen Zuständen.The VisualStateManager keeps track of the states of a control and performs the logic necessary to transition between states. Beim Hinzufügen VisualState Objekte die ControlTemplate, fügen Sie die Objekte einer VisualStateGroup und Hinzufügen der VisualStateGroup auf die VisualStateGroups angefügte Eigenschaft, damit die VisualStateManager hat Zugriff auf diese.When you add VisualState objects to the ControlTemplate, you add them to a VisualStateGroup and add the VisualStateGroup to the VisualStateGroups attached property so that the VisualStateManager has access to them.

Im folgende Beispiel wird im vorherige Beispiel, das zeigt, wiederholt der VisualState Objekte, die entsprechen, den Positive und Negative Zustände des Steuerelements.The following example repeats the previous example that shows the VisualState objects that correspond to the Positive and Negative states of the control. Die Storyboard in die Negative VisualState aktiviert die Foreground von der TextBlock Rot.The Storyboard in the NegativeVisualState turns the Foreground of the TextBlock red. Wenn die NumericUpDown Steuerelement befindet sich in der Negative -Status befindet, handelt es sich bei das Storyboard in der Negative Zustand beginnt.When the NumericUpDown control is in the Negative state, the storyboard in the Negative state begins. Die Storyboard in die Negative Zustand beendet wird, wenn das Steuerelement zurückgegeben der Positive Zustand.Then the Storyboard in the Negative state stops when the control returns to the Positive state. Die Positive VisualState muss nicht enthalten eine Storyboard da bei der Storyboard für die Negative beendet wird, die Foreground an seine ursprüngliche Farbe zurückgegeben.The PositiveVisualState does not need to contain a Storyboard because when the Storyboard for the Negative stops, the Foreground returns to its original color.

<ControlTemplate TargetType="local:NumericUpDown">
  <Grid  Margin="3" 
         Background="{TemplateBinding Background}">

    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="ValueStates">

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

        </VisualState>

        <!--Return the TextBlock's Foreground to its 
            original color.-->
        <VisualState Name="Positive"/>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
  </Grid>
</ControlTemplate>

Beachten Sie, dass die TextBlock erhält einen Namen, aber die TextBlock befindet sich nicht in den Steuerelementvertrag für NumericUpDown , da die Logik des Steuerelements niemals verweist auf die TextBlock.Note that the TextBlock is given a name, but the TextBlock is not in the control contract for NumericUpDown because the control's logic never references the TextBlock. Elemente, auf die verweist, die ControlTemplate Namen haben, müssen jedoch nicht Teil des Steuerelementvertrags sein, da eine neue ControlTemplate für das Steuerelement möglicherweise nicht auf dieses Element verweisen muss.Elements that are referenced in the ControlTemplate have names, but do not need to be part of the control contract because a new ControlTemplate for the control might not need to reference that element. Z. B. eine Person, ein neues erstellt ControlTemplate für NumericUpDown ggf. nicht angeben, dass Value negativ ist, durch Ändern der Foreground.For example, someone who creates a new ControlTemplate for NumericUpDown might decide to not indicate that Value is negative by changing the Foreground. In diesem Fall weder den Code noch die ControlTemplate Verweise der TextBlock anhand des Namens.In that case, neither the code nor the ControlTemplate references the TextBlock by name.

Die Logik des Steuerelements ist verantwortlich für den Zustand des Steuerelements zu ändern.The control's logic is responsible for changing the control's state. Das folgende Beispiel zeigt, dass die NumericUpDown kontrollaufrufe der GoToState Methode näher betrachten die Positive Zustand über, wenn Value ist 0 oder größer ist, und die Negative Zustand über, wenn Value ist kleiner als 0.The following example shows that the NumericUpDown control calls the GoToState method to go into the Positive state when Value is 0 or greater, and the Negative state when Value is less than 0.

if (Value >= 0)
{
    VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
    VisualStateManager.GoToState(this, "Negative", useTransitions);
}
If Value >= 0 Then
    VisualStateManager.GoToState(Me, "Positive", useTransitions)
Else
    VisualStateManager.GoToState(Me, "Negative", useTransitions)
End If

Die GoToState Methode führt die erforderliche Logik zum Starten und Beenden des Storyboards.The GoToState method performs the logic necessary to start and stop the storyboards appropriately. Wenn ein Steuerelement ruft GoToState so ändern Sie seinen Status, der die VisualStateManager bewirkt Folgendes:When a control calls GoToState to change its state, the VisualStateManager does the following:

  • Wenn die VisualState hat, dass das Steuerelement, wird eine Storyboard, beginnt das Storyboard.If the VisualState that the control is going to has a Storyboard, the storyboard begins. Wenn danach die VisualState , das Steuerelement stammt verfügt über eine Storyboard, das Storyboard beendet.Then, if the VisualState that the control is coming from has a Storyboard, the storyboard ends.

  • Wenn das Steuerelement bereits im Zustand, die angegeben wird ist, GoToState führt keine Aktion aus und gibt true.If the control is already in the state that is specified, GoToState takes no action and returns true.

  • Wenn angegebene Zustand im nicht vorhanden. die ControlTemplate von control, GoToState führt keine Aktion aus und gibt false.If state that is specified doesn't exist in the ControlTemplate of control, GoToState takes no action and returns false.

Bewährte Methoden für die Arbeit mit der VisualStateManagerBest Practices for Working with the VisualStateManager

Es wird empfohlen, dass Sie Folgendes ein, um die Verwaltung der Zustände Ihres Steuerelements ausführen:It is recommended that you do the following to maintain your control's states:

  • Verwenden Sie Eigenschaften, um seinen Status zu nachzuverfolgen.Use properties to track its state.

  • Erstellen Sie eine Hilfsmethode zum Wechseln zwischen Zuständen.Create a helper method to transition between states.

Die NumericUpDown -Steuerelement verwendet die Value Eigenschaft, um nachzuverfolgen, ob es in der Positive oder Negative Zustand.The NumericUpDown control uses its Value property to track whether it is in the Positive or Negative state. Die NumericUpDown Steuerelement definiert auch die Focused und UnFocused angibt, welche verfolgt die IsFocused Eigenschaft.The NumericUpDown control also defines the Focused and UnFocused states, which tracks the IsFocused property. Wenn Sie Zustände, die nicht auf natürliche Weise an eine Eigenschaft des Steuerelements entsprechen verwenden, können Sie eine private Eigenschaft zum Nachverfolgen des Status definieren.If you use states that do not naturally correspond to a property of the control, you can define a private property to track the state.

Eine einzelne Methode, die alle Status aktualisiert zentralisiert die Aufrufe an die VisualStateManager und hält Sie Ihren Code überschaubar.A single method that updates all the states centralizes calls to the VisualStateManager and keeps your code manageable. Das folgende Beispiel zeigt die NumericUpDown Hilfsprogramm-Methode des Steuerelements UpdateStates.The following example shows the NumericUpDown control's helper method, UpdateStates. Wenn Value ist größer als oder gleich 0 (null) der Control befindet sich in der Positive Zustand.When Value is greater than or equal to 0, the Control is in the Positive state. Wenn Value ist kleiner als 0 ist, das Steuerelement ist der Negative Zustand.When Value is less than 0, the control is in the Negative state. Bei IsFocused ist true, das Steuerelement befindet sich in der Focused Status; andernfalls ist es der Unfocused Zustand.When IsFocused is true, the control is in the Focused state; otherwise, it is in the Unfocused state. Rufen Sie das Steuerelement kann UpdateStates muss jedes Mal, wenn ändert seinen Status, unabhängig davon, welcher Zustand sich ändert.The control can call UpdateStates whenever it needs to change its state, regardless of what state changes.

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);
    }
}
Private Sub UpdateStates(ByVal useTransitions As Boolean)

    If Value >= 0 Then
        VisualStateManager.GoToState(Me, "Positive", useTransitions)
    Else
        VisualStateManager.GoToState(Me, "Negative", useTransitions)
    End If

    If IsFocused Then
        VisualStateManager.GoToState(Me, "Focused", useTransitions)
    Else
        VisualStateManager.GoToState(Me, "Unfocused", useTransitions)

    End If
End Sub

Wenn Sie einen Statusnamen übergeben GoToState Wenn das Steuerelement bereits in diesem Status wird GoToState nichts, weshalb Sie nicht für den aktuellen Zustand des Steuerelements zu überprüfen.If you pass a state name to GoToState when the control is already in that state, GoToState does nothing, so you don't need to check for the control's current state. Z. B. wenn Value ändert sich von der eine negative Zahl in eine andere negative Zahl ist, das Storyboard für die Negative Zustand nicht unterbrochen, und dem Benutzer eine Änderung im Steuerelement nicht angezeigt.For example, if Value changes from one negative number to another negative number, the storyboard for the Negative state is not interrupted and the user will not see a change in the control.

Die VisualStateManager verwendet VisualStateGroup Objekte, um zu bestimmen, welcher Zustand beendet wird, wenn Sie aufrufen GoToState.The VisualStateManager uses VisualStateGroup objects to determine which state to exit when you call GoToState. Das Steuerelement ist immer in einem Status für jeden VisualStateGroup , definiert ist, dessen ControlTemplate und lässt nur einen Status aus, wenn er in einen anderen Zustand, aus der gleichen wechselt VisualStateGroup.The control is always in one state for each VisualStateGroup that is defined in its ControlTemplate and only leaves a state when it goes into another state from the same VisualStateGroup. Z. B. die ControlTemplate von der NumericUpDown -Steuerelement definiert die Positive und Negative VisualState Objekte in einem VisualStateGroup und Focused und Unfocused VisualState Objekte in einer anderen.For example, the ControlTemplate of the NumericUpDown control defines the Positive and NegativeVisualState objects in one VisualStateGroup and the Focused and UnfocusedVisualState objects in another. (Sehen Sie die Focused und Unfocused VisualState in definiert die vollständiges Beispiel in diesem Thema, wenn das Steuerelement aus wechselt die Positive Zustand der Negative Zustand, oder umgekehrt, die Steuerung bleibt, entweder in der Focused oder Unfocused Zustand.(You can see the Focused and UnfocusedVisualState defined in the Complete Example section in this topic When the control goes from the Positive state to the Negative state, or vice versa, the control remains in either the Focused or Unfocused state.

Es gibt drei typische stellen, in denen der Zustand eines Steuerelements ändern können:There are three typical places where the state of a control might change:

Die folgenden Beispiele veranschaulichen, aktualisieren den Status der NumericUpDown -Steuerelement in diesen Fällen.The following examples demonstrate updating the state of the NumericUpDown control in these cases.

Aktualisieren Sie den Zustand des Steuerelements in der OnApplyTemplate Methode, damit das Steuerelement angezeigt wird, den richtigen Zustand über, wenn die ControlTemplate angewendet wird.You should update the state of the control in the OnApplyTemplate method so that the control appears in the correct state when the ControlTemplate is applied. Im folgenden Beispiel wird UpdateStates in OnApplyTemplate um sicherzustellen, dass das Steuerelement im entsprechenden Zustand befindet.The following example calls UpdateStates in OnApplyTemplate to ensure that the control is in the appropriate states. Nehmen wir beispielsweise an, dass Sie erstellen eine NumericUpDown steuern, und legen Sie dessen Foreground in Grün und Value -5.For example, suppose that you create a NumericUpDown control, and then set its Foreground to green and Value to -5. Wenn Sie nicht aufrufen UpdateStates bei der ControlTemplate gilt für die NumericUpDown -Steuerelement, das Steuerelement ist nicht in der Negative Status und der Wert ist Grün anstatt Rot.If you do not call UpdateStates when the ControlTemplate is applied to the NumericUpDown control, the control is not in the Negative state and the value is green instead of red. Rufen Sie UpdateStates zum Platzieren des Steuerelements der Negative Zustand.You must call UpdateStates to put the control in the Negative state.

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

    UpdateStates(false);
}
Public Overloads Overrides Sub OnApplyTemplate()

    UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
    DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)

    UpdateStates(False)
End Sub

Häufig müssen Sie den Zuständen eines Steuerelements bei Änderung einer Eigenschaft zu aktualisieren.You often need to update the states of a control when a property changes. Das folgende Beispiel zeigt die gesamte ValueChangedCallback Methode.The following example shows the entire ValueChangedCallback method. Da ValueChangedCallback wird aufgerufen, wenn Value geändert wird, ruft die Methode UpdateStates bei Value positiv, negativ oder umgekehrt geändert.Because ValueChangedCallback is called when Value changes, the method calls UpdateStates in case Value changed from positive to negative or vice versa. Es ist akzeptabel, rufen Sie UpdateStates beim Value geändert, jedoch bleibt positiv oder negativ sein, da in diesem Fall das Steuerelement den Zustand nicht verändert wird.It is acceptable to call UpdateStates when Value changes but remains positive or negative because in that case, the control will not change states.

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

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

    // Call OnValueChanged to raise the ValueChanged event.
    ctl.OnValueChanged(
        new ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, 
            newValue));
}
Private Shared Sub ValueChangedCallback(ByVal obj As DependencyObject,
                                        ByVal args As DependencyPropertyChangedEventArgs)

    Dim ctl As NumericUpDown = DirectCast(obj, NumericUpDown)
    Dim newValue As Integer = CInt(args.NewValue)

    ' Call UpdateStates because the Value might have caused the
    ' control to change ValueStates.
    ctl.UpdateStates(True)

    ' Call OnValueChanged to raise the ValueChanged event.
    ctl.OnValueChanged(New ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, newValue))
End Sub

Sie sollten auch die Status zu aktualisieren, wenn ein Ereignis auftritt.You might also need to update states when an event occurs. Das folgende Beispiel zeigt, dass die NumericUpDown Aufrufe UpdateStates auf die Control , behandeln die GotFocus Ereignis.The following example shows that the NumericUpDown calls UpdateStates on the Control to handle the GotFocus event.

protected override void OnGotFocus(RoutedEventArgs e)
{
    base.OnGotFocus(e);
    UpdateStates(true);
}
Protected Overloads Overrides Sub OnGotFocus(ByVal e As RoutedEventArgs)
    MyBase.OnGotFocus(e)
    UpdateStates(True)
End Sub

Die VisualStateManager hilft Ihnen beim Verwalten Ihres Steuerelements Zustände.The VisualStateManager helps you manage your control's states. Mithilfe der VisualStateManager, Sie stellen Sie sicher, dass das Steuerelement ordnungsgemäß zwischen den Zuständen wechselt.By using the VisualStateManager, you ensure that your control correctly transitions between states. Wenn Sie in den Empfehlungen in diesem Abschnitt für die Arbeit mit folgen der VisualStateManager, Ihres Steuerelements Code lesbarer und leichter verwaltbar bleiben.If you follow the recommendations described in this section for working with the VisualStateManager, your control's code will remain readable and maintainable.

Bereitstellen des SteuerelementvertragsProviding the Control Contract

Sie stellen einen Steuerelementvertrag bereit, damit ControlTemplate Autoren weiß, was in der Vorlage eingefügt.You provide a control contract so that ControlTemplate authors will know what to put in the template. Ein Steuerelementvertrag besteht aus drei Elementen:A control contract has three elements:

  • Den visuellen Elementen, die die Logik des Steuerelements verwenden.The visual elements that the control's logic uses.

  • Den Zuständen des Steuerelements und den Gruppen, zu denen die einzelnen Zustände gehören.The states of the control and the group each state belongs to.

  • Den öffentlichen Eigenschaften, die die visuelle Darstellung des Steuerelements beeinflussen.The public properties that visually affect the control.

Eine Person, die eine neue erstellt ControlTemplate muss wissen, was FrameworkElement Objekte, die die Logik des Steuerelements verwendet werden, was jeder Objekttyp werden und was ihr Name ist.Someone that creates a new ControlTemplate needs to know what FrameworkElement objects the control's logic uses, what type each object is, and what its name is. Ein ControlTemplate Autor muss auch wissen, den Namen der jedem möglichen Status, die das Steuerelement in sein kann und welche VisualStateGroup der Zustand befindet.A ControlTemplate author also needs to know the name of each possible state the control can be in, and which VisualStateGroup the state is in.

Zum Zurückgeben der NumericUpDown beispielsweise erwartet, dass das Steuerelement die ControlTemplate über die folgenden FrameworkElement Objekte:Returning to the NumericUpDown example, the control expects the ControlTemplate to have the following FrameworkElement objects:

Das Steuerelement kann die folgenden Zustände aufweisen:The control can be in the following states:

Um anzugeben, welcher FrameworkElement Objekte erwartet, dass das Steuerelement, das Sie verwenden die TemplatePartAttribute, die angibt, den Namen und Typ der erwarteten Elemente.To specify what FrameworkElement objects the control expects, you use the TemplatePartAttribute, which specifies the name and type of the expected elements. Um die möglichen Zustände eines Steuerelements anzugeben, verwenden Sie die TemplateVisualStateAttribute, der angibt, der den Namen des Zustands und der VisualStateGroup es gehört.To specify the possible states of a control, you use the TemplateVisualStateAttribute, which specifies the state's name and which VisualStateGroup it belongs to. Platzieren der TemplatePartAttribute und TemplateVisualStateAttribute auf die Definition der Klasse des Steuerelements.Put the TemplatePartAttribute and TemplateVisualStateAttribute on the class definition of the control.

Eine beliebige öffentliche Eigenschaft, die die Darstellung Ihres Steuerelements wirkt sich auf ist auch ein Teil des Steuerelementvertrags.Any public property that affects the appearance of your control is also a part of the control contract.

Im folgenden Beispiel wird die FrameworkElement Objekt und die Zustände für die NumericUpDown Steuerelement.The following example specifies the FrameworkElement object and states for the NumericUpDown control.

[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 static readonly DependencyProperty BackgroundProperty;
    public static readonly DependencyProperty BorderBrushProperty;
    public static readonly DependencyProperty BorderThicknessProperty;
    public static readonly DependencyProperty FontFamilyProperty;
    public static readonly DependencyProperty FontSizeProperty;
    public static readonly DependencyProperty FontStretchProperty;
    public static readonly DependencyProperty FontStyleProperty;
    public static readonly DependencyProperty FontWeightProperty;
    public static readonly DependencyProperty ForegroundProperty;
    public static readonly DependencyProperty HorizontalContentAlignmentProperty;
    public static readonly DependencyProperty PaddingProperty;
    public static readonly DependencyProperty TextAlignmentProperty;
    public static readonly DependencyProperty TextDecorationsProperty;
    public static readonly DependencyProperty TextWrappingProperty;
    public static readonly DependencyProperty VerticalContentAlignmentProperty;

    public Brush Background { get; set; }
    public Brush BorderBrush { get; set; }
    public Thickness BorderThickness { get; set; }
    public FontFamily FontFamily { get; set; }
    public double FontSize { get; set; }
    public FontStretch FontStretch { get; set; }
    public FontStyle FontStyle { get; set; }
    public FontWeight FontWeight { get; set; }
    public Brush Foreground { get; set; }
    public HorizontalAlignment HorizontalContentAlignment { get; set; }
    public Thickness Padding { get; set; }
    public TextAlignment TextAlignment { get; set; }
    public TextDecorationCollection TextDecorations { get; set; }
    public TextWrapping TextWrapping { get; set; }
    public VerticalAlignment VerticalContentAlignment { get; set; }
}
<TemplatePart(Name:="UpButtonElement", Type:=GetType(RepeatButton))> _
<TemplatePart(Name:="DownButtonElement", Type:=GetType(RepeatButton))> _
<TemplateVisualState(Name:="Positive", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Negative", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Focused", GroupName:="FocusedStates")> _
<TemplateVisualState(Name:="Unfocused", GroupName:="FocusedStates")> _
Public Class NumericUpDown
    Inherits Control
    Public Shared ReadOnly BackgroundProperty As DependencyProperty
    Public Shared ReadOnly BorderBrushProperty As DependencyProperty
    Public Shared ReadOnly BorderThicknessProperty As DependencyProperty
    Public Shared ReadOnly FontFamilyProperty As DependencyProperty
    Public Shared ReadOnly FontSizeProperty As DependencyProperty
    Public Shared ReadOnly FontStretchProperty As DependencyProperty
    Public Shared ReadOnly FontStyleProperty As DependencyProperty
    Public Shared ReadOnly FontWeightProperty As DependencyProperty
    Public Shared ReadOnly ForegroundProperty As DependencyProperty
    Public Shared ReadOnly HorizontalContentAlignmentProperty As DependencyProperty
    Public Shared ReadOnly PaddingProperty As DependencyProperty
    Public Shared ReadOnly TextAlignmentProperty As DependencyProperty
    Public Shared ReadOnly TextDecorationsProperty As DependencyProperty
    Public Shared ReadOnly TextWrappingProperty As DependencyProperty
    Public Shared ReadOnly VerticalContentAlignmentProperty As DependencyProperty


    Private _Background As Brush
    Public Property Background() As Brush
        Get
            Return _Background
        End Get
        Set(ByVal value As Brush)
            _Background = value
        End Set
    End Property

    Private _BorderBrush As Brush
    Public Property BorderBrush() As Brush
        Get
            Return _BorderBrush
        End Get
        Set(ByVal value As Brush)
            _BorderBrush = value
        End Set
    End Property

    Private _BorderThickness As Thickness
    Public Property BorderThickness() As Thickness
        Get
            Return _BorderThickness
        End Get
        Set(ByVal value As Thickness)
            _BorderThickness = value
        End Set
    End Property

    Private _FontFamily As FontFamily
    Public Property FontFamily() As FontFamily
        Get
            Return _FontFamily
        End Get
        Set(ByVal value As FontFamily)
            _FontFamily = value
        End Set
    End Property

    Private _FontSize As Double
    Public Property FontSize() As Double
        Get
            Return _FontSize
        End Get
        Set(ByVal value As Double)
            _FontSize = value
        End Set
    End Property

    Private _FontStretch As FontStretch
    Public Property FontStretch() As FontStretch
        Get
            Return _FontStretch
        End Get
        Set(ByVal value As FontStretch)
            _FontStretch = value
        End Set
    End Property

    Private _FontStyle As FontStyle
    Public Property FontStyle() As FontStyle
        Get
            Return _FontStyle
        End Get
        Set(ByVal value As FontStyle)
            _FontStyle = value
        End Set
    End Property

    Private _FontWeight As FontWeight
    Public Property FontWeight() As FontWeight
        Get
            Return _FontWeight
        End Get
        Set(ByVal value As FontWeight)
            _FontWeight = value
        End Set
    End Property

    Private _Foreground As Brush
    Public Property Foreground() As Brush
        Get
            Return _Foreground
        End Get
        Set(ByVal value As Brush)
            _Foreground = value
        End Set
    End Property

    Private _HorizontalContentAlignment As HorizontalAlignment
    Public Property HorizontalContentAlignment() As HorizontalAlignment
        Get
            Return _HorizontalContentAlignment
        End Get
        Set(ByVal value As HorizontalAlignment)
            _HorizontalContentAlignment = value
        End Set
    End Property

    Private _Padding As Thickness
    Public Property Padding() As Thickness
        Get
            Return _Padding
        End Get
        Set(ByVal value As Thickness)
            _Padding = value
        End Set
    End Property

    Private _TextAlignment As TextAlignment
    Public Property TextAlignment() As TextAlignment
        Get
            Return _TextAlignment
        End Get
        Set(ByVal value As TextAlignment)
            _TextAlignment = value
        End Set
    End Property

    Private _TextDecorations As TextDecorationCollection
    Public Property TextDecorations() As TextDecorationCollection
        Get
            Return _TextDecorations
        End Get
        Set(ByVal value As TextDecorationCollection)
            _TextDecorations = value
        End Set
    End Property

    Private _TextWrapping As TextWrapping
    Public Property TextWrapping() As TextWrapping
        Get
            Return _TextWrapping
        End Get
        Set(ByVal value As TextWrapping)
            _TextWrapping = value
        End Set
    End Property

    Private _VerticalContentAlignment As VerticalAlignment
    Public Property VerticalContentAlignment() As VerticalAlignment
        Get
            Return _VerticalContentAlignment
        End Get
        Set(ByVal value As VerticalAlignment)
            _VerticalContentAlignment = value
        End Set
    End Property
End Class

Vollständiges BeispielComplete Example

Im folgende Beispiel wird die gesamte ControlTemplate für die NumericUpDown Steuerelement.The following example is the entire ControlTemplate for the NumericUpDown control.

<!--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:local="clr-namespace:VSMCustomControl">


  <Style TargetType="{x:Type local:NumericUpDown}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:NumericUpDown">
          <Grid  Margin="3" 
                Background="{TemplateBinding Background}">


            <VisualStateManager.VisualStateGroups>

              <VisualStateGroup Name="ValueStates">

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

                </VisualState>

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

              <VisualStateGroup Name="FocusStates">

                <!--Add a focus rectangle to highlight the entire control
                    when it has focus.-->
                <VisualState 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>
                </VisualState>

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

            </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">
                <!--Bind the TextBlock to the Value property-->
                <TextBlock Name="TextBlock"
                  Width="60" TextAlignment="Right" Padding="5"
                  Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                                 AncestorType={x:Type local:NumericUpDown}}, 
                                 Path=Value}"/>
              </Border>

              <RepeatButton Content="Up" Margin="2,5,5,0"
                Name="UpButton"
                Grid.Column="1" Grid.Row="0"/>
              <RepeatButton Content="Down" Margin="2,0,5,5"
                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>

Das folgende Beispiel zeigt die Logik für die NumericUpDown.The following example shows the logic for the NumericUpDown.

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

namespace VSMCustomControl
{
    [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)));

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

            set
            {
                SetValue(ValueProperty, value);

            }
        }

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

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

            // Call OnValueChanged to raise the ValueChanged event.
            ctl.OnValueChanged(
                new ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, 
                    newValue));
        }

        public static readonly RoutedEvent ValueChangedEvent =
            EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Direct,
                          typeof(ValueChangedEventHandler), typeof(NumericUpDown));

        public event ValueChangedEventHandler ValueChanged
        {
            add { AddHandler(ValueChangedEvent, value); }
            remove { RemoveHandler(ValueChangedEvent, value); }
        }


        protected virtual void OnValueChanged(ValueChangedEventArgs e)
        {
            // Raise the ValueChanged event so applications can be alerted
            // when Value changes.
            RaiseEvent(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 override void OnApplyTemplate()
        {
            UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
            DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
            //TextElement = GetTemplateChild("TextBlock") as TextBlock;

            UpdateStates(false);
        }

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


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

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


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

    public class ValueChangedEventArgs : RoutedEventArgs
    {
        private int _value;

        public ValueChangedEventArgs(RoutedEvent id, int num)
        {
            _value = num;
            RoutedEvent = id;
        }

        public int Value
        {
            get { return _value; }
        }
    }
}
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Controls.Primitives
Imports System.Windows.Input
Imports System.Windows.Media

<TemplatePart(Name:="UpButtonElement", Type:=GetType(RepeatButton))> _
<TemplatePart(Name:="DownButtonElement", Type:=GetType(RepeatButton))> _
<TemplateVisualState(Name:="Positive", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Negative", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Focused", GroupName:="FocusedStates")> _
<TemplateVisualState(Name:="Unfocused", GroupName:="FocusedStates")> _
Public Class NumericUpDown
    Inherits Control

    Public Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
        Me.IsTabStop = True
    End Sub

    Public Shared ReadOnly ValueProperty As DependencyProperty =
        DependencyProperty.Register("Value", GetType(Integer), GetType(NumericUpDown),
                          New PropertyMetadata(New PropertyChangedCallback(AddressOf ValueChangedCallback)))

    Public Property Value() As Integer

        Get
            Return CInt(GetValue(ValueProperty))
        End Get

        Set(ByVal value As Integer)

            SetValue(ValueProperty, value)
        End Set
    End Property

    Private Shared Sub ValueChangedCallback(ByVal obj As DependencyObject,
                                            ByVal args As DependencyPropertyChangedEventArgs)

        Dim ctl As NumericUpDown = DirectCast(obj, NumericUpDown)
        Dim newValue As Integer = CInt(args.NewValue)

        ' Call UpdateStates because the Value might have caused the
        ' control to change ValueStates.
        ctl.UpdateStates(True)

        ' Call OnValueChanged to raise the ValueChanged event.
        ctl.OnValueChanged(New ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, newValue))
    End Sub

    Public Shared ReadOnly ValueChangedEvent As RoutedEvent =
        EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Direct,
                                         GetType(ValueChangedEventHandler), GetType(NumericUpDown))

    Public Custom Event ValueChanged As ValueChangedEventHandler

        AddHandler(ByVal value As ValueChangedEventHandler)
            Me.AddHandler(ValueChangedEvent, value)
        End AddHandler

        RemoveHandler(ByVal value As ValueChangedEventHandler)
            Me.RemoveHandler(ValueChangedEvent, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Me.RaiseEvent(e)
        End RaiseEvent

    End Event


    Protected Overridable Sub OnValueChanged(ByVal e As ValueChangedEventArgs)
        ' Raise the ValueChanged event so applications can be alerted
        ' when Value changes.
        MyBase.RaiseEvent(e)
    End Sub


#Region "NUDCode"
    Private Sub UpdateStates(ByVal useTransitions As Boolean)

        If Value >= 0 Then
            VisualStateManager.GoToState(Me, "Positive", useTransitions)
        Else
            VisualStateManager.GoToState(Me, "Negative", useTransitions)
        End If

        If IsFocused Then
            VisualStateManager.GoToState(Me, "Focused", useTransitions)
        Else
            VisualStateManager.GoToState(Me, "Unfocused", useTransitions)

        End If
    End Sub

    Public Overloads Overrides Sub OnApplyTemplate()

        UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
        DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)

        UpdateStates(False)
    End Sub

    Private m_downButtonElement As RepeatButton

    Private Property DownButtonElement() As RepeatButton
        Get
            Return m_downButtonElement
        End Get

        Set(ByVal value As RepeatButton)

            If m_downButtonElement IsNot Nothing Then
                RemoveHandler m_downButtonElement.Click, AddressOf downButtonElement_Click
            End If
            m_downButtonElement = value

            If m_downButtonElement IsNot Nothing Then
                AddHandler m_downButtonElement.Click, AddressOf downButtonElement_Click
            End If
        End Set
    End Property

    Private Sub downButtonElement_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Value -= 1
    End Sub

    Private m_upButtonElement As RepeatButton

    Private Property UpButtonElement() As RepeatButton
        Get
            Return m_upButtonElement
        End Get

        Set(ByVal value As RepeatButton)
            If m_upButtonElement IsNot Nothing Then
                RemoveHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
            End If
            m_upButtonElement = value

            If m_upButtonElement IsNot Nothing Then
                AddHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
            End If
        End Set
    End Property

    Private Sub upButtonElement_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Value += 1
    End Sub

    Protected Overloads Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)
        MyBase.OnMouseLeftButtonDown(e)
        Focus()
    End Sub


    Protected Overloads Overrides Sub OnGotFocus(ByVal e As RoutedEventArgs)
        MyBase.OnGotFocus(e)
        UpdateStates(True)
    End Sub

    Protected Overloads Overrides Sub OnLostFocus(ByVal e As RoutedEventArgs)
        MyBase.OnLostFocus(e)
        UpdateStates(True)
    End Sub
#End Region
End Class


Public Delegate Sub ValueChangedEventHandler(ByVal sender As Object,
                                             ByVal e As ValueChangedEventArgs)

Public Class ValueChangedEventArgs
    Inherits RoutedEventArgs
    Private _value As Integer

    Public Sub New(ByVal id As RoutedEvent,
                   ByVal num As Integer)

        _value = num
        RoutedEvent = id
    End Sub

    Public ReadOnly Property Value() As Integer
        Get
            Return _value
        End Get
    End Property
End Class

Siehe auchSee also