2009-09-03 9 views
1

Ok - Je suis en train de sortir mes cheveux de ce que je pensais être un scénario simple: créer une étiquette personnalisée pour une utilisation bilingue qui contenait deux propriétés supplémentaires (EnglishText, FrenchText). À l'heure actuelle sa structure comme ceci:WPF Custom Control et l'exposition des propriétés par DependencyProperty

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 

Ma grille de fenêtre de test XAML est simple:

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

Cela semble fonctionner dans l'environnement de développement - changer les textes anglais et français changer le dans l'aperçu de la conception et cela fonctionne quand l'application s'exécute et la fenêtre de test est ouverte. Mais seulement la première fois - si j'ouvre la fenêtre de test une deuxième fois que je reçois le message suivant:

propriété « myEnglishText » était déjà enregistré par « myCustomLabel ».

Je comprends maintenant que si je change les déclarations de propriété de dépendance à partager alors ce problème disparaît - mais cela conduit à une foule d'autres problèmes tels que la fonction de rappel étant nécessaire pour être partagé aussi bien - et donc incapables de mettre à jour le contenu (qui doit être instancié avec la classe). Tout ce que je veux, c'est que la propriété de contenu soit mise à jour au moment de la conception lorsque les étiquettes anglaises et françaises sont modifiées.

Y a-t-il un moyen de contourner ce problème? Ou peut-être les propriétés de dépendance exagèrent-elles pour ce dont j'ai besoin?

Répondre

7

Vous enregistrez vos propriétés de dépendance comme variables d'instance, et au cours du constructeur d'instance. Ils sont donc à nouveau enregistrés chaque fois que vous instanciez le contrôle, ce qui provoque une erreur la seconde fois. Comme vous l'avez découvert, les propriétés de dépendance doivent être membres statiques (partagés):

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

Vous devez probablement appeler OverrideMetadata dans votre constructeur partagé (type initialiseur) plutôt que votre constructeur d'instance ainsi.

En ce qui concerne votre problème avec le rappel ayant besoin d'être partagé: oui, ce sera le cas, mais l'un des arguments du rappel est l'instance de l'étiquette. Ainsi, vous pouvez simplement lancer ce à étiqueter et d'appeler une méthode d'instance sur ce point:

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

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

(pardonnez-C syntaxe #)

+0

Merci itowlson!L'objet de dépendance "d" me regardait en face tout le temps et je ne mettais jamais deux et deux ensemble. – Gatmando

2

La raison pour laquelle vous ne souhaitez pas que la méthode de rappel soit partagée est-elle la raison pour laquelle vous accédez à l'instance "moi"? Si c'est tout, partagez-le et utilisez le paramètre "d". Je ne connais pas assez bien VB pour vous montrer le code, mais juste créer une variable de type myCustomLabel et lui assigner "d" (avec un cast). Ensuite, utilisez cette variable (disons "lbl") à la place:

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 
+0

Merci Matt - même réponse que ci-dessous (je l'ai mis en réponse à cause de l'exhaustivité) mais votre était correct aussi. J'apprécie l'aide. – Gatmando

1

De plus, il y a un léger bug dans votre exemple de code. Essayez d'utiliser ceci:

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 

Au lieu de cela:

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