10 votes

Comment désenregistrer une propriété de dépendance WPF ?

Je rencontre un problème inhabituel dans mes tests unitaires. La classe que je teste crée une propriété de dépendance dynamiquement à l'exécution et le type de cette propriété de dépendance peut varier selon les circonstances. En écrivant mes tests unitaires, je dois créer la propriété de dépendance avec différents types et cela conduit à des erreurs parce que vous ne pouvez pas redéfinir une propriété de dépendance existante.

Existe-t-il un moyen d'annuler l'enregistrement d'une propriété de dépendance ou de modifier le type d'une propriété de dépendance existante ?

Gracias.


OverrideMetadata() ne permet de modifier que très peu de choses, comme la valeur par défaut, et n'est donc pas utile. L'approche AppDomain est une bonne idée et pourrait fonctionner, mais elle semble plus compliquée que ce que je voulais vraiment approfondir dans l'intérêt des tests unitaires.

Je n'ai jamais trouvé de moyen de désenregistrer une propriété de dépendance, j'ai donc renoncé et j'ai soigneusement réorganisé mes tests unitaires pour éviter ce problème. J'ai un peu moins de couverture de test, mais comme ce problème ne se produirait jamais dans une application réelle et seulement pendant les tests unitaires, je peux m'en accommoder.

Merci pour votre aide !

9voto

statenjason Points 3635

J'ai eu un problème similaire hier en essayant de tester ma propre classe de création de DependencyProperty. Je suis tombé sur cette question, et j'ai remarqué qu'il n'y avait pas de solution réelle pour désenregistrer les propriétés de dépendance. J'ai donc fait quelques recherches en utilisant Red Gate .NET Reflector pour voir ce que je pouvais trouver.

En regardant la DependencyProperty.Register surcharges, ils semblaient tous indiquer que les DependencyProperty.RegisterCommon . Cette méthode comporte deux parties :

Vérifier d'abord si le bien est déjà enregistré

FromNameKey key = new FromNameKey(name, ownerType);
lock (Synchronized)
{
  if (PropertyFromName.Contains(key))
  {
    throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", 
      new object[] { name, ownerType.Name }));
  }
}

Deuxièmement, enregistrement de la propriété de dépendance

DependencyProperty dp = 
  new DependencyProperty(name, propertyType, ownerType, 
    defaultMetadata, validateValueCallback);

defaultMetadata.Seal(dp, null);
//...Yada yada...
lock (Synchronized)
{
  PropertyFromName[key] = dp;
}

Les deux pièces s'articulent autour de DependencyProperty.PropertyFromName , une table de hachage. J'ai également remarqué que le DependencyProperty.RegisteredPropertyList , un ItemStructList<DependencyProperty> mais je n'ai pas vu où elle était utilisée. Toutefois, pour des raisons de sécurité, je me suis dit que je devais essayer de m'en débarrasser si possible.

J'ai donc obtenu le code suivant qui me permet de "désenregistrer" une propriété de dépendance.

private void RemoveDependency(DependencyProperty prop)
{
  var registeredPropertyField = typeof(DependencyProperty).
    GetField("RegisteredPropertyList", BindingFlags.NonPublic | BindingFlags.Static);
  object list = registeredPropertyField.GetValue(null);
  var genericMeth = list.GetType().GetMethod("Remove");
  try
  {
    genericMeth.Invoke(list, new[] { prop });
  }
  catch (TargetInvocationException)
  {
    Console.WriteLine("Does not exist in list");
  }

  var propertyFromNameField = typeof(DependencyProperty).
    GetField("PropertyFromName", BindingFlags.NonPublic | BindingFlags.Static);
  var propertyFromName = (Hashtable)propertyFromNameField.GetValue(null);

  object keyToRemove = null;
  foreach (DictionaryEntry item in propertyFromName)
  {
    if (item.Value == prop)
      keyToRemove = item.Key;
  }
  if (keyToRemove != null)
  propertyFromName.Remove(keyToRemove);
}

Cela a fonctionné suffisamment bien pour que je puisse exécuter mes tests sans obtenir une exception "AlreadyRegistered". Cependant, je vous recommande fortement de ne l'utilisez pas dans un code de production. Il y a probablement une raison pour laquelle MSFT a choisi de ne pas avoir de moyen formel de désenregistrer une propriété de dépendance, et tenter d'aller à l'encontre de ce choix est une source d'ennuis.

2voto

David Schmitt Points 29384

Si tout le reste échoue, vous pouvez créer un nouveau AppDomain pour chaque test.

1voto

Micah Points 28683

Je ne pense pas que vous puissiez annuler l'enregistrement d'une propriété de dépendance, mais vous pouvez la redéfinir en remplaçant les métadonnées comme ceci :

MyDependencyProperty.OverrideMetadata(typeof(MyNewType), 
                     new PropertyMetadata());

0voto

Si nous enregistrons le nom d'un label comme celui-ci :

Label myLabel = new Label();
this.RegisterName(myLabel.Name, myLabel);

Nous pouvons facilement annuler l'enregistrement du nom en utilisant :

this.UnregisterName(myLabel.Name);

0voto

Shimmy Points 23393

J'ai été confronté à un scénario dans lequel j'ai créé un contrôle personnalisé qui hérite de Selector qui doit avoir deux propriétés ItemsSource, HorizontalItemsSource y VerticalItemsSource .

Je n'utilise même pas la propriété ItemsControl et je ne veux pas que l'utilisateur puisse y accéder.

J'ai donc lu La bonne réponse de statenjason Cela m'a donné un point de vue très intéressant sur la manière de retirer un DP.
Cependant, mon problème était que, puisque j'avais déclaré la fonction ItemsSourceProperty et le ItemsSource comme Private Shadows ( private new en C#), je n'ai pas pu le charger au moment de la conception car l'utilisation de MyControlType.ItemsSourceProperty ferait référence à la variable ombrée.
De même, lorsque l'on utilise la boucle mentionnée dans la réponse ci-dessus ( foreach DictionaryEntry etc.), j'ai obtenu une exception indiquant que la collection avait changé pendant l'itération.

J'ai donc trouvé une approche légèrement différente où la DependencyProperty est référencée en dur au moment de l'exécution, et la collection est copiée dans un tableau pour ne pas être modifiée (VB.NET, désolé) :

Dim dpType = GetType(DependencyProperty)
Dim bFlags = BindingFlags.NonPublic Or BindingFlags.Static

Dim FromName = 
  Function(name As String, ownerType As Type) DirectCast(dpType.GetMethod("FromName",
    bFlags).Invoke(Nothing, {name, ownerType}), DependencyProperty)

Dim PropertyFromName = DirectCast(dpType.GetField("PropertyFromName", bFlags).
  GetValue(Nothing), Hashtable)

Dim dp = FromName.Invoke("ItemsSource", GetType(DimensionalGrid))
Dim entries(PropertyFromName.Count - 1) As DictionaryEntry
PropertyFromName.CopyTo(entries, 0)
Dim entry = entries.Single(Function(e) e.Value Is dp)
PropertyFromName.Remove(entry.Key)

Remarque importante : le code ci-dessus est entouré dans le constructeur partagé du contrôle personnalisé, et je n'ai pas besoin de vérifier s'il est enregistré, parce que je sais qu'une sous-classe de Selcetor fournit cela ItemsSource dp.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X