WPF自定义控制和通过DependencyProperty公开属性

时间:2009-09-03 01:44:55

标签: wpf controls properties dependencies

好的 - 我正在把头发拉出我认为是一个简单的场景:为双语使用创建一个包含两个附加属性的自定义标签(EnglishText,FrenchText)。目前它的结构如下:

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

我的测试窗口网格xaml很简单:

<Grid>
        <my:myCustomLabel myEnglishText="English Text" myFrenchText="English Text" Height="25" Width="100" Background="Aqua" Foreground="Black"/>
</Grid>

这似乎适用于开发环境 - 更改英语和法语文本会更改设计预览,并且当应用程序运行并打开测试窗口时它会起作用。但只是第一次 - 如果我第二次打开测试窗口,我会收到以下消息:

  

'myEnglishText'属性已经存在   由'myCustomLabel'注册。

我现在明白,如果我将依赖属性声明更改为共享,那么这个问题就会消失 - 但这会导致许多其他问题,例如需要共享回调函数 - 因此无法更新内容(需要用类实例化)。 我真正想要的是在更改英文和法文标签时设计时更新的内容属性。

有解决方法吗?或者也许依赖属性过度杀伤我需要什么?

3 个答案:

答案 0 :(得分:7)

您正在将依赖项属性注册为实例变量,并在实例构造函数中注册。因此,每次实例化控件时,它们都会再次注册,这会导致第二次出错。如您所知,依赖属性需要是静态(共享)成员:

Public Shared myEnglishTextProperty As DependencyProperty =   
  DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel),
  New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))

您可能需要在共享构造函数(类型初始化函数)中调用OverrideMetadata而不是实例构造函数。

关于需要共享回调的问题:是的,它会是,但回调的一个参数是标签实例。所以你可以将它转换为标签并在其上调用实例方法:

private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  ((MyLabel)d).TextChanged();
}

private void TextChanged()
{
  // your code here
}

(原谅C#语法)

答案 1 :(得分:2)

您是否因为访问“我”实例而不希望共享回调方法?如果就是这样,请将其共享并使用“d”参数。我不太了解VB以显示代码,但只需创建一个myCustomLabel类型的变量并为其指定“d”(使用强制转换)。然后使用该变量(比如说“lbl”):

If  DesignerProperties.GetIsInDesignMode(lbl) = True Then
    lbl.Content = myEnglishText
Else
    If myUser.Language = "E" Then
        lbl.Content = myEnglishText
    Else
        lbl.Content = myFrenchText
    End If
End If

答案 2 :(得分:1)

此外,您的示例代码中存在轻微错误。试试这个:

Public Property myEnglishText() As String
    Get
        Return MyBase.GetValue(myEnglishTextProperty)
    End Get
    Set(ByVal value As String)
        MyBase.SetValue(myEnglishTextProperty, value)
    End Set
End Property

而不是:

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