Tipps und Tricks

Frage (552) zu VB(14).NET (VS2015), WPF:

WPF-Nutzersteuerelement mit zusätzlichen Eigenschaften, die im Eigenschaftsfenster des Visual Studio bearbeitet werden.

Antwort:

Wenn man ein WPF-Nutzersteuerelement mit Eigenschaften erweitert, dann kann man, wenn man das Steuerelement im XAML nutzt, diese Eigenschaften im Eigenschaftsfenster bearbeiten. Es kann jedoch erforderlich sein, dass die Standardbearbeitung (z.B. TextBox im Eigenschaftsfenster) für eine effektive Arbeitsweise nicht ausreicht. Dafür bietet das Visual Studio Bibliotheken, mit denen man Funktionen zur Bearbeitung einer Eigenschaft für das konkrete Steuerelement hinzufügen kann. Diese Möglichkeit bietet auch die Comminity Edition des Visual Studio 2015. Im folgenden Beispiel werden die erforderlichen Schritte beschrieben.

1. Bibliothek für UserControls anlegen (im Beispiel WpfControlLibrary1)

2. UserControl aufbauen (im Beispiel UserControl1)

3. Im XAML ist im vorliegenden Beispiel ein Label-Steuerelement vorhanden, in dem der Modus des Steuerelementes beispielhaft angezeigt wird:

[xml]

<UserControl x:Class="UserControl1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:local="clr-namespace:WpfControlLibrary1"

mc:Ignorable="d"

d:DesignHeight="300" d:DesignWidth="300">

<Grid>

<Label Name="lbl"/>

</Grid>

</UserControl>

[/xml]

4. Im CodeBehind das UserControls Code für Eigenschaft hinzufügen (z.B. Eigenschaften "Eigenschaft " vom Typ "Guid" und "Filename" vom Typ String)

[vb]

Imports System.ComponentModel

Public Class UserControl1

Public Sub New()

' This call is required by the designer.

InitializeComponent()

' Add any initialization after the InitializeComponent() call.

If System.ComponentModel.DesignerProperties.GetIsInDesignMode(Me) Then

Me.lbl.Content = "Entwickler-Modus"

Else

Me.lbl.Content = "Ausführungs-Modus"

End If

End Sub

Public Shared ReadOnly FileNameProperty As DependencyProperty = DependencyProperty.Register(

"FileName",

GetType(String),

GetType(UserControl1),

New PropertyMetadata("File name not set."))

<Description("Dateiname eintragen")>

<Category("Meine Eigenschaften")>

Public Property FileName() As String

Get

Return CType(Me.GetValue(EigenschaftProperty), String)

End Get

Set(ByVal value As String)

Me.SetValue(EigenschaftProperty, value)

End Set

End Property

Public Shared ReadOnly EigenschaftProperty As DependencyProperty =

DependencyProperty.Register("Eigenschaft", GetType(Guid), GetType(UserControl1), New PropertyMetadata(Nothing))

<Description("Neue GUID eintragen")>

<Category("Meine Eigenschaften")>

<DefaultValue(GetType(Guid), "00000000-0000-0000-0000-000000000000")>

Public Property Eigenschaft() As Guid

Get

Return CType(Me.GetValue(EigenschaftProperty), Guid)

End Get

Set(ByVal value As Guid)

Me.SetValue(EigenschaftProperty, value)

End Set

End Property

End Class

[/vb]

5. Genutzt werden kann das Steuerelement im Hauptprogramm in üblicher Weise (natürlich Verweis auf die Steuerelementebibliothek setzen):

[xml]

<Window x:Class="MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:WpfApplication1"

xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"

mc:Ignorable="d"

Title="MainWindow" Height="350" Width="525">

<Grid>

<uc:UserControl1/>

</Grid>

</Window>

[/xml]

Es können im Eigenschaftsfenster mit den üblichen Darstellungen diese Eigenschaften bearbeitet werden, was für den Fall einer Guid recht umständlich sein kein (separater Guid-Generator). Deshalb wird ein Designer zur Bearbeitung der Eigenschaftswerte im Eigenschaftsfenster aufgebaut:

6. Bibliothek für design des UserControls anlegen (im Beispiel WpfControlLibrary1.Design)

7. Projekteigenschaften ändern:

7.1 Root Namespace löschen, um spätere Konflikte zu vermeiden. Alle Programmteile müssen dann explizit den Namensraum enthalten.

7.2 Ausgabepfad für das Übersetzungsergebnis auf den Ausgabepfad der betreffenden Bibliothek für UserControls setzen, damit beide Assemblys im gleichen Verzeichnis liegen

7.3 Target CPU auf X86 setzen, da das Visual Studio im Modus x86 arbeitet und die genutzten Bibliotheken dafür ausgelegt sind. Damit werden Warnungen im Error Log verhindert.

7.4 Referenzen für 2 benötigte Bibliotheken des Visual Studio einfügen: Microsoft.Windows.Design.Extensibility.dll und Microsoft.Windows.Design.Interaction.dll.

Diese befinden sich standardmäßig im Pfad C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PublicAssemblies

Zu beachten ist, dass jede Version des Visual Studios seine eigenen Bibliotheken mitbringt. Das betreifft auch die verschiedenen Servicepacks (z.B. im Visual Studio 2013).

7.5 Referenz auf die Nutzersteuerbibliothek setzen, da in den Metadaten die Designerfunktionalität mit den Eigenschaften des Nutzersteuerelementes verknüpft werden.

