Übersicht über das Erstellen von Steuerelementen

Aktualisiert: November 2007

Dank der Erweiterbarkeit des Windows Presentation Foundation (WPF)-Steuerelementmodells ist es nur selten erforderlich, ein neues Steuerelement zu erstellen. In bestimmten Fällen kann es jedoch auch weiterhin erforderlich sein, ein benutzerdefiniertes Steuerelement zu erstellen. In diesem Thema werden die Features erläutert, dank denen ein benutzerdefiniertes Steuerelement nur noch selten erstellt werden muss, und es werden die verschiedenen Modelle für das Erstellen von Steuerelementen in Windows Presentation Foundation (WPF) beschrieben. Dieses Thema veranschaulicht auch, wie ein neues Steuerelement erstellt wird.

Dieses Thema enthält folgende Abschnitte.

  • Alternativen zum Schreiben eines neuen Steuerelements
  • Modelle für das Erstellen von Steuerelementen
  • Grundlagen des Erstellens von Steuerelementen
  • Erben von UserControl im Vergleich zur Verwendung einer ControlTemplate
  • Verwandte Abschnitte

Alternativen zum Schreiben eines neuen Steuerelements

Wenn Sie in der Vergangenheit ein vorhandenes Steuerelement an eigene Zwecke anpassen wollten, waren Sie darauf beschränkt, die Standardeigenschaften des Steuerelements zu ändern, also Hintergrundfarbe, Rahmenbreite und Schriftgröße. Wenn Sie das Aussehen oder Verhalten eines Steuerelements über diese vordefinierten Parameter hinaus erweitern möchten, müssen Sie ein neues Steuerelement erstellen. Normalerweise verwenden Sie dazu die Vererbung von einem vorhandenen Steuerelement und das Überschreiben der Methode, die für das Zeichnen des Steuerelements zuständig ist. Dies ist auch weiterhin möglich, aber Sie können vorhandene Steuerelemente mit WPF anpassen, indem Sie das umfangreiche Inhaltsmodell sowie die Stile, Vorlagen und Trigger verwenden. Die folgende Liste enthält Beispiele dazu, wie Sie mit diesen Features benutzerdefinierte und einheitliche Abläufe erzielen, ohne ein neues Steuerelement erstellen zu müssen.

  • Umfangreicher Inhalt Viele der WPF-Standardsteuerelemente unterstützen umfangreichen Inhalt. Die Inhaltseigenschaft eines Button-Elements hat z. B. den Typ Object, sodass auf einem Button-Element theoretisch alles angezeigt werden kann. Damit eine Schaltfläche ein Bild und Text anzeigt, können Sie ein Bild und einen TextBlock einem StackPanel hinzufügen und das StackPanel der Content-Eigenschaft zuweisen. Da diese Steuerelemente visuelle WPF-Elemente und beliebige Daten anzeigen können, ist das Erstellen neuer Steuerelemente oder das Ändern vorhandener Steuerelemente zur Unterstützung einer komplexen visuellen Darstellung nur noch selten notwendig. Weitere Informationen zum Inhaltsmodell für Button sowie andere Steuerelemente finden Sie unter Übersicht über Inhaltsmodelle für Steuerelemente. Weitere Informationen zu anderen Inhaltsmodellen in WPF finden Sie unter Inhaltsmodelle.

  • Stile Ein Style ist eine Auflistung von Werten, die Eigenschaften für ein Steuerelement darstellen. Durch die Verwendung von Stilen können Sie eine wiederverwendbare Darstellung des gewünschten Aussehens und Verhaltens eines Steuerelements ohne Schreiben eines neuen Steuerelements erstellen. Angenommen, Sie möchten für alle TextBlock-Steuerelemente die Schriftart Arial in Rot und mit dem Schriftgrad 14 verwenden. Sie können einen Stil als Ressource erstellen und die jeweiligen Eigenschaften entsprechend festlegen. So verfügt jeder TextBlock, den Sie der Anwendung hinzufügen, über die gleiche Darstellung.

  • Datenvorlagen Mithilfe einer DataTemplate können Sie anpassen, wie Daten auf einem Steuerelement angezeigt werden. Zum Beispiel können Sie eine DataTemplate verwenden, um anzugeben, wie Daten in einem ListBox angezeigt werden. Ein Beispiel hierfür finden Sie unter Übersicht über Datenvorlagen. Zusätzlich zum Anpassen der Datendarstellung kann eine DataTemplate Benutzeroberflächenelemente enthalten. Auf diese Weise können Sie beim Erstellen benutzerdefinierter Oberflächen flexibel vorgehen. Mithilfe einer DataTemplate können Sie z. B. ein ComboBox-Element erstellen, in dem jedes Element ein Kontrollkästchen enthält.

  • Steuerelementvorlagen. Viele Steuerelemente in WPF verwenden eine ControlTemplate, um die Struktur und das Aussehen eines Steuerelements zu definieren. Dadurch ist die Darstellung eines Steuerelements von seiner Funktionalität getrennt. Sie können das Aussehen eines Steuerelements stark verändern, indem Sie seine ControlTemplate neu definieren. Angenommen, Sie möchten ein Steuerelement erstellen, das wie eine Ampel aussieht. Dieses Steuerelement verfügt über eine einfache Benutzeroberfläche und Funktion. Das Steuerelement besteht aus drei Kreisen, von denen nur jeweils einer leuchten kann. Nach einiger Überlegung stellen Sie fest, dass ein RadioButton-Element über die Funktion verfügt, bei der jeweils eine Komponente ausgewählt ist. Das standardmäßige Aussehen des RadioButton-Elements entspricht jedoch überhaupt nicht einer Ampel. Da das RadioButton-Element zum Definieren seines Aussehens eine Steuerelementvorlage verwendet, können Sie die ControlTemplate auf einfache Weise neu definieren, um die Anforderungen des Steuerelements zu erfüllen und die Ampel mithilfe von Optionsfeldern zu erstellen.

    Tipp

    Obwohl ein RadioButton-Element eine DataTemplate verwenden kann, ist eine DataTemplate in diesem Beispiel nicht ausreichend. Die DataTemplate definiert die Darstellung des Inhalts eines Steuerelements. Bei einem RadioButton-Element ist der Inhalt das, was rechts von dem Kreis angezeigt wird, der angibt, ob das RadioButton-Element aktiviert ist. Für das Beispiel mit der Ampel muss das Optionsfeld nur ein Kreis sein, der leuchten kann. Da die Darstellungsanforderung für die Ampel sich so stark von der Standarddarstellung des RadioButton-Elements unterscheidet, müssen Sie die ControlTemplate neu definieren. Normalerweise wird eine DataTemplate verwendet, um den Inhalt (bzw. die Daten) eines Steuerelements zu definieren, und eine ControlTemplate wird verwendet, um die Strukturierung eines Steuerelements zu definieren.

  • Trigger Mit einem Trigger können Sie das Aussehen und Verhalten eines Steuerelements dynamisch ändern, ohne ein neues Steuerelement erstellen zu müssen. Angenommen, Sie verwenden in der Anwendung mehrere ListBox-Steuerelemente und möchten, dass die Elemente in allen ListBox-Objekten fett und rot angezeigt werden, wenn sie markiert werden. Sie neigen ggf. zuerst dazu, eine Klasse zu erstellen, die von ListBox erbt, und die OnSelectionChanged-Methode zu überschreiben, um das Aussehen des markierten Elements zu ändern. Es ist jedoch besser, einem Stil eines ListBoxItem-Objekts einen Trigger hinzuzufügen, der das Aussehen des markierten Elements ändert. Mit einem Trigger können Sie Eigenschaftenwerte ändern oder basierend auf dem Wert einer Eigenschaft Aktionen ausführen. Mit einem EventTrigger können Sie Aktionen ausführen, wenn ein Ereignis eintritt.

