Есть ли способ отменить регистрацию свойства зависимостей WPF? - PullRequest
9 голосов
/ 29 сентября 2008

Я столкнулся с необычной проблемой в моих юнит-тестах. Класс, который я тестирую, создает свойство зависимости динамически во время выполнения, и тип этого свойства зависимости может варьироваться в зависимости от обстоятельств. При написании моих модульных тестов мне нужно создать свойство зависимости с разными типами, что приведет к ошибкам, потому что вы не можете переопределить существующее свойство зависимости.

Так есть ли способ отменить регистрацию свойства зависимости или изменить тип существующего свойства зависимости?

Спасибо!


OverrideMetadata () позволяет изменять только несколько вещей, например, значение по умолчанию, поэтому это бесполезно. Подход AppDomain является хорошей идеей и может сработать, но кажется более сложным, чем я действительно хотел бы углубиться ради модульного тестирования.

Я так и не нашел способ отменить регистрацию свойства зависимостей, поэтому я пробовал и тщательно реорганизовал свои модульные тесты, чтобы избежать этой проблемы. Я получаю немного меньше тестового покрытия, но, поскольку эта проблема никогда не возникнет в реальном приложении, и только во время модульного тестирования я могу с этим справиться.

Спасибо за помощь!

Ответы [ 6 ]

8 голосов
/ 11 сентября 2009

У меня была похожая проблема только вчера, когда я пытался протестировать свой собственный класс создания DependencyProperty. Я наткнулся на этот вопрос и заметил, что не было реального решения для отмены регистрации свойств зависимостей. Поэтому я немного покопался, используя Red Gate .NET Reflector , чтобы посмотреть, что я могу придумать.

Глядя на перегрузки DependencyProperty.Register, все они, похоже, указывают на DependencyProperty.RegisterCommon. Этот метод состоит из двух частей:

Сначала проверьте, если недвижимость уже зарегистрирована

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

Второе, регистрация свойства DependencyProperty

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

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

Обе части сосредоточены вокруг DependencyProperty.PropertyFromName, HashTable. Я также заметил DependencyProperty.RegisteredPropertyList, ItemStructList<DependencyProperty>, но не видел, где он используется. Однако, ради безопасности, я решил, что постараюсь убрать это, если это возможно.

Итак, я получил следующий код, который позволил мне «отменить регистрацию» свойства зависимости.

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

Это сработало достаточно для того, чтобы я смог выполнить свои тесты без исключения «AlreadyRegistered». Тем не менее, я настоятельно рекомендую вам не использовать это в каком-либо производственном коде. Вероятно, есть причина, по которой MSFT решила не использовать формальный способ отмены регистрации свойства зависимости и попыталась пойти против этого. просто просит неприятностей.

2 голосов
/ 30 сентября 2008

Если ничего не помогает, вы можете создать новый домен приложения для каждого теста.

0 голосов
/ 09 февраля 2018

Возникла проблема с ContentPresenter с различными шаблонами данных, где у одного из них было свойство DependencyProperty с PropertyChangedCallback При изменении содержимого ContentPresenters на другой шаблон данных обратный вызов сохранялся.

В событии UserControls Unloaded я вызвал:

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

Это сработало для меня

0 голосов
/ 24 ноября 2010

Я столкнулся со сценарием, в котором я создал пользовательский элемент управления, наследуемый от Selector, который должен иметь два свойства ItemsSource, HorizontalItemsSource и VerticalItemsSource.

Я даже не использую свойство ItemsControl и не хочу, чтобы пользователь мог получить к нему доступ.

Итак, я прочитал отличный ответ Стейтенджасона , и он дал мне огромное POV о том, как удалить DP.
Однако моя проблема заключалась в том, что, поскольку я объявил элемент ItemsSourceProperty и ItemsSource как Private Shadows (private new в C #), я не смог загрузить его во время разработки, поскольку использование MyControlType.ItemsSourceProperty будет ссылаться на теневая переменная.
Кроме того, при использовании упомянутого выше цикла (foreach DictionaryEntry и т. Д.) У меня возникло исключение, говорящее о том, что коллекция изменилась во время итерации.

Поэтому я предложил несколько иной подход, в котором DependencyProperty жестко кодируется во время выполнения, а коллекция копируется в массив, поэтому она не изменяется (VB.NET, извините):

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)

Важное примечание: весь приведенный выше код заключен в общий конструктор пользовательского элемента управления, и мне не нужно проверять, зарегистрирован ли он, потому что я знаю, что подкласс Selcetor ДЕЛАЕТ при условии, что ItemsSource дп.

0 голосов
/ 14 июня 2009

Если мы зарегистрируем имя для Ярлыка как это:

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

Мы можем легко отменить регистрацию имени, используя:

this.UnregisterName(myLabel.Name);
0 голосов
/ 29 сентября 2008

Не думаю, что вы можете отменить регистрацию свойства зависимостей, но вы можете переопределить его, переопределив метаданные следующим образом:

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