Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die Erweiterbarkeit des Windows Presentation Foundation (WPF)-Steuerelementmodells verringert erheblich die Notwendigkeit, ein neues Steuerelement zu erstellen. In bestimmten Fällen müssen Sie jedoch möglicherweise noch ein benutzerdefiniertes Steuerelement erstellen. In diesem Thema werden die Features erläutert, die Ihre Notwendigkeit minimieren, ein benutzerdefiniertes Steuerelement und die verschiedenen Steuerelementerstellungsmodelle in Windows Presentation Foundation (WPF) zu erstellen. In diesem Thema wird auch das Erstellen eines neuen Steuerelements veranschaulicht.
Alternativen zur Erstellung eines neuen Steuerelements
Wenn Sie eine angepasste Benutzeroberfläche aus einem vorhandenen Steuerelement abrufen möchten, waren Sie auf das Ändern der Standardeigenschaften des Steuerelements beschränkt, z. B. Hintergrundfarbe, Rahmenbreite und Schriftgrad. Wenn Sie das Erscheinungsbild oder Verhalten eines Steuerelements über diese vordefinierten Parameter hinaus erweitern möchten, müssen Sie normalerweise ein neues Steuerelement erstellen, indem Sie von einem vorhandenen Steuerelement erben und die methode außer Kraft setzen, die für das Zeichnen des Steuerelements verantwortlich ist. Obwohl dies weiterhin eine Option ist, können Sie mit WPF vorhandene Steuerelemente mithilfe des umfangreichen Inhaltsmodells, der Formatvorlagen, Vorlagen und Trigger anpassen. Die folgende Liste enthält Beispiele dafür, wie diese Features verwendet werden können, um benutzerdefinierte und konsistente Oberflächen zu erstellen, ohne ein neues Steuerelement erstellen zu müssen.
Umfangreicher Inhalt Viele der standardmäßigen WPF-Steuerelemente unterstützen umfangreiche Inhalte. Beispielsweise ist die Inhaltseigenschaft eines Button vom Typ Object, sodass theoretisch alles auf einem Button angezeigt werden kann. Damit eine Schaltfläche ein Bild und Text anzeigt, können Sie ein Bild und ein TextBlock zu einer StackPanel hinzufügen und der StackPanel der Content-Eigenschaft zuweisen. Da die Steuerelemente visuelle WPF-Elemente und beliebige Daten anzeigen können, ist es weniger erforderlich, ein neues Steuerelement zu erstellen oder ein vorhandenes Steuerelement zu ändern, um eine komplexe Visualisierung zu unterstützen. Weitere Informationen zum Inhaltsmodell für Button und andere Inhaltsmodelle in WPF finden Sie unter WPF-Inhaltsmodell.
Stile. A Style ist eine Auflistung von Werten, die Eigenschaften für ein Steuerelement darstellen. Mithilfe von Formatvorlagen können Sie eine wiederverwendbare Darstellung eines gewünschten Steuerelements und Verhaltens erstellen, ohne ein neues Steuerelement zu schreiben. Angenommen, Sie möchten, dass alle Ihre TextBlock Steuerelemente eine rote Arial-Schriftart mit einer Schriftgröße von 14 haben. Sie können eine Formatvorlage als Ressource erstellen und die passenden Eigenschaften entsprechend festlegen. Dann hat jedes TextBlock , was Sie zu Ihrer Anwendung hinzufügen, das gleiche Erscheinungsbild.
Datenvorlagen. Mithilfe eines DataTemplate Steuerelements können Sie anpassen, wie Daten in einem Steuerelement angezeigt werden. Beispielsweise kann eine DataTemplate verwendet werden, um anzugeben, wie Daten in einer ListBox dargestellt werden. Ein Beispiel hierfür finden Sie unter "Übersicht über das Data Templating". Neben der Anpassung der Darstellung von Daten kann eine DataTemplate Ui-Elemente enthalten, die Ihnen in benutzerdefinierten UIs eine menge Flexibilität bieten. Beispielsweise können Sie mit einem DataTemplate ein ComboBox erstellen, in dem jedes Element ein Kontrollkästchen enthält.
Steuerungsvorlagen. Viele Steuerelemente in WPF verwenden eine ControlTemplate , um die Struktur und Darstellung des Steuerelements zu definieren, wodurch die Darstellung eines Steuerelements von der Funktionalität des Steuerelements getrennt wird. Sie können das Erscheinungsbild eines Steuerelements drastisch ändern, indem Sie das ControlTemplateSteuerelement neu definieren. Angenommen, Sie möchten ein Steuerelement, das wie eine Ampel aussieht. Dieses Steuerelement verfügt über eine einfache Benutzeroberfläche und Funktionalität. Das Steuerelement besteht aus drei Kreisen, von denen jeweils nur eines beleuchtet werden kann. Nach einiger Überlegung stellen Sie möglicherweise fest, dass eine RadioButton die Funktionalität bietet, dass jeweils nur eine Option ausgewählt werden kann, aber das Standardaussehen von RadioButton sieht überhaupt nicht aus wie die Lichter einer Ampel. Da die RadioButton Steuerelementvorlage zum Definieren ihrer Darstellung eine Steuerelementvorlage verwendet, ist es einfach, die ControlTemplate Anforderungen des Steuerelements anzupassen und Optionsfelder zu verwenden, um ihre Stopplichter zu gestalten.
Hinweis
Obwohl ein RadioButton ein DataTemplate verwenden kann, ist ein DataTemplate in diesem Beispiel nicht ausreichend. Das DataTemplate Definiert die Darstellung des Inhalts eines Steuerelements. Bei einem RadioButton wird der Inhalt rechts vom Kreis angezeigt, der angibt, ob das RadioButton ausgewählt ist. Im Beispiel der Ampel muss das Optionsfeld nur ein Kreis sein, der leuchten kann. Da die Darstellungsanforderung für die Ampel so anders ist als die Standarddarstellung der RadioButton, ist es notwendig, die ControlTemplate neu zu definieren. Im Allgemeinen wird eine DataTemplate zum Definieren des Inhalts (oder der Daten) eines Steuerelements verwendet, und eine ControlTemplate wird verwendet, um zu definieren, wie ein Steuerelement strukturiert ist.
Auslöser. Mithilfe Trigger eines Steuerelements können Sie das Erscheinungsbild und Verhalten eines Steuerelements dynamisch ändern, ohne ein neues Steuerelement zu erstellen. Angenommen, Sie haben mehrere ListBox-Steuerelemente in Ihrer Anwendung und möchten, dass die Elemente in jedem ListBox fett und rot sind, wenn sie ausgewählt werden. Möglicherweise erstellen Sie zunächst eine Klasse, die von ListBox erbt und die OnSelectionChanged Methode überschreibt, um das Erscheinungsbild des ausgewählten Elements zu ändern, aber ein besserer Ansatz besteht darin, einem Stil eines ListBoxItem einen Trigger hinzuzufügen, um das Erscheinungsbild des ausgewählten Elements zu ändern. Mit einem Trigger können Sie Eigenschaftswerte ändern oder Aktionen basierend auf dem Wert einer Eigenschaft ausführen. Mit EventTrigger dieser Option können Sie Aktionen ausführen, wenn ein Ereignis auftritt.
Weitere Informationen zu Stilgestaltung, Vorlagen und Triggern finden Sie unter Stilgestaltung und Vorlagen.
Wenn Ihr Steuerelement die Funktionalität eines vorhandenen Steuerelements widerspiegelt, das Steuerelement jedoch anders aussehen soll, sollten Sie zunächst überlegen, ob Sie eine der in diesem Abschnitt beschriebenen Methoden verwenden können, um die Darstellung des vorhandenen Steuerelements zu ändern.
Modelle für die Steuerelementerstellung
Das umfangreiche Inhaltsmodell, Formatvorlagen, Vorlagen und Trigger minimieren die Notwendigkeit, ein neues Steuerelement zu erstellen. Wenn Sie jedoch ein neues Steuerelement erstellen müssen, ist es wichtig, die verschiedenen Steuerelementerstellungsmodelle in WPF zu verstehen. WPF bietet drei allgemeine Modelle zum Erstellen eines Steuerelements, von denen jeder eine andere Gruppe von Features und Flexibilität bietet. Die Basisklassen für die drei Modelle sind UserControl, Controlund FrameworkElement.
Ableiten von UserControl
Die einfachste Möglichkeit zum Erstellen eines Steuerelements in WPF ist das Ableiten von UserControl. Wenn Sie ein Steuerelement erstellen, von UserControldem geerbt wird, fügen Sie der UserControlKomponente vorhandene Komponenten hinzu, benennen sie die Komponenten, und verweisen Sie in XAML auf Ereignishandler. Anschließend können Sie auf die benannten Elemente verweisen und die Ereignishandler im Code definieren. Dieses Entwicklungsmodell ähnelt dem Modell, das für die Anwendungsentwicklung in WPF verwendet wird.
Wenn es ordnungsgemäß erstellt wird, kann ein UserControl die Vorteile von umfangreichen Inhalten, Stilen und Triggern nutzen. Wenn Ihr Steuerelement jedoch von erbt UserControl, können Personen, die Ihr Steuerelement verwenden, keine Darstellung verwenden DataTemplate oder ControlTemplate anpassen. Es ist erforderlich, von der Control Klasse oder einer der abgeleiteten Klassen (außer UserControl) abzuleiten, 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 Punkte zutreffen:
Sie möchten Ihr Steuerelement ähnlich wie beim Erstellen einer Anwendung erstellen.
Ihr Steuerelement besteht nur aus vorhandenen Komponenten.
Sie müssen keine komplexen Anpassungen unterstützen.
Ableiten von Kontrolle
Die von der Control Klasse abgeleitete Methode ist das Modell, das von den meisten existierenden WPF-Steuerelementen genutzt wird. Wenn Sie ein Steuerelement erstellen, das von der Control Klasse erbt, definieren Sie dessen Darstellung mithilfe von Vorlagen. Dabei trennen Sie die Betriebslogik von der visuellen Darstellung. Sie können auch die Entkoppelung der Benutzeroberfläche und der Logik sicherstellen, indem Sie Befehle und Bindungen anstelle von Ereignissen verwenden und das ControlTemplate Verweisen auf Elemente nach Möglichkeit vermeiden. Wenn die Benutzeroberfläche und die Logik des Steuerelements ordnungsgemäß entkoppelt sind, kann ein Benutzer des Steuerelements die Darstellung des Steuerelements ControlTemplate neu definieren, um seine Darstellung anzupassen. Obwohl das Erstellen eines benutzerdefinierten Control nicht so einfach wie das eines UserControl ist, bietet ein benutzerdefiniertes Control die größte Flexibilität.
Vorteile der Ableitung von Steuerungen
Erwägen Sie, von Control abzuleiten, anstatt die UserControl Klasse zu verwenden, wenn eine der folgenden Bedingungen zutrifft:
Sie möchten, dass das Erscheinungsbild Ihres Steuerelements über die ControlTemplate.
Sie möchten, dass Ihr Steuerelement unterschiedliche Themen unterstützt.
Ableiten von FrameworkElement
Steuerelemente, die von UserControl oder Control abgeleitet sind, beruhen auf der Zusammensetzung vorhandener Elemente. In vielen Szenarien ist dies eine akzeptable Lösung, weil jedes Objekt, das von FrameworkElement geerbt wird, in einem ControlTemplate sein kann. Es gibt jedoch Situationen, in denen die Darstellung eines Steuerelements mehr erfordert als die Funktionalität der einfachen Elementkomposition. Bei diesen Szenarien ist es die richtige Wahl, eine Komponente auf der Grundlage von FrameworkElement zu erstellen.
Es gibt zwei Standardmethoden zum Erstellen von FrameworkElement-basierten Komponenten: direktes Rendering und benutzerdefinierte Elemente-Zusammensetzung. Das direkte Rendering umfasst das Überschreiben der OnRender-Methode von FrameworkElement und das Bereitstellen von DrawingContext-Operationen, die die visuellen Komponenten explizit definieren. Dies ist die Methode, die von Image und Border. Die Komposition benutzerdefinierter Elemente umfasst die Verwendung von Objekten vom Typ Visual zur Gestaltung des Aussehens Ihrer Komponente. Ein Beispiel finden Sie unter Using DrawingVisual Objects. Track ist ein Beispiel für ein Steuerelement in WPF, das eine benutzerdefinierte Elementkomposition verwendet. Es ist auch möglich, direktes Rendering und benutzerdefinierte Elementkomposition im selben Steuerelement zu kombinieren.
Vorteile der Ableitung von FrameworkElement
Erwägen Sie eine Ableitung von FrameworkElement, wenn eine der folgenden Bedingungen zutrifft.
Sie möchten präzise Kontrolle über das Erscheinungsbild Ihres Steuerelements haben, das über die von der einfachen Elementkomposition bereitgestellten Elemente hinausgeht.
Sie möchten die Darstellung Ihres Steuerelements definieren, indem Sie ihre eigene Renderlogik definieren.
Sie möchten vorhandene Elemente auf neuen Arten zusammenstellen, die über das hinausgehen, was mit UserControl und Control möglich ist.
Grundlagen der Steuerungserstellung
Wie bereits erwähnt, ist eines der leistungsstärksten Features von WPF die Möglichkeit, über das Festlegen grundlegender Eigenschaften eines Steuerelements hinauszugehen, um sein Aussehen und Verhalten zu ändern, aber dennoch kein benutzerdefiniertes Steuerelement erstellen muss. Die Formatierungs-, Datenbindungs- und Triggerfunktionen werden vom WPF-Eigenschaftensystem und WPF-Ereignissystem bereitgestellt. In den folgenden Abschnitten werden einige Methoden beschrieben, die Sie befolgen sollten, unabhängig vom Modell, das Sie zum Erstellen des benutzerdefinierten Steuerelements verwenden, damit Benutzer des benutzerdefinierten Steuerelements diese Features genauso wie für ein Steuerelement verwenden können, das in WPF enthalten ist.
Verwenden von Abhängigkeitseigenschaften
Wenn es sich bei einer Eigenschaft um eine Abhängigkeitseigenschaft handelt, kann Folgendes ausgeführt werden:
Legen Sie die Eigenschaft in einer Formatvorlage 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 Ihres Steuerelements eine dieser Funktionen unterstützt, sollten Sie sie als Abhängigkeitseigenschaft implementieren. Im folgenden Beispiel wird eine Abhängigkeitseigenschaft definiert, die wie folgt benannt wird Value
:
Definieren Sie einen DependencyProperty Bezeichner mit dem Namen
ValueProperty
alspublic
static
readonly
Feld.Registrieren Sie den Eigenschaftennamen beim Eigenschaftensystem, indem Sie DependencyProperty.Register aufrufen, um Folgendes festzulegen:
Der Name der Eigenschaft.
Den Typ der Eigenschaft.
Die Art, die das Eigentum besitzt.
Die Metadaten für die Eigenschaft. Die Metadaten enthalten den Standardwert der Eigenschaft a CoerceValueCallback und a PropertyChangedCallback.
Definieren Sie eine CLR-Wrappereigenschaft namens
Value
, die denselben Namen wie die Abhängigkeitseigenschaft aufweist, indem Sie die Zugriffsmethodenget
undset
implementieren. Beachten Sie, dass die Accessorenget
undset
nur GetValue bzw. SetValue aufrufen. Es wird empfohlen, dass die Accessoren von Abhängigkeitseigenschaften keine zusätzliche Logik enthalten, da Clients und WPF die Accessoren umgehen und direkt aufrufen GetValueSetValue können. Wenn beispielsweise eine Eigenschaft an eine Datenquelle gebunden ist, wird der Accessor der Eigenschaftset
nicht aufgerufen. Anstatt zusätzliche Logik für die Get- und Set-Accessoren hinzuzufügen, verwenden Sie die ValidateValueCallback, CoerceValueCallback und PropertyChangedCallback-Stellvertretungen, um auf den Wert zu reagieren oder zu überprüfen, wenn er sich ändert. Weitere Informationen zu diesen Rückrufen finden Sie unter Dependency Property Callbacks and Validation.Definieren Sie eine Methode für CoerceValueCallback, die
CoerceValue
genannt wird.CoerceValue
stellt sicher, dassValue
größer oder gleichMinValue
und kleiner oder gleichMaxValue
ist.Definieren Sie eine Methode für den PropertyChangedCallback, der
OnValueChanged
genannt ist.OnValueChanged
erstellt ein RoutedPropertyChangedEventArgs<T> Objekt und bereitet vor, dasValueChanged
Routed Event auszulösen. 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);
}
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))
''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
Get
Return CDec(GetValue(ValueProperty))
End Get
Set(ByVal value As Decimal)
SetValue(ValueProperty, value)
End Set
End Property
Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
Dim newValue As Decimal = CDec(value)
Dim control As NumericUpDown = CType(element, NumericUpDown)
newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))
Return newValue
End Function
Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
Dim control As NumericUpDown = CType(obj, NumericUpDown)
Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
control.OnValueChanged(e)
End Sub
Weitere Informationen finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften.
Verwenden von Routingereignissen
Ebenso wie Abhängigkeitseigenschaften den Begriff der CLR-Eigenschaften mit zusätzlicher Funktionalität erweitern, erweitern Routingereignisse den Begriff der standardmäßigen CLR-Ereignisse. Wenn Sie ein neues WPF-Steuerelement erstellen, empfiehlt es sich auch, Das Ereignis als Routingereignis zu implementieren, da ein Routingereignis das folgende Verhalten unterstützt:
Ereignisse können auf einem übergeordneten Element mehrerer Steuerelemente behandelt werden. Wenn ein Ereignis ein Bubbling-Ereignis ist, kann ein einzelnes übergeordnetes Element in der Elementstruktur das Ereignis abonnieren. Anschließend können Anwendungsautoren einen Handler verwenden, um auf das Ereignis mehrerer Steuerelemente zu reagieren. Wenn Ihr Steuerelement beispielsweise Teil jedes Elements in einem ListBox Element ist (weil es in einem DataTemplate Element enthalten ist), kann der Anwendungsentwickler den Ereignishandler für das Ereignis Ihres Steuerelements direkt auf dem ListBox definieren. Wenn das Ereignis auf einem der Steuerelemente auftritt, wird der Ereignishandler aufgerufen.
Routingereignisse können in einem EventSetter verwendet werden, mit dem Anwendungsentwickler den Handler eines Ereignisses innerhalb eines Stils angeben können.
Routingereignisse können in einem EventTriggerXaml-Code verwendet werden, der zum Animieren von Eigenschaften nützlich ist. Weitere Informationen finden Sie unter Übersicht über Animationen.
Im folgenden Beispiel wird ein Routingereignis definiert, indem Folgendes ausgeführt wird:
Definieren Sie einen RoutedEvent Bezeichner namens
ValueChangedEvent
alspublic
static
readonly
-Feld.Registrieren Sie das Routingereignis, indem Sie die EventManager.RegisterRoutedEvent Methode aufrufen. Das Beispiel gibt die folgenden Informationen an, wenn RegisterRoutedEvent aufgerufen wird:
Der Name des Ereignisses lautet
ValueChanged
.Die Routingstrategie bedeutet Bubble, dass ein Ereignishandler für die Quelle (das Objekt, das das Ereignis auslöst) zuerst aufgerufen wird, und dann Ereignishandler für die übergeordneten Elemente der Quelle nacheinander aufgerufen werden, beginnend mit dem Ereignishandler für das nächstgelegene übergeordnete Element.
Der Typ des Ereignishandlers wird RoutedPropertyChangedEventHandler<T>mit einem Decimal Typ erstellt.
Der zugehörige Typ des Ereignisses ist
NumericUpDown
.
Deklarieren Sie ein öffentliches Ereignis namens
ValueChanged
und fügen Sie Ereignis-Accessordeklarationen hinzu. Das Beispiel ruft in deradd
-Accessordeklaration AddHandler und in derremove
-Accessordeklaration RemoveHandler auf, um die WPF-Ereignisdienste zu verwenden.Erstellen Sie eine geschützte, virtuelle Methode mit dem Namen
OnValueChanged
, die dasValueChanged
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);
}
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))
''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
MyBase.AddHandler(ValueChangedEvent, value)
End AddHandler
RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
MyBase.RemoveHandler(ValueChangedEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
End RaiseEvent
End Event
''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
MyBase.RaiseEvent(args)
End Sub
Weitere Informationen finden Sie unter "Übersicht über Routingereignisse" und "Erstellen eines benutzerdefinierten Routingereignisses".
Bindung verwenden
Um die Benutzeroberfläche ihres Steuerelements von seiner Logik zu entkoppeln, sollten Sie die Datenbindung in Betracht ziehen. Dies ist besonders wichtig, wenn Sie das Erscheinungsbild Ihres Steuerelements mit einem ControlTemplate definieren. Wenn Sie datenbindung verwenden, können Sie möglicherweise die Notwendigkeit vermeiden, auf bestimmte Teile der Benutzeroberfläche aus dem Code zu verweisen. Es ist eine gute Idee, Verweise auf Elemente zu vermeiden, die sich in ControlTemplate befinden, denn wenn der Code auf Elemente verweist, die sich in ControlTemplate befinden und ControlTemplate geändert wird, muss das referenzierte Element in das neue ControlTemplate aufgenommen werden.
Im folgenden Beispiel wird das TextBlockNumericUpDown
Steuerelement aktualisiert, ihm ein Name zugewiesen und das Textfeld anhand des Namens im Code referenziert.
<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();
}
Private Sub UpdateTextBlock()
valueText.Text = Value.ToString()
End Sub
Im folgenden Beispiel wird die Bindung verwendet, um dasselbe 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 Data Binding Overview.
Design für Designer
Befolgen Sie die folgenden Richtlinien, um Unterstützung für benutzerdefinierte WPF-Steuerelemente im WPF-Designer für Visual Studio zu erhalten (z. B. die Eigenschaftenbearbeitung mit dem Eigenschaftenfenster). Weitere Informationen zur Entwicklung für den WPF-Designer finden Sie unter Design XAML in Visual Studio.
Abhängigkeitseigenschaften
Stellen Sie sicher, dass Sie CLR get
und set
Accessoren wie weiter oben beschrieben in "Verwenden von Abhängigkeitseigenschaften" implementieren. Designer können den Wrapper verwenden, um das Vorhandensein einer Abhängigkeitseigenschaft zu erkennen, aber sie, z. B. WPF und Clients des Steuerelements, müssen die Accessoren beim Abrufen oder Festlegen der Eigenschaft nicht aufrufen.
Angefügte Eigenschaften
Sie sollten angefügte Eigenschaften für benutzerdefinierte Steuerelemente mithilfe der folgenden Richtlinien implementieren:
Weisen Sie einen
public
static
DependencyPropertyreadonly
der Form PropertyNameProperty
auf, die mit der RegisterAttached Methode erstellt wurde. Der an RegisterAttached übergebene Eigenschaftsname muss mit PropertyName übereinstimmen.Implementieren Sie ein Paar von
public
static
CLR-Methoden namensSet
PropertyName undGet
PropertyName. Beide Methoden sollten als erstes Argument eine von DependencyProperty abgeleitete Klasse akzeptieren. DieSet
PropertyName-Methode akzeptiert auch ein Argument, dessen Typ dem registrierten Datentyp für die Eigenschaft entspricht. DieGet
PropertyName-Methode sollte einen Wert desselben Typs zurückgeben. Wenn dieSet
PropertyName-Methode fehlt, wird die Eigenschaft als nur-lesbar markiert.Set
PropertyName undGet
PropertyName müssen direkt an die jeweiligen Methoden beim Ziel-Abhängigkeitsobjekt weitergeleitet werden. Designer können auf die angefügte Eigenschaft zugreifen, indem Sie den Methodenwrapper aufrufen oder einen direkten Aufruf des Zielabhängigkeitsobjekts tätigen.
Weitere Informationen zu angefügten Eigenschaften finden Sie unter "Übersicht über angefügte Eigenschaften".
Definieren und Verwenden freigegebener Ressourcen
Sie können Ihr Steuerelement in dieselbe Assembly wie Ihre Anwendung einschließen, oder Sie können das Steuerelement in einer separaten Assembly verpacken, die in mehreren Anwendungen verwendet werden kann. Die in diesem Thema behandelten Informationen gelten größtenteils unabhängig von der verwendeten Methode. Es gibt jedoch einen Unterschied, den man beachten sollte. Wenn Sie ein Steuerelement in derselben Assembly wie eine Anwendung einfügen, können Sie der Datei "App.xaml" globale Ressourcen hinzufügen. Aber eine Assembly, die nur Steuerelemente enthält, verfügt nicht über ein Application Objekt, das ihr zugeordnet ist, sodass eine App.xaml-Datei nicht verfügbar ist.
Wenn eine Anwendung nach einer Ressource sucht, werden in der folgenden Reihenfolge drei Ebenen untersucht:
Die Elementebene.
Das System beginnt mit dem Element, das auf die Ressource verweist, und durchsucht dann Ressourcen des logischen übergeordneten Elements und so weiter, bis das Stammelement erreicht ist.
Die Anwendungsebene.
Vom Application Objekt definierte Ressourcen.
Die Designebene.
Wörterbücher auf Designebene werden in einem Unterordner mit dem Namen "Designs" gespeichert. Die Dateien im Ordner "Designs" entsprechen Designs. Beispielsweise haben Sie möglicherweise Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml usw. Sie können auch eine Datei mit dem Namen "generic.xaml" haben. Wenn das System auf der Designebene nach einer Ressource sucht, sucht es zuerst in der designspezifischen Datei und dann in "generic.xaml".
Wenn sich Ihr Steuerelement in einer Assembly befindet, die von der Anwendung getrennt ist, müssen Sie die globalen Ressourcen auf Elementebene oder auf Themenebene platzieren. 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 Ressourcenverzeichnis Ihres Steuerelements zusammenführen. Wenn Sie diese Methode verwenden, können Sie ihre Ressourcendatei beliebig benennen, und sie kann sich im selben Ordner wie Ihre Steuerelemente befinden. Ressourcen auf Elementebene können auch einfache Zeichenfolgen als Schlüssel verwenden. Im folgenden Beispiel wird eine LinearGradientBrush Ressourcendatei namens Dictionary1.xaml erstellt.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://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 Ihr Wörterbuch definiert haben, müssen Sie es mit dem Ressourcenverzeichnis Ihres Steuerelements zusammenführen. Dazu können Sie XAML oder Code verwenden.
Im folgenden Beispiel wird ein Ressourcenwörterbuch mithilfe von XAML zusammengeführt.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Der Nachteil dieses Ansatzes besteht darin, dass jedes Mal ein ResourceDictionary Objekt erstellt wird, wenn Sie darauf verweisen. Wenn Sie beispielsweise über 10 benutzerdefinierte Steuerelemente in Ihrer Bibliothek verfügen und die freigegebenen Ressourcenwörterbücher für jedes Steuerelement mithilfe von XAML zusammenführen, erstellen Sie 10 identische ResourceDictionary Objekte. Sie können dies vermeiden, indem Sie eine statische Klasse erstellen, die die Ressourcen im Code zusammenführt und das resultierende ResourceDictionaryErgebnis zurückgibt.
Im folgenden Beispiel wird eine Klasse erstellt, die eine freigegebene 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 Steuerobjekts im Konstruktor des Steuerobjekts kombiniert, bevor es InitializeComponent
aufruft. Da es SharedDictionaryManager.SharedDictionary
sich um eine statische Eigenschaft handelt, wird die ResourceDictionary Eigenschaft nur einmal erstellt. Da das Ressourcenwörterbuch schon vor dem Aufruf von InitializeComponent
zusammengeführt wurde, sind die Ressourcen in der XAML-Datei für das Steuerelement verfügbar.
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Definieren von Ressourcen auf der Ebene der Themen
Mit WPF können Sie Ressourcen für verschiedene Windows-Designs erstellen. Als Steuerelementautor können Sie eine Ressource für ein bestimmtes Design definieren, um die Darstellung Ihres Steuerelements abhängig davon zu ändern, welches Design verwendet wird. Beispielsweise unterscheidet sich das Erscheinungsbild eines Button im klassischen Windows-Theme (dem Standardtheme für Windows 2000) von einem Button im Windows Luna-Theme (dem Standardtheme für Windows XP), da Button für jedes Theme ein anderes ControlTemplate verwendet wird.
Ressourcen, die für ein bestimmtes Thema spezifisch sind, werden in einer Ressourcendatei mit einem bestimmten Dateinamen aufbewahrt. Diese Dateien müssen sich in einem Ordner mit dem Namen Themes
befinden, der ein Unterordner des Ordners ist, der das Steuerelement enthält. In der folgenden Tabelle sind die Ressourcenwörterbuchdateien und das Design aufgeführt, das jeder Datei zugeordnet ist:
Dateiname des Ressourcenwörterbuchs | Windows-Design |
---|---|
Classic.xaml |
Klassisches Windows 9x/2000-Aussehen auf Windows XP |
Luna.NormalColor.xaml |
Blaues Standarddesign unter Windows XP |
Luna.Homestead.xaml |
Olivdesign unter Windows XP |
Luna.Metallic.xaml |
Silberdesign unter Windows XP |
Royale.NormalColor.xaml |
Standarddesign unter Windows XP Media Center Edition |
Aero.NormalColor.xaml |
Standarddesign unter Windows Vista |
Sie müssen keine Ressource für jedes Thema definieren. Wenn eine Ressource nicht für ein bestimmtes Design definiert ist, sucht das Steuerelement nach Classic.xaml
der Ressource. Wenn die Ressource nicht in der Datei definiert ist, die dem aktuellen Design oder in Classic.xaml
entspricht, verwendet das Steuerelement die generische Ressource, die sich in einer Ressourcenwörterbuchdatei namens generic.xaml
befindet. Die generic.xaml
Datei befindet sich im selben Ordner wie die designspezifischen Ressourcenwörterbuchdateien. Obwohl generic.xaml
keinem bestimmten Windows-Thema entspricht, ist es dennoch ein designbezogenes Wörterbuch.
Das benutzerdefinierte C# - oder Visual Basic NumericUpDown-Steuerelement mit Design- und Benutzeroberflächenautomatisierungs-Unterstützungsbeispiel enthält zwei Ressourcenwörterbücher für das NumericUpDown
Steuerelement: eine befindet sich in generic.xaml, und die andere befindet sich in Luna.NormalColor.xaml.
Wenn Sie eine ControlTemplate in eine der designspezifischen Ressourcenwörterbuchdateien einfügen, müssen Sie einen statischen Konstruktor für Ihr Steuerelement erstellen und die OverrideMetadata(Type, PropertyMetadata) Methode auf dem DefaultStyleKey aufrufen, wie im folgenden Beispiel gezeigt.
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Definieren und Verweisen auf Schlüssel für Themenressourcen
Wenn Sie eine Ressource auf Elementebene definieren, können Sie eine Zeichenfolge als Schlüssel zuweisen und über die Zeichenfolge auf die Ressource zugreifen. Wenn Sie eine Ressource auf der Themenebene definieren, müssen Sie einen ComponentResourceKey als Schlüssel verwenden. Im folgenden Beispiel wird eine Ressource in "generic.xaml" definiert.
<LinearGradientBrush
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter},
ResourceId=MyEllipseBrush}"
StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="Red" Offset="0.5" />
<GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>
Im folgenden Beispiel wird auf die Ressource verwiesen, indem ComponentResourceKey als Schlüssel angegeben wird.
<RepeatButton
Grid.Column="1" Grid.Row="0"
Background="{StaticResource {ComponentResourceKey
TypeInTargetAssembly={x:Type local:NumericUpDown},
ResourceId=ButtonBrush}}">
Up
</RepeatButton>
<RepeatButton
Grid.Column="1" Grid.Row="1"
Background="{StaticResource {ComponentResourceKey
TypeInTargetAssembly={x:Type local:NumericUpDown},
ResourceId=ButtonBrush}}">
Down
</RepeatButton>
Festlegen des Speicherorts von Themenressourcen
Um die Ressourcen für ein Steuerelement zu finden, muss die Hostanwendung wissen, dass die Assembly steuerelementspezifische Ressourcen enthält. Sie können dies erreichen, indem Sie zur ThemeInfoAttribute Assembly, die das Steuerelement enthält, hinzufügen. Die ThemeInfoAttribute Eigenschaft verfügt über eine GenericDictionaryLocation Eigenschaft, die den Speicherort generischer Ressourcen angibt, und eine ThemeDictionaryLocation Eigenschaft, die den Speicherort der designspezifischen Ressourcen angibt.
Im folgenden Beispiel werden die Eigenschaften GenericDictionaryLocation und ThemeDictionaryLocation auf SourceAssembly festgelegt, um anzugeben, dass die generischen und designspezifischen Ressourcen in derselben Assembly wie das Steuerelement enthalten sind.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
Siehe auch
.NET Desktop feedback