INotifyPropertyChanged vs. DependencyProperty no ViewModel

353

Ao implementar o ViewModel em um aplicativo WPF da arquitetura Model-View-ViewModel, parece haver duas opções principais de como torná-lo vinculável. Vi implementações que usam DependencyPropertypropriedades que o View vinculará e vi o ViewModel implementando INotifyPropertyChanged.

Minha pergunta é quando devo preferir um ao outro? Existem diferenças de desempenho? É realmente uma boa idéia atribuir as dependências do ViewModel ao WPF? O que mais preciso considerar ao tomar a decisão de design?

bitbonk
fonte
11
consulte stackoverflow.com/questions/1329138/… para obter uma maneira verificada pelo compilador de implementar INotifyPropertyChanged. Evitando ter os nomes das propriedades como uma sequência mágica.
31810 Ian Ringrose
10
Geralmente, há uma grande diferença entre uma propriedade de dependência e uma propriedade normal em uma classe que implementa INotifyPropertyChanged. As propriedades de dependência podem ser de origem ou destino na ligação de dados, mas as propriedades normais com o suporte INotifyPropertyChanged podem ser usadas apenas como fonte. Portanto, essas soluções não são totalmente intercambiáveis. A infraestrutura de ligação de dados requer um DP como destino para funcionar, mas a origem pode ser uma propriedade normal com o suporte do INotifyPropertyChanged ou um DP comum.
Mostafa Rezaei
4
Consulte stackoverflow.com/a/10595688/200442 para obter a maneira de implementar .net 4.5 INotifyPropertyChanged.
Daniel Little
melhor explicado aqui stackoverflow.com/a/3552550/366064
Bizhan 16/10

Respostas:

214

Kent escreveu um blog interessante sobre esse tópico: Exibir modelos: POCOs versus DependencyObjects .

Pequeno resumo:

  1. DependencyObjects não estão marcados como serializáveis
  2. A classe DependencyObject substitui e sela os métodos Equals () e GetHashCode ()
  3. Um DependencyObject tem afinidade de encadeamento - ele só pode ser acessado no encadeamento no qual foi criado

Eu prefiro a abordagem POCO. Uma classe base para PresentationModel (também conhecida como ViewModel) que implementa a interface INotifyPropertyChanged pode ser encontrada aqui: http://compositeextensions.codeplex.com

jbe
fonte
24
O DependencyObject também depende das bibliotecas WPF, enquanto o POCO não, permitindo que os seus modelos de exibição controlem alguma outra pilha de UI onde o WPF não está disponível (Compact Framework, Mono).
usar o seguinte código
26
É claro, então, que as Propriedades de Dependecy são construídas exclusivamente para a interface do usuário e não para a camada de negócios.
Andrei Rînea
11
Propriedades de dependência também exigem um pai DependencyObject. Seu ViewModel realmente não deve herdar do DependencyObject.
Gusdor
38

De acordo com o guia de desempenho do WPF, o DependencyObjects definitivamente tem um desempenho melhor do que os POCOs que implementam INotifyPropertyChanged:

http://msdn.microsoft.com/en-us/library/bb613546.aspx

James Ashley
fonte
11
Eu devo concordar com isso ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/…
Jonatha ANTOINE
Se você selecionar o .NET Framework versão 4, o link ainda funcionará. Apenas não está disponível para a "versão atual".
Doubleyou
Obrigado por apontar isso, há muitas informações escandalosas por trás dos desenvolvedores que afirmam com veemência que INotifyPropertyChanged é mais rápido ou incorre em menos sobrecarga do que os PDs e é simplesmente infundado. Os PDs são maneiras rápidas, elegantes e poderosas de definir estruturalmente a árvore virtual (de dados).
tpartee
Há um mal oculto nos DependencyObjects. Eles precisam ser criados no mesmo thread que os controles que se ligam a eles. Isso significa thread da GUI. Isso significa que você precisa despachar a criação para esse segmento. Você não pode ter essas coisas carregadas e criadas em algum encadeamento em segundo plano do DB, por exemplo. A menos que você despache a criação. Insano.
ed22
28

