Ok – I’m pulling my hair out over what I thought was a simple scenario: create a custom Label for bilingual use that contained two additional properties (EnglishText, FrenchText). Currently its structured like this:
Public Class myCustomLabel
Inherits System.Windows.Controls.Label
Public myEnglishTextProperty As DependencyProperty = DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
Public myFrenchTextProperty As DependencyProperty = DependencyProperty.Register("myFrenchText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("Francais", New PropertyChangedCallback(AddressOf TextChanged)))
Public Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(myCustomLabel), New FrameworkPropertyMetadata(GetType(myCustomLabel)))
End Sub
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Public Property myFrenchText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Private Sub TextChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If DesignerProperties.GetIsInDesignMode(Me) = True Then
Me.Content = myEnglishText
Else
If myUser.Language = "E" Then
Me.Content = myEnglishText
Else
Me.Content = myFrenchText
End If
End If
End Sub
End Class
My test window grid xaml is simple:
<Grid>
<my:myCustomLabel myEnglishText="English Text" myFrenchText="English Text" Height="25" Width="100" Background="Aqua" Foreground="Black"/>
</Grid>
This seems to work in the development environment – changing the English and French texts change the in the design preview and it works when the app runs and the test window is opened. But only the first time – if I open the test window a second time I receive the following message:
‘myEnglishText’ property was already
registered by ‘myCustomLabel’.
I understand now that if I change the dependency property declarations to shared then this problem goes away – but that leads to a host of other problems like the callback function being required to be shared as well – and thus unable to update the Content (which needs to be instantiated with the class). All I really want is the content property to be updated in design time when the english and french labels are changed.
Is there a way around this? Or maybe are dependency properties overkill for what I need?
You are registering your dependency properties as instance variables, and during the instance constructor. So they are getting registered again every time you instantiate the control, which causes an error the second time. As you have found out, dependency properties need to be static (Shared) members:
You probably need to call OverrideMetadata in your shared constructor (type initialiser) rather than your instance constructor as well.
Regarding your issue with the callback needing to be shared: yes, it will be, but one of the arguments to the callback is the label instance. So you can just cast that to label and call an instance method on that:
(forgive C# syntax)