Översikt över kontrollredigering
Utökningsbarheten för WPF-kontrollmodellen (Windows Presentation Foundation) minskar avsevärt behovet av att skapa en ny kontroll. I vissa fall kan du dock fortfarande behöva skapa en anpassad kontroll. I det här avsnittet beskrivs de funktioner som minimerar behovet av att skapa en anpassad kontroll och de olika kontrollredigeringsmodellerna i Windows Presentation Foundation (WPF). Det här avsnittet visar också hur du skapar en ny kontroll.
Alternativ till att skriva en ny kontroll
Om du tidigare ville få en anpassad upplevelse från en befintlig kontroll var du begränsad till att ändra standardegenskaperna för kontrollen, till exempel bakgrundsfärg, kantlinjebredd och teckenstorlek. Om du vill utöka utseendet eller beteendet för en kontroll utöver dessa fördefinierade parametrar måste du skapa en ny kontroll, vanligtvis genom att ärva från en befintlig kontroll och åsidosätta den metod som ansvarar för att rita kontrollen. Även om det fortfarande är ett alternativ gör WPF att du kan anpassa befintliga kontroller med hjälp av dess omfattande innehållsmodell, formatmallar, mallar och utlösare. Följande lista innehåller exempel på hur dessa funktioner kan användas för att skapa anpassade och konsekventa upplevelser utan att behöva skapa en ny kontroll.
Omfattande innehåll. Många av WPF-standardkontrollerna stöder omfattande innehåll. Innehållsegenskapen för en Button är till exempel av typen Object, så teoretiskt sett kan allt visas på en Button. Om du vill att en knapp ska visa en bild och text kan du lägga till en bild och en TextBlock till en StackPanel och tilldela StackPanel till egenskapen Content. Eftersom kontrollerna kan visa visuella WPF-element och godtyckliga data, finns det mindre behov av att skapa en ny kontroll eller att ändra en befintlig kontroll för att stödja en komplex visualisering. Mer information om innehållsmodellen för Button och andra innehållsmodeller i WPF finns i WPF-innehållsmodell.
Stilar. En Style är en samling värden som representerar egenskaper för en kontroll. Med hjälp av formatmallar kan du skapa en återanvändbar representation av ett önskat kontrollutseende och -beteende utan att skriva en ny kontroll. Anta till exempel att du vill att alla dina TextBlock kontroller ska ha rött, Arial-teckensnitt med teckenstorleken 14. Du kan skapa ett format som en resurs och ange lämpliga egenskaper i enlighet med detta. Varje TextBlock som du lägger till i ditt program har sedan samma utseende.
Datamallar. Med en DataTemplate kan du anpassa hur data visas på en kontroll. Till exempel kan en DataTemplate användas för att ange hur data ska visas i en ListBox. Ett exempel på detta finns i Översikt över datakryptering. Förutom att anpassa utseendet på data kan en DataTemplate innehålla gränssnittselement, vilket ger dig stor flexibilitet i anpassade UIs. Genom att till exempel använda en DataTemplatekan du skapa en ComboBox där varje objekt innehåller en kryssruta.
Kontrollmallar. Många kontroller i WPF använder en ControlTemplate för att definiera kontrollens struktur och utseende, vilket skiljer utseendet på en kontroll från kontrollens funktioner. Du kan ändra utseendet på en kontroll drastiskt genom att omdefiniera dess ControlTemplate. Anta till exempel att du vill ha en kontroll som ser ut som ett stoppljus. Den här kontrollen har ett enkelt användargränssnitt och funktioner. Kontrollen är tre cirklar, varav endast en kan tändas i taget. Efter lite eftertanke kanske du inser att en RadioButton endast erbjuder funktioner för en som väljs i taget, men standardutseendet för RadioButton ser inte ut som lamporna på ett stoppljus. Eftersom RadioButton använder en kontrollmall för att definiera sitt utseende, är det enkelt att omforma ControlTemplate för att passa kontrollens krav och använda alternativknappar för att skapa ditt stoppljus.
Notis
Även om en RadioButton kan använda en DataTemplateräcker inte en DataTemplate i det här exemplet. DataTemplate definierar utseendet på innehållet i en kontroll. När det gäller en RadioButtonär innehållet det som visas till höger om cirkeln som anger om RadioButton har valts. I exemplet med stoppljuset behöver radioknappen bara vara en cirkel som kan "lysa upp". Eftersom utseendekravet för stoppljuset är så annorlunda än standardutseendet för RadioButtonär det nödvändigt att omdefiniera ControlTemplate. I allmänhet används en DataTemplate för att definiera innehållet (eller data) för en kontroll, och en ControlTemplate används för att definiera hur en kontroll är strukturerad.
Utlösare. Med en Trigger kan du dynamiskt ändra utseendet och beteendet för en kontroll utan att skapa en ny kontroll. Anta till exempel att du har flera ListBox kontroller i ditt program och vill att objekten i varje ListBox ska vara fetstil och röda när de väljs. Din första instinkt kan vara att skapa en klass som ärver från ListBox och åsidosätta metoden OnSelectionChanged för att ändra utseendet på det markerade objektet, men en bättre metod är att lägga till en utlösare i ett format av en ListBoxItem som ändrar utseendet på det markerade objektet. Med en utlösare kan du ändra egenskapsvärden eller vidta åtgärder baserat på värdet för en egenskap. Med en EventTrigger kan du vidta åtgärder när en händelse inträffar.
Mer information om formatmallar, mallar och utlösare finns i Styling och Templating.
Om kontrollen i allmänhet speglar funktionerna i en befintlig kontroll, men du vill att kontrollen ska se annorlunda ut, bör du först överväga om du kan använda någon av de metoder som beskrivs i det här avsnittet för att ändra den befintliga kontrollens utseende.
Modeller för styrningsförfattande
Den omfattande innehållsmodellen, formatmallarna, mallarna och utlösarna minimerar behovet av att skapa en ny kontroll. Men om du behöver skapa en ny kontroll är det viktigt att förstå de olika kontrollredigeringsmodellerna i WPF. WPF innehåller tre allmänna modeller för att skapa en kontroll, som var och en ger olika funktioner och flexibilitetsnivå. Basklasserna för de tre modellerna är UserControl, Controloch FrameworkElement.
Härledande från UserControl
Det enklaste sättet att skapa en kontroll i WPF är att härleda från UserControl. När du skapar en kontroll som ärver från UserControllägger du till befintliga komponenter i UserControl, namnger komponenterna och referenshändelsehanterare i XAML. Du kan sedan referera till de namngivna elementen och definiera händelsehanterarna i koden. Den här utvecklingsmodellen liknar den modell som används för programutveckling i WPF.
Om det skapas på rätt sätt kan en UserControl dra nytta av fördelarna med omfattande innehåll, format och utlösare. Men om kontrollen ärver från UserControlkan personer som använder din kontroll inte använda en DataTemplate eller ControlTemplate för att anpassa utseendet på den. Det är nödvändigt att härleda från klassen Control eller någon av dess härledda klasser (förutom UserControl) för att skapa en anpassad kontroll som stöder mallar.
Fördelar med att härleda från UserControl
Överväg att härleda från UserControl om alla följande gäller:
Du vill skapa din kontroll på samma sätt som du skapar ett program.
Kontrollen består bara av befintliga komponenter.
Du behöver inte ha stöd för komplex anpassning.
Härstammar från kontroll
Den modell som härstammar från klassen Control är den som används av de flesta befintliga WPF-kontrollerna. När du skapar en kontroll som ärver från klassen Control definierar du dess utseende med hjälp av mallar. På så sätt separerar du den operativa logiken från den visuella representationen. Du kan också se till att användargränssnittet och logiken avkodas genom att använda kommandon och bindningar i stället för händelser och undvika att referera till element i ControlTemplate när det är möjligt. Om användargränssnittet och logiken för kontrollen är korrekt frikopplade kan en användare av kontrollen omdefiniera kontrollens ControlTemplate för att anpassa dess utseende. Även om det inte är lika enkelt att skapa en anpassad Control som att skapa en UserControlger en anpassad Control mest flexibilitet.
Fördelar med att härleda från kontroll
Överväg att härleda från Control i stället för att använda klassen UserControl om något av följande gäller:
Du vill att kontrollens utseende ska vara anpassningsbart via ControlTemplate.
Du vill att kontrollen ska stödja olika teman.
Härleds från FrameworkElement
Kontroller som härleds från UserControl eller Control förlitar sig på att skapa befintliga element. I många scenarier är detta en acceptabel lösning eftersom alla objekt som ärver från FrameworkElement kan finnas i en ControlTemplate. Det finns dock tillfällen då en kontrolls utseende kräver mer än funktionerna i enkel elementsammansättning. För dessa scenarier är det rätt val att basera en komponent på FrameworkElement.
Det finns två standardmetoder för att skapa FrameworkElement-baserade komponenter: direktrendering och anpassad elementsammansättning. Direktrendering innebär att åsidosätta metoden OnRender för FrameworkElement och tillhandahålla DrawingContext operationer som uttryckligen definierar komponentens visuella delar. Det här är den metod som används av Image och Border. Anpassad elementsammansättning omfattar användning av objekt av typen Visual för att skapa utseendet på komponenten. Ett exempel finns i Using DrawingVisual Objects. Track är ett exempel på en kontroll i WPF som använder anpassad elementsammansättning. Det är också möjligt att blanda direktrendering och anpassad elementsammansättning i samma kontroll.
Fördelar med att härleda från FrameworkElement
Överväg att härleda från FrameworkElement om något av följande gäller:
Du vill ha exakt kontroll över kontrollens utseende utöver vad som tillhandahålls av enkel elementsammansättning.
Du vill definiera kontrollens utseende genom att definiera din egen renderingslogik.
Du vill skapa befintliga element på nya sätt som går utöver vad som är möjligt med UserControl och Control.
Kontrollera grunderna för redigering
Som tidigare nämnts är en av de mest kraftfulla funktionerna i WPF möjligheten att gå utöver att ange grundläggande egenskaper för en kontroll för att ändra dess utseende och beteende, men ändå inte behöver skapa en anpassad kontroll. Funktionerna för formatering, databindning och utlösare möjliggörs av WPF-egenskapssystemet och WPF-händelsesystemet. I följande avsnitt beskrivs några metoder som du bör följa, oavsett vilken modell du använder för att skapa den anpassade kontrollen, så att användare av din anpassade kontroll kan använda dessa funktioner på samma sätt som för en kontroll som ingår i WPF.
Använd beroendeegenskaper
När en egenskap är en beroende egenskap kan du göra följande:
Ange egenskapen i ett format.
Binda egenskapen till en datakälla.
Använd en dynamisk resurs som egenskapens värde.
Animera egenskapen.
Om du vill att en egenskap för din kontroll ska ha stöd för någon av de här funktionerna bör du implementera den som en beroendeegenskap. I följande exempel definieras en beroendeegenskap med namnet Value
genom att göra följande:
Definiera en DependencyProperty identifierare med namnet
ValueProperty
som ettpublic
static
readonly
fält.Registrera egenskapsnamnet med egenskapssystemet genom att anropa DependencyProperty.Registerför att ange följande:
Namnet på egenskapen.
Egenskapens typ.
Den typ som äger egendomen.
Metadata för egenskapen. Metadata innehåller egenskapens standardvärde, en CoerceValueCallback och en PropertyChangedCallback.
Definiera en CLR-omslutningsegenskap med namnet
Value
, som är samma namn som används för att registrera beroendeegenskapen genom att implementera egenskapensget
- ochset
-åtkomst. Observera attget
- ochset
-åtkomsterna endast anropar GetValue respektive SetValue. Vi rekommenderar att åtkomsterna till beroendeegenskaper inte innehåller ytterligare logik eftersom klienter och WPF kan kringgå åtkomsterna och anropa GetValue och SetValue direkt. När en egenskap till exempel är bunden till en datakälla anropas inte egenskapensset
-accessor. I stället för att lägga till ytterligare logik i get- och set-åtkomsterna använder du delegeringarna ValidateValueCallback, CoerceValueCallbackoch PropertyChangedCallback för att svara på eller kontrollera värdet när det ändras. Mer information om dessa återanrop finns i Återanrop för beroendeegenskaper och validering.Definiera en metod för CoerceValueCallback med namnet
CoerceValue
.CoerceValue
ser till attValue
är större eller lika medMinValue
och mindre än eller lika medMaxValue
.Definiera en metod för PropertyChangedCallbackmed namnet
OnValueChanged
.OnValueChanged
skapar ett RoutedPropertyChangedEventArgs<T> objekt och förbereder för att utlösa denValueChanged
routade händelsen. Routade händelser beskrivs i nästa avsnitt.
/// <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
Mer information finns i egenanpassade beroendeegenskaper.
Använda routade händelser
Precis som beroendeegenskaper utökar begreppet CLR-egenskaper med ytterligare funktioner utökar routade händelser begreppet CLR-standardhändelser. När du skapar en ny WPF-kontroll är det också bra att implementera händelsen som en dirigerad händelse eftersom en dirigerad händelse stöder följande beteende:
Händelser kan hanteras på en överordnad enhet av flera kontroller. Om en händelse är en bubblande händelse kan en enda överordnad i elementträdet prenumerera på händelsen. Sedan kan programförfattare använda en hanterare för att svara på händelsen med flera kontroller. Om din kontroll till exempel är en del av varje objekt i en ListBox (eftersom den ingår i en DataTemplate) kan programutvecklaren definiera händelsehanteraren för din kontrolls händelse på ListBox. När händelsen inträffar på någon av kontrollerna anropas händelsehanteraren.
Routade händelser kan användas i en EventSetter, vilket gör det möjligt för programutvecklare att ange hanteraren för en händelse inom en stil.
Routade händelser kan användas i en EventTrigger, vilket är användbart för att animera egenskaper med hjälp av XAML. Mer information finns i översikten över animering.
I följande exempel definieras en dirigerad händelse genom att göra följande:
Definiera en RoutedEvent identifierare med namnet
ValueChangedEvent
som ettpublic
static
readonly
fält.Registrera den dirigerade händelsen genom att anropa metoden EventManager.RegisterRoutedEvent. Exemplet anger följande information när den anropar RegisterRoutedEvent:
Namnet på händelsen är
ValueChanged
.Routningsstrategin är Bubble, vilket innebär att en händelsehanterare på källan (objektet som genererar händelsen) anropas först och sedan anropas händelsehanterare på källans överordnade element i följd, med början med händelsehanteraren för närmaste överordnade element.
Typen av händelsehanterare är RoutedPropertyChangedEventHandler<T>, konstruerad med en Decimal typ.
Ägandetypen för händelsen är
NumericUpDown
.
Deklarera en offentlig händelse med namnet
ValueChanged
, som inkluderar deklarationer för händelseåtkomst. Exemplet anropar AddHandler iadd
-åtkomstdeklarationen och RemoveHandler iremove
-åtkomstdeklarationen för att använda WPF-händelsetjänsterna.Skapa en skyddad virtuell metod med namnet
OnValueChanged
som genererar händelsenValueChanged
.
/// <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
Mer information finns i Översikt över routade händelser och Skapa en anpassad dirigerad händelse.
Använd bindning
Om du vill frikoppla användargränssnittet för din kontroll från dess logik bör du överväga att använda databindning. Detta är särskilt viktigt om du definierar kontrollens utseende med hjälp av en ControlTemplate. När du använder databindning kanske du kan eliminera behovet av att referera till specifika delar av användargränssnittet från koden. Det är en bra idé att undvika att referera till element som finns i ControlTemplate eftersom när koden refererar till element som finns i ControlTemplate och ControlTemplate ändras, måste det refererade elementet inkluderas i den nya ControlTemplate.
I följande exempel uppdateras TextBlock för NumericUpDown
-kontrollen genom att tilldela ett namn och sedan referera till textrutan efter namn i koden.
<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
I följande exempel används bindning för att åstadkomma samma sak.
<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>
Mer information om databindning finns i Översikt över databindning.
Design för designers
Om du vill få stöd för anpassade WPF-kontroller i WPF Designer för Visual Studio (till exempel egenskapsredigering med fönstret Egenskaper) följer du dessa riktlinjer. Mer information om hur du utvecklar för WPF Designer finns i Design XAML i Visual Studio.
Beroendeegenskaper
Se till att implementera CLR-get
och set
-accessorer som beskrivs tidigare i "Använd beroendeegenskaper". Designers kan använda wrappern för att identifiera förekomsten av en beroendeegenskap, men de, såsom WPF och kontrollklienter, behöver inte anropa accessorerna när de hämtar eller ställer in egenskapen.
Anslutna egenskaper
Du bör implementera anslutna egenskaper för anpassade kontroller med hjälp av följande riktlinjer:
Ha en
public
static
readonly
DependencyProperty i formen av PropertyNameProperty
som skapades med hjälp av metoden RegisterAttached. Egenskapsnamnet som skickas till RegisterAttached måste matcha PropertyName.Implementera ett par
public
static
CLR-metoder med namnetSet
PropertyName ochGet
PropertyName. Båda metoderna bör acceptera en klass som härleds från DependencyProperty som sitt första argument. MetodenSet
PropertyName accepterar också ett argument vars typ matchar den registrerade datatypen för egenskapen. MetodenGet
PropertyName ska returnera ett värde av samma typ. Om metodenSet
PropertyName saknas, markeras egenskapen som skrivskyddat.Set
PropertyName ochGet
PropertyName måste dirigeras direkt till metoderna GetValue respektive SetValue på målberoendeobjektet. Designers kan komma åt den anslutna egenskapen genom att anropa via metodomslutningen eller göra ett direkt anrop till målberoendeobjektet.
Mer information om bifogade egenskaper finns i Översikt över bifogade egenskaper.
Definiera och använda delade resurser
Du kan inkludera din kontroll i samma sammansättning som ditt program, eller så kan du paketera kontrollen i en separat sammansättning som kan användas i flera program. För det mesta gäller informationen som beskrivs i det här avsnittet oavsett vilken metod du använder. Det finns dock en skillnad som är värd att notera. När du placerar en kontroll i samma sammansättning som ett program kan du lägga till globala resurser i Filen App.xaml. Men en sammansättning som endast innehåller kontroller har inte ett Application objekt som är associerat med det, så en App.xaml-fil är inte tillgänglig.
När ett program söker efter en resurs tittar det på tre nivåer i följande ordning:
Elementnivån.
Systemet börjar med elementet som refererar till resursen och söker sedan efter resurser i den logiska överordnade filen och så vidare tills rotelementet har nåtts.
Programnivån.
Resurser som definieras av objektet Application.
Temanivån.
Ordlistor på temanivå lagras i en undermapp med namnet Teman. Filerna i mappen Teman motsvarar teman. Du kan till exempel ha Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml och så vidare. Du kan också ha en fil med namnet generic.xaml. När systemet letar efter en resurs på temannivå letar det först efter den i den temaspecifika filen och letar sedan efter den i generic.xaml.
När kontrollen finns i en sammansättning som är separat från programmet måste du placera dina globala resurser på elementnivå eller på temanivå. Båda metoderna har sina fördelar.
Definiera resurser på elementnivå
Du kan definiera delade resurser på elementnivå genom att skapa en anpassad resursordlista och sammanfoga den med kontrollens resursordlista. När du använder den här metoden kan du ge resursfilen namnet vad du vill och den kan finnas i samma mapp som kontrollerna. Resurser på elementnivå kan också använda enkla strängar som nycklar. I följande exempel skapas en LinearGradientBrush resursfil med namnet 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>
När du har definierat ordlistan måste du sammanfoga den med kontrollens resursordlista. Du kan göra detta med hjälp av XAML eller kod.
I följande exempel sammanfogas en resursordlista med hjälp av XAML.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Nackdelen med den här metoden är att ett ResourceDictionary objekt skapas varje gång du refererar till det. Om du till exempel har 10 anpassade kontroller i biblioteket och sammanfogar de delade resursordlistorna för varje kontroll med hjälp av XAML skapar du 10 identiska ResourceDictionary objekt. Du kan undvika detta genom att skapa en statisk klass som sammanfogar resurserna i koden och returnerar den resulterande ResourceDictionary.
I följande exempel skapas en klass som returnerar en delad ResourceDictionary.
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;
}
I följande exempel sammanfogas den delade resursen med resurserna för en anpassad kontroll i kontrollens konstruktor innan den anropar InitializeComponent
. Eftersom SharedDictionaryManager.SharedDictionary
är en statisk egenskap skapas ResourceDictionary bara en gång. Eftersom resursordlistan sammanfogades innan InitializeComponent
anropades är resurserna tillgängliga för kontrollen i XAML-filen.
public NumericUpDown()
{
this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
InitializeComponent();
}
Definiera resurser på temanivå
MED WPF kan du skapa resurser för olika Windows-teman. Som kontrollförfattare kan du definiera en resurs för ett specifikt tema för att ändra kontrollens utseende beroende på vilket tema som används. Till exempel skiljer sig utseendet på en Button i det klassiska Windows-temat (standardtemat för Windows 2000) från en Button i Windows Luna-temat (standardtemat för Windows XP) eftersom Button använder olika ControlTemplate för varje tema.
Resurser som är specifika för ett tema sparas i en resursordlista med ett specifikt filnamn. Filerna måste finnas i en mapp med namnet Themes
som är en undermapp till mappen som innehåller kontrollen. I följande tabell visas resursordlistefilerna och det tema som är associerat med varje fil:
Filnamn för resursordlista | Windows-tema |
---|---|
Classic.xaml |
Utseende från klassiska Windows 9x/2000 på Windows XP |
Luna.NormalColor.xaml |
Standardblå tema i Windows XP |
Luna.Homestead.xaml |
Olivtema i Windows XP |
Luna.Metallic.xaml |
Silvertema i Windows XP |
Royale.NormalColor.xaml |
Standardtema i Windows XP Media Center Edition |
Aero.NormalColor.xaml |
Standardtema i Windows Vista |
Du behöver inte definiera en resurs för varje tema. Om en resurs inte har definierats för ett specifikt tema kontrollerar kontrollen Classic.xaml
för resursen. Om resursen inte har definierats i filen som motsvarar det aktuella temat eller i Classic.xaml
använder kontrollen den allmänna resursen, som finns i en resursordlistefil med namnet generic.xaml
. Den generic.xaml
filen finns i samma mapp som de temaspecifika resursordlistefilerna. Även om generic.xaml
inte motsvarar ett specifikt Windows-tema är det fortfarande en ordlista på temanivå.
Den anpassade kontrollen C# eller Visual Basic NumericUpDown med stödexempel för tema- och gränssnittsautomatisering innehåller två resursordlistor för NumericUpDown
-kontrollen: den ena är i generic.xaml och den andra finns i Luna.NormalColor.xaml.
När du placerar en ControlTemplate i någon av de temaspecifika resursordlistefilerna måste du skapa en statisk konstruktor för kontrollen och anropa metoden OverrideMetadata(Type, PropertyMetadata) på DefaultStyleKey, som du ser i följande exempel.
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown),
new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
Definiera och referera till nycklar för temaresurser
När du definierar en resurs på elementnivå kan du tilldela en sträng som dess nyckel och komma åt resursen via strängen. När du definierar en resurs på temanivå måste du använda en ComponentResourceKey som nyckel. I följande exempel definieras en resurs i 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>
Följande exempel refererar till resursen genom att ange ComponentResourceKey som nyckel.
<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>
Ange platsen för temaresurser
För att hitta resurserna för en kontroll måste värdprogrammet veta att sammansättningen innehåller kontrollspecifika resurser. Du kan göra det genom att lägga till ThemeInfoAttribute till sammansättningen som innehåller kontrollen. ThemeInfoAttribute har en egenskap för GenericDictionaryLocation som anger platsen för allmänna resurser och en ThemeDictionaryLocation egenskap som anger platsen för de temaspecifika resurserna.
I följande exempel anges egenskaperna GenericDictionaryLocation och ThemeDictionaryLocation till SourceAssembly, för att ange att de allmänna och temaspecifika resurserna finns i samma sammansättning som kontrollen.
[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly,
ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>
Se även
.NET Desktop feedback