Weitere Informationen zu Stilen, Vorlagen und Triggern finden Sie unter Erstellen von Formaten und Vorlagen.

Wenn ein Steuerelement über die Funktionalität eines vorhandenen Steuerelements verfügt, Sie jedoch ein anderes Aussehen benötigen, sollten Sie zuerst die Verwendung der Methoden erwägen, die in diesem Abschnitt erläutert werden, um das Aussehen eines vorhandenen Steuerelements zu ändern.

Modelle für das Erstellen von Steuerelementen

Das umfangreiche Inhaltsmodell sowie die Stile, Vorlagen und Trigger sorgen dafür, dass Sie in vielen Fällen kein neues Steuerelement erstellen müssen. Wenn Sie jedoch ein neues Steuerelement erstellen müssen, sollten Sie mit den verschiedenen Modellen zum Erstellen von Steuerelementen von WPF vertraut sein. WPF stellt drei allgemeine Modelle zum Erstellen eines Steuerelements bereit, die jeweils verschiedene Features und Flexibilitätsgrade bieten. Die Basisklassen für die drei Modelle sind UserControl, Control und FrameworkElement.

Ableiten von UserControl

Die einfachste Möglichkeit zum Erstellen eines Steuerelements in WPF ist die Ableitung von UserControl. Wenn Sie ein Steuerelement erstellen, das von UserControl erbt, fügen Sie dem UserControl-Element vorhandene Komponenten hinzu und verweisen mithilfe von Extensible Application Markup Language (XAML) auf Ereignishandler. Sie können dann auf die benannten Elemente verweisen und die Ereignishandler im Code definieren. Dieses Entwicklungsmodell ähnelt dem Modell, das in WPF für die Anwendungsentwicklung verwendet wird.

Ein UserControl kann, wenn es ordnungsgemäß erstellt wurde, die Vorteile von umfangreichem Inhalt, Stilen und Triggern nutzen. Wenn das Steuerelement jedoch von UserControl erbt, können Personen, die das Steuerelement verwenden, keine DataTemplate oder ControlTemplate verwenden, um dessen Aussehen anzupassen. Es ist eine Ableitung von der Control-Klasse oder einer ihrer abgeleiteten Klassen (mit Ausnahme von UserControl) erforderlich, um ein benutzerdefiniertes Steuerelement zu erstellen, das Vorlagen unterstützt.

Vorteile der Ableitung von UserControl

Erwägen Sie die Ableitung von UserControl, wenn alle folgenden Bedingungen gelten:

  • Sie möchten das Steuerelement in ähnlicher Weise wie eine Anwendung erstellen.

  • Das Steuerelement besteht nur aus bereits vorhandenen Komponenten.

  • Sie müssen keine komplexe Anpassung unterstützen.

Ableiten von Control

Eine Ableitung von der Control-Klasse ist das Modell, das die meisten der vorhandenen WPF-Steuerelemente verwenden. Wenn Sie ein Steuerelement erstellen, das von der Control-Klasse erbt, definieren Sie das Aussehen mithilfe von Vorlagen. Dabei trennen Sie die Funktionslogik von der visuellen Darstellung. Sie können auch die Entkopplung der Benutzeroberfläche und der Logik sicherstellen, indem Sie anstelle von Ereignissen Befehle und Bindungen verwenden und das Verweisen auf Elemente in der ControlTemplate nach Möglichkeit vermeiden.  Wenn die Benutzeroberfläche und die Logik des Steuerelements korrekt entkoppelt sind, können die Benutzer des Steuerelements die ControlTemplate des Steuerelements neu definieren, um das Aussehen anzupassen. Auch wenn das Erstellen eines benutzerdefinierten Control-Elements nicht so einfach wie das Erstellen eines UserControl-Elements ist, bietet ein benutzerdefiniertes Control-Element doch die größte Flexibilität.

Vorteile der Ableitung von Control