8. In einem Resourcen-Wörterbuch (im Beispiel EditorResources) das Design als Datatemplate ablegen:

[xml]

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:PropertyEditing="clr-namespace:Microsoft.Windows.Design.PropertyEditing;assembly=Microsoft.Windows.Design.Interaction"

xmlns:Local="clr-namespace:WpfControlLibrary1.Design"

x:Class="WpfControlLibrary1.Design.EditorResources">

<DataTemplate x:Key="EigenschaftsEditorTemplate">

<Grid>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

<TextBox Grid.Row="0" Text="{Binding StringValue}" Margin="2"/>

<PropertyEditing:EditModeSwitchButton Grid.Row="1" Content="New Guid" ToolTip="Neue GUID erzeugen." Margin="2"/>

</Grid>

</DataTemplate>

<DataTemplate x:Key="FileBrowserInlineEditorTemplate">

<Grid>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

<TextBox Grid.Row="0" Text="{Binding StringValue}" Margin="2"/>

<PropertyEditing:EditModeSwitchButton Grid.Row="1" Content="Neuer Dateiname" ToolTip="Neuen Dateinamen holen." Margin="2"/>

</Grid>

</DataTemplate>

</ResourceDictionary>

[/xml]

Wichtig sind die Verweise auf PropertyEditing=Microsoft.Windows.Design.Interaction und x:Class=EditorResources, was die dazugehörende Klasse für die Nutzung des Datatemplates im Code ist.

9. Klasse für den Zugriff auf die Datatemplates anlegen (im Beispiel EditorResources):

[vb]

Namespace WpfControlLibrary1.Design

Partial Public Class EditorResources

Inherits ResourceDictionary

Public Sub New()

MyBase.New

Me.InitializeComponent()

End Sub

End Class

End Namespace

[/vb]

10. Für jede Eigenschaft einen EigenschaftsEditor erstellen, der das Template bereitstellt und die Verarbeitung der Nutzeraktivitäten ausführt:

Beispiel für Guid-Eigenschaft:

[vb]

Imports Microsoft.Windows.Design.PropertyEditing

Namespace WpfControlLibrary1.Design

Public Class EigenschaftsEditor

Inherits DialogPropertyValueEditor

Private res As EditorResources = New EditorResources()

Public Sub New()

Me.InlineEditorTemplate = TryCast(res("EigenschaftsEditorTemplate"), DataTemplate)

End Sub

Public Overrides Sub ShowDialog(propertyValue As PropertyValue, commandSource As IInputElement)

propertyValue.StringValue = Guid.NewGuid().ToString()

End Sub

End Class

End Namespace

[/vb]

Beispiel für Filename-Eigenschaft:

[vb]

Imports Microsoft.Windows.Design.PropertyEditing

Imports Microsoft.Win32

Namespace WpfControlLibrary1.Design

Public Class FileBrowserDialogPropertyValueEditor

Inherits DialogPropertyValueEditor

Private res As EditorResources = New EditorResources()

Public Sub New()

Me.InlineEditorTemplate = TryCast(res("FileBrowserInlineEditorTemplate"), DataTemplate)

End Sub

Public Overrides Sub ShowDialog(propertyValue As PropertyValue, commandSource As IInputElement)

Dim ofd As New OpenFileDialog() With {.Multiselect = False}

If ofd.ShowDialog() Then propertyValue.StringValue = ofd.FileName

End Sub

End Class

End Namespace

[/vb]

11. Zum Schluss noch die Klasse für die Verknüpfung der Metainformation:

[vb]

'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\PublicAssemblies

Imports Microsoft.Windows.Design.Metadata

Imports Microsoft.Windows.Design.PropertyEditing

<Assembly: ProvideMetadata(GetType(WpfControlLibrary1.Design.Metadata))>

Namespace WpfControlLibrary1.Design

Public Class Metadata

Implements IProvideAttributeTable

Private ReadOnly Property AttributeTable As AttributeTable Implements IProvideAttributeTable.AttributeTable

Get

Dim builder As AttributeTableBuilder = New AttributeTableBuilder()

builder.AddCustomAttributes(GetType(WpfControlLibrary1.UserControl1),

"FileName",

PropertyValueEditor.CreateEditorAttribute(

GetType(FileBrowserDialogPropertyValueEditor)))

builder.AddCustomAttributes(GetType(WpfControlLibrary1.UserControl1),

"Eigenschaft",

PropertyValueEditor.CreateEditorAttribute(

GetType(EigenschaftsEditor)))

Return builder.CreateTable()

End Get

End Property

End Class

End Namespace

[/vb]

Nach der Übersetzung können die Eigenschaften im Eigenschaftsfenster bearbeitet werden. Falls die Bearbeitungsmöglichkeit (Buttons im vorliegenden Beispiel) nicht angezeigt wird, ist das Visual Studio zu beenden und neu zu starten. Das kann beispielsweise der Fall sein, wenn im Steuerelement bzw. im Desigenrcode Änderungen durchgeführt wurden und die Assembblys nicht mehr zueinander passen.

Stand des Beitrages: 02.02.16 06:07, zuletzt geändert: 02.02.16 06:53



Bitte wählen sie den Themenbereich aus
Bitte geben sie einen Suchbegriff ein

Die hier dargestellten Tipps und Tricks sind das Ergebnis selbst ersteller Lösungsvarianten, die für Projekte und Schulungen erarbeitet wurden.