Pergunta

Eu estou correndo em um problema incomum em meus testes de unidade. A classe Estou testando cria uma propriedade de dependência dinamicamente em tempo de execução eo tipo de que propriedade de dependência pode variar dependendo das circunstâncias. Ao escrever meus testes de unidade, eu preciso para criar a propriedade de dependência com diferentes tipos e que leva a erros, porque você não pode redefinir uma propriedade de dependência existente.

Então, há alguma maneira ou cancelar o registro de uma propriedade de dependência ou para alterar o tipo de uma propriedade de dependência existente?

Obrigado!


OverrideMetadata () só permite alterar algumas poucas coisas como valor padrão para que ele não é útil. A abordagem AppDomain é uma boa idéia e pode funcionar, mas parece mais complicado do que eu realmente queria se aprofundar para o bem de testes de unidade.

Eu nunca fiz encontrar uma maneira de cancelar o registro de uma propriedade de dependência para que eu punted e cuidadosamente reorganizou meus testes de unidade para evitar o problema. Estou recebendo uma cobertura pouco menos teste, mas uma vez que este problema nunca iria ocorrer em uma aplicação real e apenas durante a unidade de teste que eu posso viver com isso.

Obrigado pela ajuda!

Foi útil?

Solução

Eu tive problema semelhante ontem ao tentar testar a minha própria classe criando DependencyProperty. Me deparei com esta questão, e notou que não havia solução real para propriedades de dependência Unregister. Então eu fiz alguma escavação usando Red Gate .NET Reflector para ver o que eu poderia vir acima com.

Olhando para as sobrecargas DependencyProperty.Register, todos eles parecia apontar para DependencyProperty.RegisterCommon. Esse método tem duas partes:

Em primeiro lugar para verificar se a propriedade já está registrado

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

Em segundo lugar, Registrando o DependencyProperty

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

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

Ambas as peças giram em torno DependencyProperty.PropertyFromName, um HashTable. Notei também o DependencyProperty.RegisteredPropertyList, um ItemStructList<DependencyProperty> mas não vi onde ele é usado. No entanto, para a segurança, eu percebi que eu tente remover do que bem, se possível.

Então, eu acabei com o seguinte código que me permitiu "unregister" uma propriedade de dependência.

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

Ele funcionou bem o suficiente para eu correr meus testes sem recebendo uma exceção "AlreadyRegistered". No entanto, eu recomendo fortemente que você não use este em qualquer tipo de código de produção. Não é provável uma razão que MSFT escolhi não ter uma maneira formal para cancelar o registro de uma propriedade de dependência, e tentar ir contra é apenas pedindo para ter problemas.

Outras dicas

Se tudo isso falhar, você pode criar um novo AppDomain para cada teste.

Eu não acho que você pode cancelar o registro de uma propriedade de dependência, mas você pode redefini-lo, substituindo os metadados como esta:

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

Se registrar o nome para uma etiqueta como esta:

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

Podemos facilmente cancelar o registro o nome usando:

this.UnregisterName(myLabel.Name);

Eu estava enfrentando cenário onde eu criei um controle personalizado que herda de Selector que se destina a ter dois ItemsSource propriedades, HorizontalItemsSource e VerticalItemsSource.

Eu não até mesmo usar a propriedade ItemsControl, e não quero que o usuário seja capaz de acessá-lo.

Então eu li grande resposta , e ele me deu uma enorme POV sobre como remover um DP.
No entanto, o meu problema foi que, desde que eu declarou o membro ItemsSourceProperty eo ItemsSource como Private Shadows (private new em C #), eu não podia carregá-lo em tempo de design desde usando MyControlType.ItemsSourceProperty remete para a variável sombreada.
Além disso, quando se utiliza o circuito mencionado em cima é enswer (foreach DictionaryEntry etc), tinha uma exceção lançada dizendo que a recolha foi alterado durante a iteração.

Por isso eu vim com uma abordagem ligeiramente diferente, onde o DependencyProperty é hardcodedly referiu em tempo de execução, ea coleção é copiado para array de forma que não é alterado (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)

Nota importante: O código acima é toda cercada no construtor compartilhada do controle personalizado, e eu não tenho que verificar wether é registrado, porque eu sei que uma sub-classe de Selcetor prevê que dp ItemsSource.

teve um problema com um ContentPresenter com diferentes DataTemplates onde um deles tinha um DependencyProperty com um PropertyChangedCallback Ao alterar o conteúdo ContentPresenters para outro DataTemplate a chamada de retorno permaneceu.

No UserControls evento descarregado i chamado:

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

Isso funcionou para mim

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top