Wenn einer oder mehrere der folgenden Umstände zutreffen, sollten Sie die Ableitung von Control statt der Verwendung der UserControl-Klasse in Betracht ziehen:

  • Sie möchten, dass die Darstellung des Steuerelements über die ControlTemplate anpassbar ist.

  • Sie möchten, dass das Steuerelement verschiedene Designs unterstützt.

Ableiten von FrameworkElement

Steuerelemente, die von UserControl oder Control abgeleitet werden, basieren auf dem Zusammensetzen vorhandener Elemente. In vielen Fällen ist dies akzeptabel, da sich Objekte, die von FrameworkElement erben, in einer ControlTemplate befinden können. Es kann jedoch vorkommen, dass die Darstellung eines Steuerelements mehr als die Funktionalität einer einfachen Elementzusammensetzung erfordert. Für solche Szenarien sollte eine Komponente auf der Basis von FrameworkElement erstellt werden.

Es gibt zwei Standardmethoden zum Erstellen von auf FrameworkElement basierenden Komponenten: direktes Rendering und benutzerdefinierte Elementzusammensetzung. Das direkte Rendering beinhaltet das Überschreiben der OnRender-Methode von FrameworkElement und das Bereitstellen von DrawingContext-Operationen, die die visuelle Struktur der Komponente explizit definieren. Diese Methode wird von Image und Border verwendet. Die benutzerdefinierte Elementzusammensetzung beinhaltet das Verwenden von Objekten des Typs Visual, um die Darstellung der Komponente zusammenzusetzen. Ein Beispiel finden Sie unter Verwenden von DrawingVisual-Objekten. Track ist ein Beispiel für ein Steuerelement in WPF, das die benutzerdefinierte Elementzusammensetzung verwendet. Es ist auch möglich, direktes Rendering und benutzerdefinierte Elementzusammensetzung im gleichen Steuerelement zu kombinieren.

Vorteile der Ableitung von FrameworkElement

Erwägen Sie die Ableitung von FrameworkElement, wenn eine oder mehrere der folgenden Bedingungen gelten:

  • Sie benötigen eine genaue Steuerung der Darstellung des Steuerelements, die über das hinausgeht, was die einfache Elementzusammensetzung bietet.

  • Sie möchten die Darstellung des Steuerelements definieren, indem Sie eine eigene Renderinglogik definieren.

  • Sie möchten vorhandene Elemente in einer Weise neu zusammensetzen, die über das hinausgeht, was mit UserControl und Control möglich ist.

Grundlagen des Erstellens von Steuerelementen

Wie bereits erwähnt, ist eine der leistungsfähigsten Features von WPF die Möglichkeit, über die Festlegung der grundlegenden Eigenschaften eines Steuerelements zum Ändern des Aussehens und Verhaltens hinausgehen zu können, ohne ein benutzerdefiniertes Steuerelement erstellen zu müssen. Die Features in Bezug auf Stil, Datenbindung und Trigger werden mithilfe des WPF-Eigenschaftensystems und des WPF-Ereignissystems möglich gemacht. Wenn Sie im Steuerelement Abhängigkeitseigenschaften und Routingereignisse implementieren, können Benutzer des benutzerdefinierten Steuerelements diese Features wie bei einem Steuerelement verwenden, das bereits in WPF integriert ist. Dies gilt unabhängig von dem Modell, das Sie zum Erstellen des benutzerdefinierten Steuerelements verwenden. 

Verwenden von Abhängigkeitseigenschaften

Wenn es sich bei einer Eigenschaft um eine Abhängigkeitseigenschaft handelt, können Sie wie folgt vorgehen:

  • Legen Sie die Eigenschaft in einem Stil fest.

  • Binden Sie die Eigenschaft an eine Datenquelle.

  • Verwenden Sie eine dynamische Ressource als Wert der Eigenschaft.

  • Animieren Sie die Eigenschaft.

Wenn Sie möchten, dass eine Eigenschaft des Steuerelements diese Funktionalität unterstützt, sollten Sie sie als Abhängigkeitseigenschaft implementieren. Im folgenden Beispiel wird eine Abhängigkeitseigenschaft mit dem Namen Value definiert, indem wie folgt vorgegangen wird:

  • Ein DependencyProperty-Bezeichner mit dem Namen ValueProperty wird als public static readonly-Feld definiert.

  • Der Eigenschaftenname wird beim Eigenschaftensystem registriert, indem DependencyProperty.Register aufgerufen wird, um Folgendes anzugeben:

    • Name der Eigenschaft.

    • Typ der Eigenschaft.

    • Typ, der die Eigenschaft besitzt.

    • Die Metadaten für die Eigenschaft. Die Metadaten enthalten den Standardwert der Eigenschaft, einen CoerceValueCallback und einen PropertyChangedCallback.

  • Eine CLR-Wrappereigenschaft mit dem Namen Value wird definiert. Dies ist derselbe Name, der zum Registrieren der Abhängigkeitseigenschaft verwendet wird, indem die Accessoren get und set der Eigenschaft implementiert werden. Beachten Sie, dass die Accessoren get und set nur GetValue bzw. SetValue aufrufen. Es ist zu empfehlen, dass die Accessoren von Abhängigkeitseigenschaften keine zusätzliche Logik enthalten, da Clients und WPF die Accessoren umgehen und GetValue und SetValue direkt aufrufen können. Wenn eine Eigenschaft z. B. an eine Datenquelle gebunden ist, wird der set-Accessor der Eigenschaft nicht aufgerufen. Anstatt den Accessoren get und set zusätzliche Logik hinzuzufügen, sollten Sie die Delegaten ValidateValueCallback, CoerceValueCallback und PropertyChangedCallback verwenden, um auf den Wert zu reagieren bzw. den Wert zu prüfen, wenn dieser sich ändert. Weitere Informationen zu diesen Rückrufen finden Sie unter Rückrufe und Validierung von Abhängigkeitseigenschaften.

  • Eine Methode für den CoerceValueCallback mit dem Namen CoerceValue wird definiert. CoerceValue stellt sicher, dass Value größer oder gleich MinValue und kleiner oder gleich MaxValue ist.

  • Eine Methode für den PropertyChangedCallback mit dem Namen OnValueChanged wird definiert. OnValueChanged erstellt ein RoutedPropertyChangedEventArgs<T>-Objekt und bereitet das Auslösen des Routingereignisses ValueChanged vor. Routingereignisse werden im nächsten Abschnitt erläutert.

