Ligação OneWayToSource parece interrompida no .NET 4.0

Ligação OneWayToSource parece interrompida no .NET 4.0

Eu tenho esse pedaço simples de Xaml

   

E meu código por trás parece com isso

 public MainWindow() { InitializeComponent(); this.DataContext = this; } private string m_textProperty; public string TextProperty { get { return "Should not be used in OneWayToSource Binding"; } set { m_textProperty = value; } } 

No .NET 3.5 isso funciona como você pode, exceto. Coloque algum texto na checkbox de TextBox , pressione Tab para torná-lo perder foco e o TextProperty atualiza com qualquer texto que foi inserido no TextBox

No .NET 4.0 , se eu digitar algum texto no TextBox e, em seguida, pressione Tab para torná-lo perder foco, o TextBox reverte para o valor de TextProperty (que significa “não deve ser usado em OneWayToSource Binding” ). Esta releitura destina-se a uma binding OneWayToSource no .NET 4.0? Eu só quero que o TextBox empurre seu valor para o TextProperty e não o contrário.

Atualizar
Adicionando um Bounty a esta pergunta, pois isso se tornou um inconveniente maior em meu projeto e eu gostaria de saber a razão pela qual isso mudou. Parece que get é chamado depois que a vinculação atualizou a fonte. Esse é o comportamento desejado para uma binding OneWayToSource no .NET 4.0?

Se sim

  • Qual foi o problema com o modo como funcionava no 3.5?
  • Em quais cenários esse novo comportamento é melhor?

Ou isso é, na verdade, um bug que podemos esperar consertar em uma versão futura?

O blog de Karl Shifflett e a resposta de @ Simpzon já cobrem por que eles adicionaram esse recurso e por que não é um problema para propriedades que sempre obtêm o que foi definido. Em seu próprio código, você sempre usa uma propriedade intermediária que possui a semântica apropriada para binding e usa uma propriedade interna que possui a semântica desejada. Eu chamaria a propriedade intermediária de propriedade de “bloqueio” porque impede o getter de atingir sua propriedade interna.

Mas, no caso em que você não tem access ao código-fonte da entidade na qual está definindo a propriedade e deseja o comportamento antigo, é possível usar um conversor.

Aqui está um conversor de bloqueio com estado:

 public class BlockingConverter : IValueConverter { public object lastValue; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return lastValue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { lastValue = value; return value; } } 

e você pode usá-lo para o seu exemplo como este:

          

Observe que, como o conversor tem um estado, você precisa de uma instância separada toda vez que o recurso é usado e, para isso, podemos usar o atributo x:Shared="False" no recurso.

Isso é de fato por design. Normalmente, não deve causar problemas, mas a implementação de sua propriedade é, pelo menos, não convencional, eu diria. Getters e setters (acessadores) devem realmente não fazer muito mais do que obter e definir, e cada get deve ser consistente com o último conjunto correspondente. (desculpe, não há fonte para isso, é exatamente o que chamamos de boa cidadania em todas as equipes de desenvolvimento em que estive).

Mais detalhes sobre o recurso aqui: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/

Bug, definitivamente.

se é um “recurso”, é muito ruim …

parece que eles adicionaram uma chamada à function get () depois que o set () é feito, mesmo no modo OneWayToSource … alguém pode explicar por quê?

também, obrigado por apontar isso, isso explica um problema que tive desde que eu atualizei meu projeto para o .net 4.0 e que eu não poderia explicar até agora …

Apenas uma observação: resolvi isso usando propriedades de dependência no final.

Isso é claramente um erro. Parece ter sido relatado pelo menos uma vez por alguém. https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0

Eu concordo com muitos dos outros que um caminho deve ser um período de sentido único.

Meu cenário é complicado e ter a funcionalidade alterada entre as versões do framework me causou uma dor de cabeça real.

Eu tenho uma checkbox de texto vinculada a uma propriedade. Eu tenho um conversor que muda o formato na hora. EG: Eu entro EU5 na checkbox de texto, a propriedade recebe EU005. Eu tenho o conjunto de binding para acionar na propriedade alterada como eu preciso fazer pesquisas dentro do ViewModel como o usuário digita. A nova implementação altera o valor da checkbox de texto conforme eu digito. Então, se eu quiser digitar EU512, não poderia facilmente, pois o texto da checkbox de texto continuaria mudando.

Eu tentei muitas coisas – binding explícita (onde você decide quando atualizar e qual o caminho.) Isso tem o mesmo problema. Se eu digo, UpdateSource, ele faz, mas também releia a propriedade e altera o alvo também.

Eu tentei OneWayToSource e tive o mesmo problema. Eu não encontrei nenhuma maneira de contornar isso sem fazer alterações irritantes para minha VM. A única outra maneira seria remover a binding neste campo e começar a triggersr events que seriam terríveis.

Se a MS fez a binding se comportar como é logicamente chamada, então meu problema desapareceria. Até mesmo uma propriedade na binding para optar pela implementação do .net4 e se comportar como 3.5 funcionaria para mim.

Alguém tem alguma sugestão para mim sobre como eu posso contornar isso?

Eu tive uma variação deste problema para uma binding bidirecional. Eu percebo que isso não é exatamente o mesmo que o problema que está sendo discutido, mas esta resposta surgiu enquanto procurava e levou à minha solução. Espero que alguém ache útil.

Minha solução bloqueia a releitura da propriedade de backup, mas atualiza a interface do usuário quando ela é alterada por alguma outra fonte. Eu baseei minha solução no conversor de bloqueio na resposta de Rick Sladkey.

Apenas adiciona uma verificação à conversão para ver se o campo lastValue converteria para o mesmo valor da loja lastValue . Caso contrário, o valor do repository de armazenamento deve ter sido alterado de outra origem e a interface do usuário deve ser atualizada.

 public class MyConverter : IValueConverter { public object lastValue; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (LastValue != null && MyConvertBack(LastValue).Equals(value)) return lastValue; else return MyConvert(value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { lastValue = value; return MyConvertBack(value); } private object MyConvertBack(Object value) { //Conversion Code Here } private object MyConvert(Object value) { //Conversion Code Here } } 

No meu caso particular para isso, eu tinha um sufixo de comprimento e dimensão armazenado em uma checkbox de texto (10m, 100mm, etc.). O conversor analisou isso para um valor duplo ou adicionou o sufixo (dependendo da direção da conversão). Sem o conversor, ele adicionaria um sufixo em cada atualização da checkbox de texto. Tentar digitar ’10’ resultaria em ‘1m0’, pois o conversor seria executado após o primeiro toque na tecla.

Esse é o comportamento desejado para uma binding OneWayToSource no .NET 4.0?

Sim. Isso é feito para a capacidade do desenvolvedor de alterar o valor fornecido sem conversores desajeitados.

Qual foi o problema com o modo como funcionava no 3.5?

Sem problemas. A maneira como funcionava no 3.5 não permitia corrigir os valores fornecidos.

Em quais cenários esse novo comportamento é melhor?

Quando você precisa corrigir os valores fornecidos. Se você não precisa, você deve simplesmente escrever getter e setter da propriedade correta.

 public string TextProperty { get; set; } 

No entanto, como eu posso ver, alterar UpdateSourceTrigger para “PropertyChanged” preserva valores de ser reler (para que você possa deixar declaração de propriedade antiga):

     private string m_textProperty; public string TextProperty { get { return "Should not be used in OneWayToSource Binding"; } set { m_textProperty = value; } }