A escolha é totalmente baseada na lógica de negócios e no nível de abstração da interface do usuário. Se você não deseja uma boa separação, o DP funcionará para você.

O DependencyProperties será aplicável principalmente no nível VisualElements, portanto, não será uma boa ideia criar um monte de PDs para cada um dos requisitos de negócios. Também há um custo maior para o DP do que um INotifyPropertyChanged. Ao projetar um WPF / Silverlight, tente projetar a interface do usuário e o ViewModel totalmente separados para que, a qualquer momento, possamos alterar os controles de Layout e da interface do usuário (com base no tema e nos estilos)

Consulte também este post - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . O link tem muitas referências ao padrão Model-View-ViewModel, o que é muito relevante para esta discussão.

Jobi Joy
fonte
9
A postagem de jbe responde às diferenças com mais precisão. Só porque uma VM (ou Presenter) herda de DependencyObject não significa que não pode ser estilizada ou não é logicamente separada da View, apenas significa que o armazenamento para os valores da propriedade é diferente dos campos declarados explicitamente no Estilo POCO. Dito isto, serialização, igualdade lógica e afinidade de encadeamento são problemas reais com os quais as VMs baseadas em DepedencyObject precisam lidar.
Micahtan 25/05/09
"Também existe um custo maior para o DP do que um INotifyPropertyChanged" - onde está sua fonte de prova disso? Muitos desenvolvedores fazem essa afirmação sem nenhuma evidência para apoiá-la. De acordo com o MSDN, isso não é verdade. "tente projetar a interface do usuário e o ViewModel totalmente separados para que, a qualquer momento, possamos alterar os controles de layout e da interface do usuário" - novamente, isso não tem absolutamente nada a ver com POCO + PropChange versus DO / DP. Se houver, o registro Reflection and Path no DO / DP aprimora sua capacidade de trabalhar no lado visual.
31417 Tpartee
20

Do ponto de vista da expressividade, gosto muito de usar propriedades de dependência e me encolher ao pensar nisso INotifyPropertyChanged. Além dos stringnomes das propriedades e possíveis vazamentos de memória devido à assinatura do evento, INotifyPropertyChangedé um mecanismo muito mais explícito.

As propriedades de dependência implicam "quando isso, faça isso" usando metadados estáticos de fácil compreensão. É uma abordagem declarativa que recebe meu voto pela elegância.

Bryan Watts
fonte
11
A parte da sequência agora tem uma solução com o operador nameof.
Newtopian
@ Newtopian: Verdadeiro. Também há algumas coisas interessantes possíveis [CallerMemberName].
Bryan Watts
Sem mencionar a riqueza dos benefícios do Registro de propriedades (Reflexão) no WPF e CLR ao usar um modelo DO / DP versus um POCO.
tpartee
16

INotifyPropertyChanged quando usado, também permite adicionar mais lógica no código de seus getters e setter de suas propriedades.

DependencyProperty exemplo:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

No seu getter e setter --- tudo o que você pode fazer é simplesmente chamar SetValue e GetValue, respectivamente, b / c em outras partes da estrutura, o getter / setter não é chamado, mas chama diretamente SetValue, GetValue, portanto sua lógica de propriedade não ser executado com segurança.

Com INotifyPropertyChanged, defina um evento:

public event PropertyChangedEventHandler PropertyChanged;

E então simplesmente tenha qualquer lógica em qualquer lugar do seu código e chame:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Isso pode ser em um getter / setter ou em qualquer outro lugar.

Adão
fonte
11
Você também pode receber notificações de alterações no DependencyProperties. Consulte PropertyMetadata.PropertyChangedCallback. Exemplo em: msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White
2
Além disso, você pode chamar SetValue de qualquer lugar, bem como, não apenas de dentro da propriedade
aL3891
Isso é enganoso e falso - há várias maneiras de se conectar a eventos de mudança em um PD, mesmo quando ele é alterado 'internamente'. Um deles foi apontado acima por Joe White
tpartee
16