/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(
        "Value", typeof(decimal), typeof(NumericUpDown),
        new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                      new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{          
    get { return (decimal)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

private static object CoerceValue(DependencyObject element, object value)
{
    decimal newValue = (decimal)value;
    NumericUpDown control = (NumericUpDown)element;

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));

    return newValue;
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    NumericUpDown control = (NumericUpDown)obj;         

    RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
        (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
    control.OnValueChanged(e);
}

Weitere Informationen finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften.

Verwenden von Routingereignissen

Ähnlich wie Abhängigkeitseigenschaften, die das Konzept der CLR-Eigenschaften um zusätzliche Funktionen erweitern, erweitern Routingereignisse das Konzept der CLR-Standardereignisse. Beim Erstellen eines neuen WPF-Steuerelements ist es außerdem ratsam, das Ereignis als Routingereignis zu implementieren, da ein Routingereignis das folgende Verhalten unterstützt:

  • Ereignisse können über ein übergeordnetes Element mehrerer Steuerelemente behandelt werden. Wenn es sich bei einem Ereignis um ein Bubbling-Ereignis handelt, kann ein einzelnes übergeordnetes Element in der Elementstruktur das Ereignis abonnieren. Dann können Anwendungsentwickler einen Handler verwenden, um auf das Ereignis mehrerer Steuerelemente zu reagieren. Wenn das Steuerelement z. B. jeweils Teil der Elemente in einem ListBox-Objekt ist (weil es in einer DataTemplate enthalten ist), kann der Anwendungsentwickler den Ereignishandler für das Ereignis des betreffenden Steuerelements unter ListBox definieren. Der Ereignishandler wird jeweils aufgerufen, wenn das Ereignis für eines der Steuerelemente auftritt.

  • Sie können Routingereignisse in einem EventSetter verwenden, damit Anwendungsentwickler den Handler eines Ereignisses innerhalb eines Stils angeben können.

  • Sie können Routingereignisse in einem EventTrigger verwenden. Dies ist hilfreich beim Animieren von Eigenschaften mit XAML. Weitere Informationen finden Sie unter Übersicht über Animationen.

Im folgenden Beispiel wird ein Routingereignis definiert, indem wie folgt vorgegangen wird:

  • Ein RoutedEvent-Bezeichner mit dem Namen ValueChangedEvent wird als public static readonly-Feld definiert.

  • Das Routingereignis wird registriert, indem die EventManager.RegisterRoutedEvent-Methode aufgerufen wird. Im Beispiel werden beim Aufrufen von RegisterRoutedEvent die folgenden Informationen angegeben:

    • Der Name des Ereignisses lautet ValueChanged.

    • Die Routingstrategie ist Bubble. Dies bedeutet, dass zuerst ein Ereignishandler auf der Quelle (Objekt, das das Ereignis auslöst) aufgerufen wird. Danach werden nacheinander die Ereignishandler der übergeordneten Elemente der Quelle aufgerufen, wobei mit dem Ereignishandler des jeweils nächsten übergeordneten Elements begonnen wird.

    • Der Typ des Ereignishandlers ist RoutedPropertyChangedEventHandler<T>, der mit einem Decimal-Typ erstellt wird.

    • Der besitzende Typ des Ereignisses ist NumericUpDown.

  • Ein öffentliches Ereignis mit dem Namen ValueChanged wird deklariert, wobei Ereignisaccessordeklarationen eingeschlossen werden. Im Beispiel wird AddHandler in der add-Accessordeklaration und RemoveHandler in der remove-Accessordeklaration aufgerufen, um die WPF-Ereignisdienste zu verwenden.

  • Eine geschützte virtuelle Methode mit dem Namen OnValueChanged wird erstellt, die das ValueChanged-Ereignis auslöst.

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble, 
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
{
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }
}

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
{
    RaiseEvent(args);
}

Weitere Informationen finden Sie unter Übersicht über Routingereignisse und unter Gewusst wie: Erstellen eines benutzerdefinierten Routingereignisses.

Verwenden der Bindung

Um die Benutzeroberfläche des Steuerelements von seiner Logik zu entkoppeln, können Sie erwägen, die Datenbindung zu verwenden. Dies ist besonders wichtig, wenn Sie die Darstellung des Steuerelements mithilfe einer ControlTemplate definieren. Wenn Sie die Datenbindung verwenden, ist es ggf. nicht mehr erforderlich, auf bestimmte Teile der Benutzeroberfläche zu verweisen. Vermeiden Sie Verweise auf Elemente in der ControlTemplate. Wenn der Code auf Elemente in der ControlTemplate verweist und die ControlTemplate geändert wird, muss das Element, auf das verwiesen wird, in die neue ControlTemplate aufgenommen werden.

Im folgenden Beispiel wird der TextBlock des NumericUpDown-Steuerelements aktualisiert, indem ein Name zugewiesen und im Code auf das Textfeld verwiesen wird.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
</Border>
private void UpdateTextBlock()
{
    valueText.Text = Value.ToString();
}

Im folgenden Beispiel wird die Bindung verwendet, um dasselbe Ziel zu erreichen.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

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

</Border>

Weitere Informationen zur Datenbindung finden Sie unter Übersicht über Datenbindung.

Definieren und Verwenden von Befehlen

Erwägen Sie, als Alternative zur Ereignisbehandlung zur Bereitstellung von Funktionalität Befehle zu definieren und zu verwenden. Wenn Sie Ereignishandler in einem Steuerelement verwenden, haben Anwendungen keinen Zugang zu der innerhalb des Ereignishandlers ausgeführten Aktion. Wenn Sie Befehle in dem Steuerelement implementieren, können Anwendungen auf die Funktionalität zugreifen, die andernfalls nicht verfügbar wäre.

Das folgende Beispiel ist Teil eines Steuerelements zur Behandlung des Klickereignisses zweier Schaltflächen, mit dem der Wert des NumericUpDown-Steuerelements geändert wird. Unabhängig davon, ob es sich bei dem Steuerelement um ein UserControl oder um ein Control mit einer ControlTemplate handelt, die Benutzeroberfläche und die Logik sind eng verknüpft, da das Steuerelement Ereignishandler verwendet.

 <RepeatButton Name="upButton" Click="upButton_Click" 
                  Grid.Column="1" Grid.Row="0">Up</RepeatButton>
                  
    <RepeatButton Name="downButton" Click="downButton_Click" 
                  Grid.Column="1" Grid.Row="1">Down</RepeatButton>
private void upButton_Click(object sender, EventArgs e)
{
        Value++;
}

private void downButton_Click(object sender, EventArgs e)
{
        Value--;
}

Im folgenden Beispiel werden zwei Befehle definiert, die den Wert des NumericUpDown-Steuerelements ändern.

public static RoutedCommand IncreaseCommand
{
    get
    {
        return _increaseCommand;
    }
}
public static RoutedCommand DecreaseCommand
{
    get
    {
        return _decreaseCommand;
    }
}

private static void InitializeCommands()
{
    _increaseCommand = new RoutedCommand("IncreaseCommand", typeof(NumericUpDown));
    CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), 
                            new CommandBinding(_increaseCommand, OnIncreaseCommand));
    CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), 
                            new InputBinding(_increaseCommand, new KeyGesture(Key.Up)));

    _decreaseCommand = new RoutedCommand("DecreaseCommand", typeof(NumericUpDown));
    CommandManager.RegisterClassCommandBinding(typeof(NumericUpDown), 
                            new CommandBinding(_decreaseCommand, OnDecreaseCommand));
    CommandManager.RegisterClassInputBinding(typeof(NumericUpDown), 
                            new InputBinding(_decreaseCommand, new KeyGesture(Key.Down)));
}

