Overzicht van het ontwerpen van besturingselementen
De uitbreidbaarheid van het WPF-besturingsmodel (Windows Presentation Foundation) vermindert de noodzaak om een nieuw besturingselement te maken aanzienlijk. Echter, in bepaalde gevallen moet u mogelijk nog steeds een aangepast besturingselement maken. In dit onderwerp worden de functies besproken die de noodzaak minimaliseren om een aangepast besturingselement te maken, evenals de verschillende ontwerpmodellen voor besturingselementen in Windows Presentation Foundation (WPF). In dit onderwerp wordt ook uitgelegd hoe je een nieuw besturingselement maakt.
Alternatieven voor het schrijven van een nieuw besturingscomponent
Als u in het verleden een aangepaste ervaring van een bestaand besturingselement wilt krijgen, was u beperkt tot het wijzigen van de standaardeigenschappen van het besturingselement, zoals achtergrondkleur, randbreedte en tekengrootte. Als u het uiterlijk of gedrag van een besturingselement buiten deze vooraf gedefinieerde parameters wilt uitbreiden, moet u meestal een nieuw besturingselement maken door over te nemen van een bestaand besturingselement en de methode te overschrijven die verantwoordelijk is voor het tekenen van het besturingselement. Hoewel dat nog steeds een optie is, kunt u met WPF bestaande besturingselementen aanpassen met behulp van het uitgebreide inhoudsmodel, stijlen, sjablonen en triggers. De volgende lijst bevat voorbeelden van hoe deze functies kunnen worden gebruikt om aangepaste en consistente ervaringen te maken zonder een nieuw besturingselement te hoeven maken.
Uitgebreide inhoud. Veel van de standaard WPF-besturingselementen ondersteunen uitgebreide inhoud. De inhoudseigenschap van een Button is bijvoorbeeld van het type Object, zodat theoretisch alles kan worden weergegeven op een Button. Als u een knop een afbeelding en tekst wilt weergeven, kunt u een afbeelding en een TextBlock toevoegen aan een StackPanel en de StackPanel toewijzen aan de eigenschap Content. Omdat de besturingselementen WPF-visuele elementen en willekeurige gegevens kunnen weergeven, hoeft u minder een nieuw besturingselement te maken of een bestaand besturingselement te wijzigen om een complexe visualisatie te ondersteunen. Zie WPF-inhoudsmodelvoor meer informatie over het inhoudsmodel voor Button en andere inhoudsmodellen in WPF.
Stijlen. Een Style is een verzameling waarden die eigenschappen voor een besturingselement vertegenwoordigen. Met behulp van stijlen kunt u een herbruikbare weergave van een gewenst uiterlijk en gedrag van een besturingselement maken zonder een nieuw besturingselement te schrijven. Stel dat u wilt dat al uw TextBlock besturingselementen een rood, Arial-lettertype met een tekengrootte van 14 hebben. U kunt een stijl als resource maken en de juiste eigenschappen dienovereenkomstig instellen. Vervolgens heeft elke TextBlock die u aan uw toepassing toevoegt, hetzelfde uiterlijk.
Gegevenssjablonen. Met een DataTemplate kunt u aanpassen hoe gegevens op een besturingselement worden weergegeven. Een DataTemplate kan bijvoorbeeld worden gebruikt om op te geven hoe gegevens worden weergegeven in een ListBox. Zie voor een voorbeeld hiervan Overzicht van gegevenssjabloon. Naast het aanpassen van het uiterlijk van gegevens kan een DataTemplate UI-elementen bevatten, wat u veel flexibiliteit biedt in aangepaste UI's. Met behulp van een DataTemplatekunt u bijvoorbeeld een ComboBox maken waarin elk item een selectievakje bevat.
Besturingselementsjablonen. Veel besturingselementen in WPF gebruiken een ControlTemplate om de structuur en het uiterlijk van het besturingselement te definiëren, waardoor het uiterlijk van een besturingselement wordt gescheiden van de functionaliteit van het besturingselement. U kunt het uiterlijk van een bedieningselement drastisch wijzigen door de ControlTemplate-instellingen opnieuw te definiëren. Stel dat u een besturingselement wilt dat eruitziet als een stoplicht. Dit besturingselement heeft een eenvoudig te gebruiken interface en functionaliteit. Het besturingselement is drie cirkels, waarvan slechts één tegelijk kan worden verlicht. Na enige reflectie realiseert u zich misschien dat een RadioButton de functionaliteit biedt van slechts één die tegelijk wordt geselecteerd, maar de standaardweergave van de RadioButton lijkt niets op de lichten op een stoplicht. Omdat de RadioButton een sjabloon voor bedieningselement gebruikt om het uiterlijk ervan te definiëren, is het eenvoudig om de ControlTemplate opnieuw te definiëren om aan de vereisten van het bedieningselement te voldoen door gebruik te maken van knoppen met optie om uw stoplicht te maken.
Notitie
Hoewel een RadioButton een DataTemplatekan gebruiken, is een DataTemplate in dit voorbeeld niet voldoende. De DataTemplate definieert het uiterlijk van de inhoud van een besturingselement. In het geval van een RadioButtonwordt de inhoud rechts van de cirkel weergegeven die aangeeft of de RadioButton is geselecteerd. In het voorbeeld van het stoplicht moet het keuzerondje een eenvoudige cirkel zijn die kan oplichten. Omdat de vormgevingsvereiste voor het stoplicht zo anders is dan de standaardweergave van de RadioButton, is het noodzakelijk om de ControlTemplateopnieuw te definiëren. In het algemeen wordt een DataTemplate gebruikt voor het definiëren van de inhoud (of gegevens) van een besturingselement en wordt een ControlTemplate gebruikt voor het definiëren van de structuur van een besturingselement.
Triggers Met een Trigger kunt u het uiterlijk en gedrag van een besturingselement dynamisch wijzigen zonder een nieuw besturingselement te maken. Stel dat u meerdere besturingselementen voor ListBox in uw toepassing hebt en dat de items in elke ListBox vet en rood moeten zijn wanneer ze zijn geselecteerd. Uw eerste instinct kan zijn om een klasse te maken die overgaat van ListBox en de methode OnSelectionChanged overschrijft om het uiterlijk van het geselecteerde item te wijzigen, maar een betere benadering is om een trigger toe te voegen aan een stijl van een ListBoxItem waarmee het uiterlijk van het geselecteerde item wordt gewijzigd. Met een trigger kunt u eigenschapswaarden wijzigen of acties ondernemen op basis van de waarde van een eigenschap. Met een EventTrigger kunt u acties ondernemen wanneer een gebeurtenis plaatsvindt.
Zie Styling and Templatingvoor meer informatie over stijlen, sjablonen en triggers.
Als uw besturingselement de functionaliteit van een bestaand besturingselement spiegelt, maar u wilt dat het besturingselement er anders uitziet, moet u eerst overwegen of u een van de methoden die in deze sectie worden besproken, kunt gebruiken om het uiterlijk van het bestaande besturingselement te wijzigen.
Modellen voor het ontwerpen van besturingselementen
Het uitgebreide inhoudsmodel, stijlen, sjablonen en triggers minimaliseren de noodzaak om een nieuw besturingselement te maken. Als u echter wel een nieuw besturingselement moet maken, is het belangrijk dat u de verschillende ontwerpmodellen voor besturingselementen in WPF begrijpt. WPF biedt drie algemene modellen voor het maken van een besturingselement, die elk een andere set functies en flexibiliteit bieden. De basisklassen voor de drie modellen zijn UserControl, Controlen FrameworkElement.
Afgeleid van UserControl
De eenvoudigste manier om een besturingselement in WPF te maken is afgeleid van UserControl. Wanneer u een besturingselement bouwt dat overgaat van UserControl, voegt u bestaande onderdelen toe aan de UserControl, noemt u de onderdelen en verwijst u naar gebeurtenis-handlers in XAML. U kunt vervolgens verwijzen naar de benoemde elementen en de gebeurtenis-handlers definiëren in code. Dit ontwikkelingsmodel is vergelijkbaar met het model dat wordt gebruikt voor het ontwikkelen van toepassingen in WPF.
Als deze correct is gebouwd, kan een UserControl profiteren van de voordelen van rijke inhoud, stijlen en triggers. Als uw controle echter overneemt van UserControl, kunnen mensen die uw controle gebruiken geen DataTemplate of ControlTemplate gebruiken om het uiterlijk ervan aan te passen. Het is nodig om af te leiden van de Control-klasse of een van de afgeleide klassen (behalve UserControl) om een aangepast besturingselement te maken dat sjablonen ondersteunt.
Voordelen van Erfen van UserControl
Overweeg af te leiden van UserControl als alle volgende van toepassing zijn:
U wilt uw besturingselement op dezelfde manier bouwen als hoe u een toepassing bouwt.
Uw besturingselement bestaat alleen uit bestaande onderdelen.
U hoeft geen ondersteuning te bieden voor complexe aanpassingen.
Afgeleid van controle
Afgeleid van de Control-klasse is het model dat wordt gebruikt door de meeste bestaande WPF-besturingselementen. Wanneer u een besturingselement maakt dat wordt overgenomen van de Control-klasse, definieert u het uiterlijk ervan met behulp van sjablonen. Door dit te doen, scheidt u de operationele logica van de visuele weergave. U kunt er ook voor zorgen dat de gebruikersinterface en logica worden ontkoppeld met behulp van opdrachten en bindingen in plaats van gebeurtenissen en het voorkomen van verwijzingen naar elementen in de ControlTemplate waar mogelijk. Als de gebruikersinterface en logica van uw besturingselement correct zijn ontkoppeld, kan een gebruiker van uw besturingselement de ControlTemplate van het besturingselement opnieuw definiëren om het uiterlijk ervan aan te passen. Hoewel het bouwen van een aangepaste Control niet zo eenvoudig is als het bouwen van een UserControl, biedt een aangepaste Control de meeste flexibiliteit.
Voordelen van het afleiden uit controle
Overweeg om te worden afgeleid van Control in plaats van de UserControl klasse te gebruiken als een van de volgende zaken van toepassing is:
U wilt dat het uiterlijk van uw besturing aanpasbaar is via de ControlTemplate.
U wilt dat uw besturingselement verschillende thema's ondersteunt.
Afgeleid van FrameworkElement
Besturingselementen die zijn afgeleid van UserControl of Control zijn afhankelijk van het opstellen van bestaande elementen. Voor veel scenario's is dit een acceptabele oplossing, omdat elk object dat wordt overgenomen van FrameworkElement zich in een ControlTemplatebevindt. Er zijn echter momenten waarop het uiterlijk van een besturingselement meer vereist is dan de functionaliteit van eenvoudige elementsamenstelling. Voor deze scenario's is het baseren van een onderdeel op FrameworkElement de juiste keuze.
Er zijn twee standaardmethoden voor het bouwen van FrameworkElement-gebaseerde onderdelen: directe weergave en op-maat-gemaakte elementsamenstelling. Direct rendering houdt in dat de OnRender-methode van FrameworkElement wordt overschreven en dat DrawingContext-bewerkingen worden geleverd die de componentvisuals expliciet definiëren. Dit is de methode die wordt gebruikt door Image en Border. Aangepaste elementencompositie omvat het gebruik van objecten van het type Visual om het uiterlijk van uw component te ontwerpen. Voor een voorbeeld, zie Het gebruik van DrawingVisual-objecten. Track is een voorbeeld van een besturingselement in WPF dat gebruikmaakt van aangepaste elementsamenstelling. Het is ook mogelijk om directe rendering en aangepaste elementcompositie in dezelfde controle te combineren.
Voordelen van afleiding van FrameworkElement
Overweeg af te leiden van FrameworkElement als een van de volgende van toepassing is:
U wilt nauwkeurige controle hebben over het uiterlijk van uw controle buiten wat wordt geboden door eenvoudige elementsamenstelling.
U wilt het uiterlijk van uw controle bepalen door uw eigen renderlogica te gebruiken.
U wilt bestaande elementen op nieuwe manieren opstellen die verder gaan dan wat mogelijk is met UserControl en Control.
Basisprincipes van auteurschap beheren
Zoals eerder besproken, is een van de krachtigste functies van WPF de mogelijkheid om verder te gaan dan alleen het instellen van basiseigenschappen van een besturingselement om het uiterlijk en gedrag aan te passen, zonder toch een aangepast besturingselement te hoeven maken. De stijl-, gegevensbindings- en triggerfuncties worden mogelijk gemaakt door het WPF-eigenschappensysteem en het WPF-gebeurtenissysteem. In de volgende secties worden enkele procedures beschreven die u moet volgen, ongeacht het model dat u gebruikt om het aangepaste besturingselement te maken, zodat gebruikers van uw aangepaste besturingselement deze functies op dezelfde wijze kunnen gebruiken als voor een besturingselement dat is opgenomen in WPF.
Afhankelijkheidseigenschappen gebruiken
Wanneer een eigenschap een afhankelijkheidseigenschap is, kunt u het volgende doen:
Stel de eigenschap in een stijl in.
Koppel de eigenschap aan een gegevensbron.
Gebruik een dynamische resource als waarde van de eigenschap.
Animatie toevoegen aan de eigenschap.
Als u een eigenschap van uw besturingselement wilt ter ondersteuning van een van deze functies, moet u deze implementeren als een afhankelijkheidseigenschap. In het volgende voorbeeld wordt een afhankelijkheidseigenschap met de naam Value
gedefinieerd door het volgende te doen:
Definieer een DependencyProperty-id met de naam
ValueProperty
alspublic
static
readonly
veld.Registreer de naam van de eigenschap bij het eigenschappensysteem door DependencyProperty.Registeraan te roepen om het volgende op te geven:
De naam van de eigenschap.
Het type eigendom.
Het type dat eigenaar is van het eigendom.
De metagegevens voor de eigenschap. De metagegevens bevatten de standaardwaarde van de eigenschap, een CoerceValueCallback en een PropertyChangedCallback.
Definieer een CLR-wrapper-eigenschap met de naam
Value
. Dit is dezelfde naam die wordt gebruikt om de afhankelijkheidseigenschap te registreren, door deget
enset
toegangsors van de eigenschap te implementeren. Houd er rekening mee dat deget
enset
accessors respectievelijk alleen GetValue en SetValue aanroepen. Het wordt aanbevolen dat de accessors van afhankelijkheidseigenschappen geen extra logica bevatten, omdat clientprogramma's en WPF de accessors kunnen omzeilen en GetValue en SetValue rechtstreeks kunnen aanroepen. Als een eigenschap bijvoorbeeld is gebonden aan een gegevensbron, wordt deset
accessor van de eigenschap niet aangeroepen. In plaats van extra logica toe te voegen aan de get-and-set accessors, gebruikt u de ValidateValueCallback, CoerceValueCallbacken PropertyChangedCallback gemachtigden om te reageren of de waarde te controleren wanneer deze wordt gewijzigd. Zie Callbackfuncties en validatie van afhankelijke eigenschappenvoor meer informatie over deze callbackfuncties.Definieer een methode voor de CoerceValueCallback met de naam
CoerceValue
.CoerceValue
zorgt ervoor datValue
groter of gelijk is aanMinValue
en kleiner dan of gelijk aanMaxValue
.Definieer een methode voor de PropertyChangedCallback, met de naam
OnValueChanged
.OnValueChanged
maakt een RoutedPropertyChangedEventArgs<T>-object aan en bereidt zich voor om hetValueChanged
gerouteerde event te initiëren. Gerouteerde gebeurtenissen worden in de volgende sectie besproken.
/// <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
Zie Aangepaste afhankelijkheidseigenschappenvoor meer informatie.
Gerouteerde gebeurtenissen gebruiken
Net zoals afhankelijkheidseigenschappen het begrip CLR-eigenschappen uitbreiden met aanvullende functionaliteit, breiden gerouteerde gebeurtenissen het concept van standaard CLR-gebeurtenissen uit. Wanneer u een nieuw WPF-besturingselement maakt, is het ook raadzaam om uw gebeurtenis te implementeren als een gerouteerde gebeurtenis, omdat een gerouteerde gebeurtenis het volgende gedrag ondersteunt:
Gebeurtenissen kunnen worden verwerkt op een bovenliggend element van meerdere besturingselementen. Als een gebeurtenis een bubblinggebeurtenis is, kan één ouder in de elementboom abonneren op de gebeurtenis. Vervolgens kunnen auteurs van toepassingen één handler gebruiken om te reageren op de gebeurtenis van meerdere besturingselementen. Als uw besturingselement bijvoorbeeld deel uitmaakt van elk item in een ListBox (omdat dit is opgenomen in een DataTemplate), kan de toepassingsontwikkelaar de gebeurtenis-handler voor de gebeurtenis van uw besturingselement definiëren op de ListBox. Wanneer de gebeurtenis plaatsvindt op een van de besturingselementen, wordt de gebeurtenis-handler aangeroepen.
Gerouteerde gebeurtenissen kunnen worden gebruikt in een EventSetter, waardoor toepassingsontwikkelaars de handler van een gebeurtenis binnen een stijl kunnen opgeven.
Gerouteerde gebeurtenissen kunnen worden gebruikt in een EventTrigger, wat handig is voor het animeren van eigenschappen met behulp van XAML. Zie Animatieoverzichtvoor meer informatie.
In het volgende voorbeeld wordt een gerouteerde gebeurtenis gedefinieerd door de volgende stappen te volgen:
Definieer een RoutedEvent-id met de naam
ValueChangedEvent
alspublic
static
readonly
veld.Registreer de gerouteerde gebeurtenis door methode EventManager.RegisterRoutedEvent aan te roepen. In het voorbeeld wordt de volgende informatie opgegeven wanneer deze RegisterRoutedEventaanroept:
De naam van de gebeurtenis is
ValueChanged
.De routeringsstrategie is Bubble, wat betekent dat een gebeurtenis-handler op de bron (het object dat de gebeurtenis genereert) eerst wordt aangeroepen en vervolgens gebeurtenis-handlers op de bovenliggende elementen van de bron achter elkaar worden aangeroepen, te beginnen met de gebeurtenis-handler op het dichtstbijzijnde bovenliggende element.
Het type gebeurtenis-handler is RoutedPropertyChangedEventHandler<T>, samengesteld met een Decimal type.
Het type dat eigenaar is van de gebeurtenis is
NumericUpDown
.
Declareer een openbare gebeurtenis met de naam
ValueChanged
en bevat declaraties voor gebeurtenistoegangsrechten. In het voorbeeld wordt AddHandler aangeroepen in de declaratie van deadd
accessor en RemoveHandler in de declaratie van deremove
accessor om de WPF-gebeurtenisservices te gebruiken.Maak een beveiligde virtuele methode met de naam
OnValueChanged
die deValueChanged
gebeurtenis genereert.
/// <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
Zie Overzicht van gerouteerde gebeurtenissen en Een aangepaste gerouteerde gebeurtenis makenvoor meer informatie.
Binding gebruiken
Als u de gebruikersinterface van uw besturingselement wilt loskoppelen van de logica, kunt u overwegen om gegevensbinding te gebruiken. Dit is met name belangrijk als u het uiterlijk van uw controle definieert met behulp van een ControlTemplate. Wanneer u gegevensbinding gebruikt, is het mogelijk dat u niet meer hoeft te verwijzen naar specifieke onderdelen van de gebruikersinterface uit de code. Het is een goed idee om te voorkomen dat naar elementen in de ControlTemplate verwijzen, omdat wanneer de code verwijst naar elementen in de ControlTemplate en de ControlTemplate wordt gewijzigd, het element waarnaar wordt verwezen, moet worden opgenomen in de nieuwe ControlTemplate.
In het volgende voorbeeld wordt de TextBlock van het besturingselement NumericUpDown
bijgewerkt, en krijgt het een naam, waarbij het tekstvak bij naam in de code wordt aangeroepen.
<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
In het volgende voorbeeld wordt binding gebruikt om hetzelfde te bereiken.
<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>
Zie Overzicht van gegevensbindingvoor meer informatie over gegevensbinding.
Ontwerpen voor ontwerpers
Als u ondersteuning wilt ontvangen voor aangepaste WPF-besturingselementen in de WPF Designer voor Visual Studio (bijvoorbeeld het bewerken van eigenschappen met het venster Eigenschappen), volgt u deze richtlijnen. Zie Design XAML in Visual Studiovoor meer informatie over ontwikkelen voor de WPF Designer.
Eigenschappen van afhankelijkheden
Zorg ervoor dat u CLR-get
- en set
-toegangen implementeert zoals eerder beschreven in 'Eigenschappen van afhankelijkheden gebruiken'. Ontwerpers kunnen de wrapper gebruiken om de aanwezigheid van een afhankelijkheidseigenschap te detecteren, maar net als bij WPF en de gebruikers van het besturingselement, zijn ze niet verplicht om de toegangen aan te roepen bij het ophalen of instellen van de eigenschap.
Gekoppelde eigenschappen
Je moet gekoppelde eigenschappen implementeren voor custom controls met behulp van de volgende richtlijnen:
Laat een
public
static
readonly
DependencyProperty in de vorm van PropertyNameProperty
die is gemaakt met de RegisterAttached-methode. De eigenschapsnaam die wordt doorgegeven aan RegisterAttached moet overeenkomen met PropertyName.Implementeer een paar
public
static
CLR-methoden met de naamSet
PropertyName enGet
PropertyName. Beide methoden moeten een klasse accepteren die is afgeleid van DependencyProperty als eerste argument. De methodeSet
PropertyName accepteert ook een argument waarvan het type overeenkomt met het geregistreerde gegevenstype voor de eigenschap. De methodeGet
PropertyName moet een waarde van hetzelfde type retourneren. Als de methodeSet
PropertyName ontbreekt, wordt de eigenschap gemarkeerd als alleen-lezen.Set
PropertyName enGet
PropertyName moeten respectievelijk rechtstreeks worden gerouteerd naar de GetValue- en SetValue-methoden voor het doelafhankelijkheidsobject. Ontwerpers kunnen toegang krijgen tot de gekoppelde eigenschap door de methode-wrapper aan te roepen of een directe aanroep naar het doelafhankelijkheidsobject te maken.
Zie Overzicht van bijgevoegde eigenschappenvoor meer informatie over gekoppelde eigenschappen.
Gedeelde resources definiëren en gebruiken
U kunt uw besturingselement opnemen in dezelfde assembly als uw toepassing, of u kunt uw besturingselement inpakken in een afzonderlijke assembly die in meerdere toepassingen kan worden gebruikt. Voor het grootste deel geldt de informatie die in dit onderwerp wordt besproken, ongeacht de methode die u gebruikt. Er is echter één verschil dat het vermelden waard is. Wanneer u een controle in dezelfde assembly plaatst als een toepassing, kunt u globale resources toevoegen aan het bestand App.xaml. Maar aan een assembly met alleen besturingselementen is geen Application object gekoppeld, dus er is geen App.xaml-bestand beschikbaar.
Wanneer een toepassing naar een resource zoekt, wordt in de volgende volgorde naar drie niveaus gekeken:
Het elementniveau.
Het systeem begint met het element dat verwijst naar de resource en zoekt vervolgens naar resources van het logische bovenliggende element, enzovoort totdat het hoofdelement is bereikt.
Het toepassingsniveau.
Resources die zijn gedefinieerd door het Application-object.
Het themaniveau.
Woordenlijsten op themaniveau worden opgeslagen in een submap met de naam Thema's. De bestanden in de map Thema's komen overeen met thema's. U hebt bijvoorbeeld Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, enzovoort. U kunt ook een bestand met de naam generic.xaml hebben. Wanneer het systeem zoekt naar een resource op themaniveau, zoekt het eerst naar een resource in het themaspecifieke bestand en zoekt het vervolgens in generic.xaml.
Wanneer uw besturingselement zich in een assembly bevindt die losstaat van de toepassing, moet u uw globale resources op elementniveau of op themaniveau plaatsen. Beide methoden hebben hun voordelen.
Resources definiëren op elementniveau
U kunt gedeelde resources definiëren op elementniveau door een aangepaste resourcewoordenlijst te maken en deze samen te voegen met de resourcewoordenlijst van uw besturingselement. Wanneer u deze methode gebruikt, kunt u het resourcebestand een naam opgeven die u wilt gebruiken en kan het zich in dezelfde map bevinden als uw besturingselementen. Resources op elementniveau kunnen ook eenvoudige tekenreeksen als sleutels gebruiken. In het volgende voorbeeld wordt een LinearGradientBrush resourcebestand gemaakt met de naam Dictionary1.xaml.
<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>
Nadat u de woordenlijst hebt gedefinieerd, moet u deze samenvoegen met de resourcewoordenlijst van uw besturingselement. U kunt dit doen met behulp van XAML of code.
In het volgende voorbeeld wordt een resourcewoordenlijst samengevoegd met behulp van XAML.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Het nadeel van deze benadering is dat een ResourceDictionary object wordt gemaakt telkens wanneer u ernaar verwijst. Als u bijvoorbeeld 10 aangepaste besturingselementen in uw bibliotheek hebt en de gedeelde resourcewoordenlijsten voor elk besturingselement samenvoegt met behulp van XAML, maakt u 10 identieke ResourceDictionary objecten. U kunt dit voorkomen door een statische klasse te maken die de resources in code samenvoegt en de resulterende ResourceDictionaryretourneert.
In het volgende voorbeeld wordt een klasse gemaakt die een gedeelde ResourceDictionaryretourneert.
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;
}
In het volgende voorbeeld wordt de gedeelde resource samengevoegd met de resources van een aangepast besturingselement in de constructor van het besturingselement voordat deze InitializeComponent
aanroept. Omdat de SharedDictionaryManager.SharedDictionary
een statische eigenschap is, wordt de ResourceDictionary slechts één keer gemaakt. Omdat het woordenboek met resources is samengevoegd voordat InitializeComponent
werd aangeroepen, zijn de resources beschikbaar voor de besturing in zijn XAML-bestand.
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Bepalen van resources op thema-niveau
Met WPF kunt u resources maken voor verschillende Windows-thema's. Als besturingselementauteur kunt u een resource voor een specifiek thema definiëren om het uiterlijk van uw besturingselement te wijzigen, afhankelijk van het thema dat wordt gebruikt. Het uiterlijk van een Button in het klassieke Windows-thema (het standaardthema voor Windows 2000) verschilt bijvoorbeeld van een Button in het Windows Luna-thema (het standaardthema voor Windows XP), omdat de Button voor elk thema een andere ControlTemplate gebruikt.
Resources die specifiek zijn voor een thema, worden bewaard in een resourcewoordenlijst met een specifieke bestandsnaam. Deze bestanden moeten zich in een map bevinden met de naam Themes
die een submap is van de map die het besturingselement bevat. De volgende tabel bevat de resourcewoordenlijstbestanden en het thema dat aan elk bestand is gekoppeld:
Bestandsnaam van resourcewoordenlijst | Windows-thema |
---|---|
Classic.xaml |
Klassiek Windows 9x/2000 uiterlijk op Windows XP |
Luna.NormalColor.xaml |
Standaard blauw thema in Windows XP |
Luna.Homestead.xaml |
Olijfthema in Windows XP |
Luna.Metallic.xaml |
Silver-thema in Windows XP |
Royale.NormalColor.xaml |
Standaardthema in Windows XP Media Center Edition |
Aero.NormalColor.xaml |
Standaardthema op Windows Vista |
U hoeft geen resource te definiëren voor elk thema. Als een resource niet is gedefinieerd voor een specifiek thema, controleert het besturingselement Classic.xaml
voor de resource. Als de resource niet is gedefinieerd in het bestand dat overeenkomt met het huidige thema of in Classic.xaml
, gebruikt het besturingselement de algemene resource, die zich in een resourcewoordenlijstbestand bevindt met de naam generic.xaml
. Het bestand generic.xaml
bevindt zich in dezelfde map als de themaspecifieke resourcewoordenlijstbestanden. Hoewel generic.xaml
niet overeenkomt met een specifiek Windows-thema, is het nog steeds een woordenlijst op themaniveau.
Het C# of Visual Basic NumericUpDown aangepaste besturingselement met thema- en UI-automatiseringsondersteuning bevat twee ressourcemappen voor het NumericUpDown
-besturingselement: een bevindt zich in generic.xaml en de andere in Luna.NormalColor.xaml.
Wanneer u een ControlTemplate in een van de themaspecifieke resourcewoordenlijstbestanden plaatst, moet u een statische constructor maken voor uw besturingselement en de OverrideMetadata(Type, PropertyMetadata) methode aanroepen op de DefaultStyleKey, zoals wordt weergegeven in het volgende voorbeeld.
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Sleutels voor themabronnen definiëren en hiernaar verwijzen
Wanneer u een resource op elementniveau definieert, kunt u een tekenreeks toewijzen als sleutel en toegang krijgen tot de resource via de tekenreeks. Wanneer u een resource op themaniveau definieert, moet u een ComponentResourceKey als sleutel gebruiken. In het volgende voorbeeld wordt een resource gedefinieerd in generic.xaml.
<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>
In het volgende voorbeeld wordt verwezen naar de resource door de ComponentResourceKey op te geven als sleutel.
<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>
De locatie van themabronnen opgeven
Om de resources voor een controle te vinden, moet de hostingtoepassing weten dat de assembly controlespecifieke resources bevat. U kunt dit doen door de ThemeInfoAttribute toe te voegen aan de assembly die het besturingselement bevat. De ThemeInfoAttribute heeft een GenericDictionaryLocation eigenschap waarmee de locatie van algemene resources wordt opgegeven en een ThemeDictionaryLocation eigenschap waarmee de locatie van de themaspecifieke resources wordt opgegeven.
In het volgende voorbeeld worden de eigenschappen GenericDictionaryLocation en ThemeDictionaryLocation ingesteld op SourceAssemblyom op te geven dat de algemene en themaspecifieke resources zich in dezelfde assembly bevinden als het besturingselement.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
Zie ook
.NET Desktop feedback