As propriedades de dependência destinam-se a dar suporte à ligação (como destino) nos elementos da interface do usuário e não como uma fonte para a ligação de dados. É aqui que INotifyProperty entra. De um ponto de vista puro, você não deve usar o DP em um ViewModels.

"Para ser a fonte de uma ligação, uma propriedade não precisa ser uma propriedade de dependência; você pode usar qualquer propriedade CLR como uma fonte de ligação. No entanto, para ser o destino de uma ligação, a propriedade deve ser um Para que uma ligação unidirecional ou bidirecional seja eficaz, a propriedade de origem deve suportar notificações de alteração que se propagam para o sistema de ligação e, portanto, para o destino.Para origens de ligação de CLR personalizadas, isso significa que a propriedade deve suportar INotifyPropertyChanged. As coleções devem suportar INotifyCollectionChanged. "

Todos os objetos de dependência não podem ser serializados (isso pode dificultar o uso de ViewModels e DTO (POCO).

Existem diferenças entre o DP no Silverlight e o WPF.

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx

Nick Hermans
fonte
Eu tenho usado Objetos de Dependência serializados desde 2009 sem problemas, por isso não sei do que você está falando quando diz "Todos os objetos de dependência não podem ser serializados" - sim, eles podem. De fato, existem muitas opções: codeproject.com/Articles/61440/… emphess.net/2008/11/25/dependencyproperty-serialization E um dos meus favoritos pessoais: forneça lojas de apoio para todos os seus PDs e torne-os serializáveis ​​( não há bons exemplos simples disponíveis em 2 minutos de pesquisa no Google, mas garanto que isso funciona).
Julio
7

Eu também tive que considerar essa decisão recentemente.

Descobri que o mecanismo INotifyPropertyChanged atendia melhor às minhas necessidades porque me permitia colar minha GUI a uma estrutura de lógica de negócios existente sem duplicar o estado. A estrutura que eu estava usando tinha seu próprio padrão de observador e era fácil encaminhar um nível de notificação para o próximo. Eu simplesmente tive uma classe que implementou a interface do observador da minha estrutura de lógica de negócios e da interface INotifyPropertyChanged.

Com o DP, você não pode definir o back-end que armazena o estado por conta própria. Eu teria que deixar o .net armazenar em cache uma cópia de cada item do estado ao qual estava vinculando. Parecia uma sobrecarga desnecessária - meu estado é grande e complicado.

Então, aqui eu achei INotifyPropertyChanged melhor para expor propriedades da lógica de negócios para a GUI.

Dito isto, onde eu precisava de um widget da GUI personalizado para expor uma propriedade e que as alterações nessa propriedade afetassem outros widgets da GUI, o DP provou ser a solução simples.

Então, lá achei o DP útil para notificação de GUI para GUI.

morechilli
fonte
6

É realmente uma boa idéia atribuir as dependências do ViewModel ao WPF?

O .NET 4.0 terá o System.Xaml.dll, portanto você não precisará depender de uma estrutura arbitrária para utilizá-lo. Veja o post de Rob Relyea sobre sua sessão PDC.

Minha vez

XAML é uma linguagem para descrever objetos e o WPF é uma estrutura cujos objetos descritos são elementos da interface do usuário.

O relacionamento deles é semelhante ao C #, uma linguagem para descrever a lógica, e ao .NET, uma estrutura que implementa tipos específicos de lógica.

O objetivo do XAML são gráficos de objetos declarativos. As tecnologias W * F são ótimas candidatas a esse paradigma, mas o XAML existe independentemente delas.

O XAML e todo o sistema de dependência foram implementados como pilhas separadas para o WF e o WPF, provavelmente para aproveitar a experiência de diferentes equipes sem criar uma dependência (sem trocadilhos) entre elas.

Bryan Watts
fonte
Ao responder, você parece estar assumindo que o bitbonk considera XAML e WPF iguais. Os ViewModels devem ter o menor número possível de dependências WPF, não para aumentar a separação lógica, mas para diminuir a complexidade do código e evitar todos os problemas associados à simples escrita da lógica no code-behind de um controle de usuário. Você inevitavelmente implementará conceitos do WPF como ICommand e apresentará um comportamento que apenas o WPF / Silverlight poderá quebrar facilmente - suas únicas preocupações de segmentação de apresentação em um modelo de exibição devem ser CollectionViews e ObservableCollection.
Gusdor
6

As propriedades de dependência são a cola da criação de controle personalizado. Se você estiver interessado em usar o Intelli-sense para mostrar suas propriedades na janela de propriedades no tempo de design do XAML, deverá usar as propriedades de Dependência. O INPC nunca mostrará uma propriedade na janela de propriedades em tempo de design.

John Peters
fonte
4

Parece que as Propriedades de Dependência devem ser usadas nos controles que você cria, como os Botões. Para usar propriedades em XAML e usar todos os recursos do WPF, essas propriedades devem ser Propriedades de Dependência.

No entanto, seu ViewModel é melhor usando INotifyPropertyChanged. O uso de INotifyPropertyChanged permitirá que você tenha uma lógica getter / setter, se necessário.

Eu recomendo verificar a versão de Josh Smith de uma classe base para um ViewModel que já implementa INotifyPropertyChanged:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

Eu acho que este é um excelente exemplo de como fazer um ViewModel.

timothymcgrath
fonte
4

Acho que DependencyProperty e INotifyPropertyChanged são usados ​​para duas coisas diferentes no Binding: o primeiro para permitir que uma propriedade seja o destino de uma ligação e receba a entrada de outra propriedade (use {Binding ...} para definir a propriedade), a última quando você deseja que o valor de uma propriedade seja usado como a origem de uma ligação (nome na Expressão do Caminho de Ligação). Portanto, a escolha é meramente técnica.

Domnik
fonte
2
Um INotifyPropertyChanged pode ser usado em ambos os casos. Você pode vincular o TwoWay a ele. Um DependencyProperty é necessário por razões técnicas apenas para algumas ações executadas em um objeto View (definindo algumas propriedades ao instanciar um objeto View em XAML, por exemplo). Um DependencyProperty nunca é necessário para um ViewModel.
Oillio
3

Prefiro uma abordagem mais direta, sobre a qual escrevi no blog no Presentation Model Without INotifyPropertyChanged . Usando uma alternativa à ligação de dados, é possível vincular diretamente às propriedades do CLR sem nenhum código de contabilidade. Você acabou de escrever um código .NET simples no seu modelo de exibição e ele é atualizado quando o modelo de dados é alterado.

Michael L Perry
fonte
Sem INotifyPropertyChanged, PropertyDescriptorsão utilizados, o que causa vazamentos de memória
Tilak
A biblioteca Update Controls que apresento nessa postagem do blog usa referências fracas, não descritores de propriedades. Não vaza memória.
Michael L Perry
11
Michael, sua biblioteca gera muito código. Não vejo benefícios. Eu posso conseguir o mesmo gerando wrapper de modelo com chamadas de evento PropertyChanged geradas.
Der_Meister
3

Há apenas uma coisa por que preferir a DependencyObject- A ligação funcionará melhor. Apenas tente um exemplo com uma lista ListBoxe TextBox, preencha com dados da INotifyPropertyChangedpropriedade vs. DependencyPropertye edite o item atual de TextBox...

ramos
fonte
11
Exemplo de código, por favor
Hassan Tareq
1

Se você deseja expor propriedades a outros controles, deve usar as propriedades Dependency ... Mas boa sorte, porque elas demoram um pouco para descobrir ...

JWP
fonte