private static void OnIncreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
    NumericUpDown control = sender as NumericUpDown;
    if (control != null)
    {
        control.OnIncrease();
    }
}
private static void OnDecreaseCommand(object sender, ExecutedRoutedEventArgs e)
{
    NumericUpDown control = sender as NumericUpDown;
    if (control != null)
    {
        control.OnDecrease();
    }
}

protected virtual void OnIncrease()
{
    Value++;
}
protected virtual void OnDecrease()
{
    Value--;
}

private static RoutedCommand _increaseCommand;
private static RoutedCommand _decreaseCommand;

Die Elemente in der Vorlage können dann auf die Befehle verweisen, wie im folgenden Beispiel gezeigt.

<RepeatButton 
    Command="{x:Static local:NumericUpDown.IncreaseCommand}"  
    Grid.Column="1" Grid.Row="0">Up</RepeatButton>
<RepeatButton 
    Command="{x:Static local:NumericUpDown.DecreaseCommand}"  
    Grid.Column="1" Grid.Row="1">Down</RepeatButton>

In diesem Fall können die Anwendungen auf die Bindungen verweisen, um auf die Funktionalität zuzugreifen, was mit Ereignishandlern nicht möglich war. Weitere Informationen zu Befehlen finden Sie unter Befehlsübersicht.

Definieren eines Elements als erforderlich in einer ControlTemplate

In den vorherigen Abschnitten haben Sie erfahren, wie Sie Datenbindungen und Befehle verwenden müssen, damit ein Steuerelement im Code nicht auf Elemente in seiner ControlTemplate verweist. In einigen Fällen ist ein Verweis auf ein Element jedoch nicht vermeidbar. Wenden Sie in einer solchen Situation das TemplatePartAttribute auf das Steuerelement an. Mit diesem Attribut werden Vorlagenautoren über die Typen und Namen der Elemente in der ControlTemplate informiert. Nicht jedes Element in einer ControlTemplate muss in einem TemplatePartAttribute benannt werden. Im Gegenteil: je weniger benannte Elemente, desto besser. Wenn Sie jedoch in Code auf das Element verweisen, müssen Sie das TemplatePartAttribute verwenden.

Weitere Informationen zum Entwerfen von Steuerelementen, die eine ControlTemplate verwenden, finden Sie unter Richtlinien zum Entwerfen formatierbarer Steuerelemente.

Entwerfen für Designer

Führen Sie die folgenden Schritte aus, um eine Unterstützung für benutzerdefinierte WPF-Steuerelemente in Windows Presentation Foundation (WPF)-Designer für Visual Studio zu erhalten (beispielsweise das Bearbeiten von Eigenschaften mit dem Eigenschaftenfenster). Weitere Informationen zur WPF-Designer-Entwicklung finden Sie unter WPF-Designer.

Abhängigkeitseigenschaften

Stellen Sie sicher, dass Sie die CLR-Accessoren get und set implementieren. Dies ist weiter oben unter "Verwenden von Abhängigkeitseigenschaften" beschrieben. Designer können den Wrapper verwenden, um das Vorhandensein einer Abhängigkeitseigenschaft zu erkennen. Genau wie WPF und Clients des Steuerelements auch, müssen diese beim Abrufen oder Festlegen der Eigenschaft jedoch nicht die Accessoren aufrufen.

Angefügte Eigenschaften

