Frage

Ich laufe in ein ungewöhnliches Problem in meinen Unit-Tests. Die Klasse-I-Tests sind schafft eine Abhängigkeitseigenschaft dynamisch zur Laufzeit und die Art dieser Abhängigkeitseigenschaft kann je nach den Umständen unterschiedlich sein. Während meiner Unit-Tests zu schreiben, muß ich die Abhängigkeitseigenschaft mit verschiedenen Arten schaffen und das führt zu Fehlern, da Sie eine bestehende Abhängigkeitseigenschaft nicht neu definieren können.

So ist es eine Möglichkeit, eine Abhängigkeitseigenschaft entweder un-Register oder die Art einer bestehenden Abhängigkeitseigenschaft zu ändern?

Danke!


OverrideMetadata () können Sie nur sehr wenige Dinge wie Standardwert zu ändern, so dass es nicht hilfreich ist. Der AppDomain Ansatz ist eine gute Idee und könnte funktioniert aber scheint komplizierter als ich wirklich in aus Gründen der Unit-Tests vertiefen wollte.

Ich habe nie einen Weg finden, eine Abhängigkeitseigenschaft deregistrieren, damit ich punted und sorgfältig meine Unit-Tests neu organisiert, um das Problem zu vermeiden. Ich bin ein bisschen weniger Testabdeckung bekommen, aber da dieses Problem auftreten würde nie in einer realen Anwendung und nur während der Unit-Tests kann ich damit leben.

Danke für die Hilfe!

War es hilfreich?

Lösung

Ich hatte ähnliches Problem gerade gestern, wenn ich meine eigene DependencyProperty Schaffung Klasse zu testen. Ich kam in dieser Frage, und bemerkte, dass es keine wirkliche Lösung war Abhängigkeitseigenschaften deregistrieren. Also habe ich einige Graben mit Red Gate .NET Reflector , um zu sehen, was ich könnte kommen mit.

Mit Blick auf den DependencyProperty.Register Überlastungen, sie schienen alle DependencyProperty.RegisterCommon zu zeigen. Das Verfahren besteht aus zwei Teilen:

Zuerst prüfen, ob das Objekt bereits registriert ist

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

Zweitens Registrierung der DependencyProperty

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

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

Beide Stücke drehen sich um DependencyProperty.PropertyFromName, eine HashTable. Ich habe auch bemerkt die DependencyProperty.RegisteredPropertyList, eine ItemStructList<DependencyProperty> aber nicht gesehen haben, wo sie verwendet wird. Doch für die Sicherheit, dachte ich, ich würde versuchen, aus, dass auch, wenn möglich, zu entfernen.

So gewickelt ich mit dem folgenden Code, das mir eine Abhängigkeitseigenschaft erlaubt „abzumelden“.

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);
}

Es funktionierte gut genug für mich, meine Tests ausführen, ohne eine „AlreadyRegistered“ Ausnahme zu bekommen. Allerdings empfehle ich dringend, dass Sie Sie dies nicht in jeder Art von Produktionscode verwenden. Es ist wahrscheinlich ein Grund dafür, dass MSFT wählte keine formalen Weg, um eine Abhängigkeitseigenschaft deregistrieren und gegen gehen versuchen, es ist nur Ärger bringen.

Andere Tipps

Wenn alles andere fehlschlägt, können Sie eine neue AppDomain für jeden Test erstellen.

Ich glaube nicht, können Sie eine Abhängigkeitseigenschaft un-Register, aber Sie können es so durch Überschreiben der Metadaten neu zu definieren:

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

Wenn wir registrieren Name für ein Label wie folgt aus:

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

Wir können leicht den Namen deregistrieren, indem Sie:

this.UnregisterName(myLabel.Name);

Ich war mit Blick auf Szenario, in dem ich eine benutzerdefinierte Steuerung geschaffen, die von Selector erbt, die zwei Itemssource Eigenschaften haben gemeint, HorizontalItemsSource und VerticalItemsSource.

Ich benutze nicht einmal die Itemscontrol-Eigenschaft, und will nicht die Benutzer in der Lage sein, darauf zuzugreifen.

So las ich statenjason die große Antwort , und es gab mir einen großen POV, wie ein DP zu entfernen.
Aber mein Problem war, dass da ich das ItemsSourceProperty Element und das ItemsSource als Private Shadows (private new in C #) erklärte, konnte ich laden Sie es nicht zur Entwurfszeit seit MyControlType.ItemsSourceProperty mit auf den beschatteten Variable beziehen würde.
Auch wenn in erwähnter die Schleife ist enswer oben (foreach DictionaryEntry etc.), hatte ich eine Ausnahme geworfen sagen, dass die Sammlung während der Iteration verändert hat.

Deshalb kam ich mit einem etwas anderen Ansatz, wo die DependencyProperty hardcodedly zur Laufzeit refered ist, und die Sammlung Array kopiert wird, so ist es nicht geändert (VB.NET, sorry):

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)

Wichtiger Hinweis: Der obige Code wird alle im Shared Konstruktor des benutzerdefinierten Steuerelement umgeben, und ich habe nicht mehr zu überprüfen, ob es registriert ist, weil ich, dass eine Unterklasse von Selcetor kennen bietet, die ItemsSource dp.

Sie hat ein Problem mit einem Content mit verschiedenen Datatemplates, wo einer von ihnen mit einem PropertyChangedCallback eine DependencyProperty hatte Wenn ContentPresenters Inhalt an einen anderen Datatemplate der Rückruf blieb ändern.

In den Benutzersteuerelementen Unloaded Ereignisse i genannt:

BindingOperations.ClearAllBindings(this);
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { return null; }), null);

Das ist für mich gearbeitet

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top