Sie sollten angefügte Eigenschaften für benutzerdefinierte Steuerelemente anhand der folgenden Richtlinien implementieren:

  • Verwenden Sie eine public static readonly-DependencyProperty der Form EigenschaftennameProperty, die mithilfe der RegisterAttached-Methode erstellt wurde. Der Eigenschaftenname, der an RegisterAttached übergeben wird, muss mit dem PropertyName übereinstimmen.

  • Implementieren Sie ein publicstatic-CLR-Methodenpaar mit den Namen SetPropertyName und GetPropertyName. Beide Methoden sollten eine von DependencyProperty abgeleitete Klasse als erstes Argument akzeptieren. Die SetPropertyName-Methode akzeptiert auch ein Argument, dessen Typ zum registrierten Datentyp für die Eigenschaft passt. Die GetPropertyName-Methode sollte einen Wert des gleichen Typs zurückgeben. Wenn die SetEigenschaftenname-Methode fehlt, wird die Eigenschaft als schreibgeschützt gekennzeichnet.

  • SetPropertyName und GetPropertyName müssen direkt an die GetValue-Methode bzw. die SetValue-Methode im Zielabhängigkeitsobjekt weiterleiten. Designer können auf die angefügte Eigenschaft zugreifen, indem sie einen Aufruf über den Methodenwrapper durchführen oder indem sie einen direkten Aufruf an das Zielabhängigkeitsobjekt ausführen.

Weitere Informationen zu angefügten Eigenschaften finden Sie unter Übersicht über angefügte Eigenschaften.

Definieren und Verwenden von freigegebenen Ressourcen für das Steuerelement

Sie können das Steuerelement in die Assembly der Anwendung oder in eine separate Assembly einbinden, die in mehreren Anwendungen verwendet werden kann. Der Großteil der in diesem Thema erläuterten Informationen gelten unabhängig von der Methode, die Sie verwenden. Sie müssen jedoch einen Unterschied beachten. Wenn Sie ein Steuerelement in die Assembly einer Anwendung einbinden, können Sie der Datei app.xaml globale Ressourcen hinzufügen. Einer Assembly, die nur Steuerelemente enthält, ist jedoch kein Application-Objekt zugeordnet, sodass keine app.xaml-Datei verfügbar ist.

Wenn eine Anwendung nach einer Ressource sucht, erfolgt die Suche auf drei Ebenen in der folgenden Reihenfolge:

  1. Elementebene: Das System beginnt mit dem Element, das auf die Ressource verweist, darauf folgen die Ressourcen des logischen übergeordneten Elements usw., bis das Stammelement erreicht ist.

  2. Anwendungsebene: Mit dem Application-Objekt definierte Ressourcen.

  3. Designebene: Wörterbücher auf Designebene werden im Unterordner Themes gespeichert. Die Dateien im Ordner Themes entsprechen den Designs. Beispiel: Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml usw. Der Ordner kann auch die Datei generic.xaml enthalten. Die Suche nach einer Ressource auf Designebene erfolgt zunächst in der designspezifischen Datei und dann in der Datei generic.xaml.

Wenn sich das Steuerelement in einer separaten Assembly befindet, speichern Sie globale Ressourcen auf Element- oder Designebene. Beide Methoden haben ihre Vorteile.

Definieren von Ressourcen auf Elementebene

Sie können freigegebene Ressourcen auf Elementebene definieren, indem Sie ein benutzerdefiniertes Ressourcenwörterbuch erstellen und mit dem Ressourcenwörterbuch des Steuerelements zusammenführen. Bei dieser Methode können Sie der Ressourcendatei einen beliebigen Namen zuweisen und sie im gleichen Ordner speichern wie die Steuerelemente. Für Ressourcen auf Elementebene können auch einfache Zeichenfolgen als Schlüssel verwendet werden. Im folgenden Beispiel wird eine LinearGradientBrush-Ressourcendatei mit dem Namen Dictionary1.XAML erstellt.

<ResourceDictionary 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>

</ResourceDictionary>

Nachdem Sie das Wörterbuch definiert haben, müssen Sie es mit dem Ressourcenwörterbuch des Steuerelements zusammenführen. Hierzu können Sie XAML oder Code verwenden.

Im folgenden Beispiel wird ein Ressourcenwörterbuch mit XAML zusammengeführt.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

Der Nachteil bei dieser Vorgehensweise besteht darin, dass jedes Mal, wenn Sie darauf verweisen, ein ResourceDictionary-Objekt erstellt wird. Beispiel: Wenn Ihre Bibliothek zehn benutzerdefinierte Steuerelemente enthält und Sie die freigegebenen Ressourcenwörterbücher für jedes Steuerelement mit XAML zusammenführen, werden zehn identische ResourceDictionary-Objekte erstellt. Sie können dies verhindern, indem Sie eine statische Klasse erstellen, die die Ressourcen in Code zusammenführt und das ResourceDictionary ausgibt.

Im folgenden Beispiel wird eine Klasse erstellt, die ein freigegebenes ResourceDictionary zurückgibt.

internal static class SharedDictionaryManager
{
    internal static ResourceDictionary SharedDictionary
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml", 
                                    System.UriKind.Relative);

                _sharedDictionary = 
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            return _sharedDictionary;
        }
    }

    private static ResourceDictionary _sharedDictionary;
}

Im folgenden Beispiel wird die freigegebene Ressource mit den Ressourcen eines benutzerdefinierten Steuerelements im Konstruktur des Steuerelements zusammengeführt, bevor InitilizeComponent aufgerufen wird. Da das SharedDictionaryManager.SharedDictionary eine statische Eigenschaft ist, wird das ResourceDictionary nur einmal erstellt. Da das Ressourcenwörterbuch vor dem Aufruf von InitializeComponent zusammengeführt wurde, sind die Ressourcen für das Steuerelement in der XAML-Datei zugänglich.

public NumericUpDown()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();

}

Definieren von Ressourcen auf Designebene

WPF ermöglicht Ihnen, Ressourcen für andere Windows-Designs zu erstellen. Als Steuerelementautor können Sie eine Ressource für ein bestimmtes Design definieren, um die Darstellung des Steuerelements an das jeweils verwendete Design anzupassen. Beispiel: Die Button-Darstellung im Design Windows - klassisch (das Standardesign in Windows 2000) unterscheidet sich von der Button-Darstellung im Windows XP-Design, da für Button für jedes Design eine andere ControlTemplate verwendet wird.  

Designspezifische Ressourcen werden in einem Ressourcenwörterbuch mit einem entsprechenden Dateinamen gespeichert. Diese Dateien müssen sich im Verzeichnis Themes befinden, einem Unterverzeichnis des Ordners, der das Steuerelement enthält. In der folgenden Tabelle werden die Ressourcenwörterbuchdateien und die den einzelnen Dateien zugeordneten Designs aufgeführt:

Name der Ressourcenwörterbuchdatei

Windows-Design

Classic.xaml

Klassische Windows 9x/2000-Darstellung unter Windows XP

Luna.NormalColor.xaml

Blaues Design unter Windows XP (Standard)

Luna.Homestead.xaml

Olivgrünes Design unter Windows XP

Luna.Metallic.xaml

Silberdesign unter Windows XP

Royale.NormalColor.xaml

Standarddesign von Windows XP Media Center Edition

Aero.NormalColor.xaml

Standarddesign unter Windows Vista

Sie müssen nicht für jedes Design eine Ressource definieren. Wenn für ein bestimmtes Design keine Ressource definiert ist, verwendet das Steuerelement die allgemeine Ressource in der Ressourcenwörterbuchdatei generic.xaml, die sich im gleichen Ordner befindet wie die designspezifischen Ressourcenwörterbuchdateien. Obwohl generic.xaml keinem bestimmten Windows-Design entspricht, handelt es sich doch um ein Wörterbuch auf Designebene.

Das Beispiel für benutzerdefiniertes NumericUpDown-Steuerelement mit Unterstützung von Designs und Benutzeroberflächenautomatisierung enthält zwei Ressourcenwörterbücher für das NumericUpDown-Steuerelement: eines in der Datei generic.xaml und eines in der Datei Luna.NormalColor.xaml. Führen Sie die Anwendung aus, und wechseln Sie zwischen dem Silberdesign für Windows XP und einem anderen Design, um den Unterschied zwischen den beiden Steuerelementvorlagen zu sehen. (Unter Windows Vista können Sie die Datei Luna.NormalColor.xaml in Aero.NormalColor.xaml umbenennen und zwischen zwei Designs wechseln, z. B. zwischen Windows - klassisch und dem Standarddesign für Windows Vista.)

Wenn Sie eine ControlTemplate in eine der designspezifischen Ressourcenwörterbuchdateien einfügen, müssen Sie einen statischen Konstruktor für das Steuerelement erstellen und die OverrideMetadata(Type, PropertyMetadata)-Methode für den DefaultStyleKey aufrufen, wie im folgenden Beispiel gezeigt.

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

Definieren und Angeben von Schlüsseln für Designressourcen

Wenn Sie eine Ressource auf Elementebene definieren, können Sie ihr eine Zeichenfolge als Schlüssel zuweisen und über diese Zeichenfolge auf die Ressource zugreifen. Wenn Sie auf Designebene eine Ressource definieren, müssen Sie einen ComponentResourceKey als Schlüssel verwenden. Im folgenden Beispiel wird eine Ressource in generic.xaml definiert.

Das folgende Beispiel verweist auf die Ressource, indem ComponentResourceKey als Schlüssel angegeben wird.

Angeben des Speicherorts von Designressourcen

Um die Ressourcen für ein Steuerelement finden zu können, muss die Hostanwendung über die Information verfügen, dass die Assembly steuerelementspezifische Ressourcen enthält. Fügen Sie hierzu der Assembly, die das Steuerelement enthält, das ThemeInfoAttribute hinzu. Das ThemeInfoAttribute weist eine GenericDictionaryLocation-Eigenschaft zur Angabe des Speicherorts allgemeiner Ressourcen sowie eine ThemeDictionaryLocation-Eigenschaft zur Angabe des Speicherorts designspezifischer Ressourcen.

Im folgenden Beispiel werden die GenericDictionaryLocation-Eigenschaft und die ThemeDictionaryLocation-Eigenschaft auf SourceAssembly gesetzt, um anzugeben, dass sich die allgemeinen und die designspezifischen Ressourcen in derselben Assembly befinden wie das Steuerelement.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, 
           ResourceDictionaryLocation.SourceAssembly)]

Erben von UserControl im Vergleich zur Verwendung einer ControlTemplate

In mehreren Beispielen werden unterschiedliche Methoden zum Schreiben und Packen des NumericUpDown-Steuerelements veranschaulicht. In Beispiel zu NumericUpDown UserControl mit DependencyProperty und RoutedEvent erbt NumericUpDown von UserControl; in Beispiel für benutzerdefiniertes NumericUpDown-Steuerelement mit Unterstützung von Designs und Benutzeroberflächenautomatisierung erbt NumericUpDown von Control und verwendet eine ControlTemplate. In diesem Abschnitt werden einige der Unterschiede zwischen diesen beiden Methoden kurz erläutert, und Sie erfahren, warum das Steuerelement, das eine ControlTemplate verwendet, vielseitiger erweiterbar ist.

Der erste große Unterschied besteht darin, dass das NumericUpDown-Steuerelement, das von UserControl erbt, keine ControlTemplate verwendet, im Gegensatz zu dem Steuerelement, das direkt von Control erbt. Das folgende Beispiel zeigt die XAML-Methode für das Steuerelement, das von UserControl erbt. Wie Sie sehen, stimmt die XAML-Methode weitestgehend mit den möglichen Ergebnissen überein, die Sie erhalten, wenn Sie eine Anwendung erstellen und mit einem Window oder einer Page beginnen.

<!--XAML for NumericUpDown that inherits from UserControl.-->
<UserControl x:Class="MyUserControl.NumericUpDown"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyUserControl">
    <Grid Margin="3">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
                Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

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

        </Border>

        <RepeatButton Name="upButton" Click="upButton_Click" 
                      Grid.Column="1" Grid.Row="0">Up</RepeatButton>

        <RepeatButton Name="downButton" Click="downButton_Click" 
                      Grid.Column="1" Grid.Row="1">Down</RepeatButton>

    </Grid>
</UserControl>

Das folgende Beispiel zeigt die ControlTemplate-Methode für das Steuerelement, das von Control erbt.Beachten Sie, dass die ControlTemplate bis auf einige kleine Unterschiede in der Syntax mit der XAML-Methode im UserControl übereinstimmt.

<!--ControlTemplate for NumericUpDown that inherits from
    Control.-->
<Style TargetType="{x:Type local:NumericUpDown}">
  <Setter Property="HorizontalAlignment" Value="Center"/>
  <Setter Property="VerticalAlignment" Value="Center"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:NumericUpDown}">
        <Grid Margin="3">
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>

          <Border BorderThickness="1" BorderBrush="Gray" 
                  Margin="2" Grid.RowSpan="2" 
                  VerticalAlignment="Center" HorizontalAlignment="Stretch">

            <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value}" 
                       Width="60" TextAlignment="Right" Padding="5"/>
          </Border>

          <RepeatButton Command="{x:Static local:NumericUpDown.IncreaseCommand}"  
                        Grid.Column="1" Grid.Row="0">Up</RepeatButton>

          <RepeatButton Command="{x:Static local:NumericUpDown.DecreaseCommand}"
                        Grid.Column="1" Grid.Row="1">Down</RepeatButton>

        </Grid>

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

Der größte Unterschied zwischen den beiden oben beschriebenen Beispielen besteht darin, dass die Darstellung des Steuerelements, das die ControlTemplate verwendet, im Gegensatz zu der Darstellung des Steuerelements, das von UserControl erbt, anpassbar ist. Wenn NumericUpDown von UserControl erbt, hat ein Anwendungsentwickler keine Möglichkeit, die Darstellung des Steuerelements zu ändern. Obwohl NumericUPDown eine ControlTemplate-Eigenschaft aufweist (weil UserControl von Control erbt), wird zur Laufzeit eine Ausnahme ausgelöst, wenn versucht wird, diese Eigenschaft einzustellen. Bei dem NumericUpDown-Steuerelement, das von Control erbt, kann der Anwendungsentwickler eine neue ControlTemplate für das Steuerelement erstellen, z. B. eine ControlTemplate, die die Schaltflächen links und rechts neben den TextBlock statt darüber und darunter platziert.

Der Unterschied zwischen den zwei Ansätzen wird in der Syntax der oben stehenden Beispiele offensichtlich. Das Steuerelement, das eine ControlTemplate verwendet, legt die Template-Eigenschaft in einem Style für NumericUpDown fest. Dies ist die übliche Methode zur Erstellung von Steuerelementvorlagen. Indem Sie die Template-Eigenschaft in einem Stil festlegen, geben Sie an, dass alle Instanzen des Steuerelements diese ControlTemplate verwenden. Anwendungsentwickler können die Template-Eigenschaft eines NumericcUpDown-Steuerelements ändern, um seine Darstellung anzupassen. Im Gegensatz dazu füllt die XAML-Methode für das Steuerelement, das von UserControl erbt, die Content-Eigenschaft von NumericUpDown (<UserControl.Content> ist in XAML impliziert). Wenn ein Anwendungsentwickler die Content-Eigenschaft nicht ändern kann, ist NumericUpDown nicht verwendbar.

Ein weiterer Unterschied zwischen den Beispielen besteht in der Reaktion der Steuerelemente auf die Schaltflächen Nach oben und Nach unten. Das Steuerelement, das von UserControl erbt, behandelt das Klickereignis, und das Steuerelement, das eine ControlTemplate verwendet, implementiert Befehle und erstellt eine Bindung an die Befehle in der ControlTemplate.Ein Anwendungsentwickler, der eine neue ControlTemplate für das NumericUpDown-Steuerelement erstellt, kann ebenfalls eine Bindung an die Befehle erstellen, sodass die Funktionalität des Steuerelements erhalten bleibt. Wenn die ControlTemplate das Klickereignis behandeln würde, statt eine Bindung an die Befehle zu erstellen, müsste der Anwendungsentwickler bei der Erstellung einer neuen ControlTemplate Ereignishandler implementieren, was die Kapselung von NumericUpDown ungültig machen würde.

Ein weiterer Unterschied besteht in der Syntax der Bindung zwischen der Text-Eigenschaft von TextBlock und der Value-Eigenschaft. Im Falle von UserControl gibt die Bindung an, dass RelativeSource das übergeordnete NumericUpDown-Steuerelement ist, das an die Value-Eigenschaft gebunden ist. Im Falle von ControlTemplate ist RelativeSource das Steuerelement, zu dem die Vorlage gehört. Das Ergebnis ist dasselbe, Sie sollten jedoch beachten, dass die Bindungssyntax in den Beispielen unterschiedlich ist.

In Beispiel für benutzerdefiniertes NumericUpDown-Steuerelement mit Unterstützung von Designs und Benutzeroberflächenautomatisierung befindet sich das NumericUpDown-Steuerelement in einer separaten Assembly und definiert und verwendet Ressourcen auf Designebene. In Beispiel zu NumericUpDown UserControl mit DependencyProperty und RoutedEvent befindet sich das NumericUpDown-Steuerelement in der Assembly der Anwendung und definiert und verwendet keine Ressourcen auf Designebene.

Beispiel für benutzerdefiniertes NumericUpDown-Steuerelement mit Unterstützung von Designs und Benutzeroberflächenautomatisierung zeigt, wie Sie einen AutomationPeer für das NumericUpDown-Steuerelement erstellen. Weitere Informationen zur Unterstützung von UI-Automatisierung für benutzerdefinierte Steuerelemente finden Sie unter Benutzeroberflächenautomatisierung eines benutzerdefinierten WPF-Steuerelements.

Siehe auch

Konzepte

Paket-URIs in Windows Presentation Foundation

Weitere Ressourcen

WPF-Designer

Anpassung von Steuerelementen

Beispiele für das Anpassen von Steuerelementen