No MVVM, o ViewModel ou Model implementa INotifyPropertyChanged?

165

A maioria dos exemplos de MVVM em que trabalhei tiveram o modelo implementado INotifyPropertyChanged, mas no exemplo CommandSink de Josh Smith, o ViewModel implementaINotifyPropertyChanged .

Ainda estou reunindo cognitivamente os conceitos do MVVM, então não sei se:

  • Você precisa colocar o INotifyPropertyChangedno ViewModel para começar CommandSinka trabalhar
  • Isso é apenas uma aberração da norma e isso realmente não importa
  • Você sempre deve implementar o modelo INotifyPropertyChangede isso é apenas um erro que seria corrigido se isso fosse desenvolvido a partir de um exemplo de código para um aplicativo

Quais foram as experiências de outras pessoas nos projetos MVVM em que você trabalhou?

Edward Tanguay
fonte
4
se você implementar o INPC, experimente github.com/Fody/PropertyChanged - ele economizará semanas de digitação.
CAD bloke

Respostas:

104

Eu diria exatamente o contrário: eu sempre coloco o meu INotifyPropertyChangedno meu ViewModel - você realmente não quer poluir seu modelo com um recurso específico do WPF INotifyPropertyChanged, como esse material deve estar no ViewModel.

Tenho certeza de que outros discordarão, mas é assim que trabalho.

Steven Robbins
fonte
84
O que você faz se uma propriedade é alterada no modelo? Você precisa obtê-lo de alguma forma para o modelo de exibição. Pergunta honesta, estou lidando com esse enigma agora.
22610 Roger Rabbitcombe
4
O EventAggregator no código do Prism é uma boa alternativa para INotifyPropertyChanged no modelo, com uma propriedade customizada alterada no tipo de evento. O código de evento nesse projeto oferece suporte ao encaminhamento de eventos entre os threads de segundo plano e da interface do usuário, o que às vezes pode ser um problema.
Steve Mitcham
51
INotifyProperyChanged não é específico WPF, que vive no namespace System.ComponentModel, eu tê-lo usado em aplicações WinForms, também INotifyPropertyChanged esteve em .Net desde 2.0, WPF só tem sido em torno desde 3.0
benPearce
40
Sou fã de colocar INotifyPropertyChanged no MODEL e no VIEWMODEL. Não consigo pensar em uma razão para não fazer isso. É uma maneira elegante de informar o VIEWMODEL sobre quando as alterações de segundo plano aconteceram no MODO que afetam o VIEWMODEL, assim como são usadas para informar o VIEW e houve alterações no VIEWMODEL.
ScottCher
6
@Steve - sobre informar ao ViewModel que a propriedade de um Model foi alterada, parece que INotifyPropertyChanged funciona muito bem como "um evento no qual o viewmodel pode se conectar". Por que não usá-lo?
skybluecodeflier
146

Eu discordo totalmente do conceito de que o modelo não deve implementar o INotifyPropertyChanged. Essa interface não é específica da interface do usuário! Simplesmente informa de uma mudança. De fato, o WPF usa isso fortemente para identificar alterações, mas isso não significa que seja uma interface de interface do usuário. Eu compararia com o seguinte comentário: " Um pneu é um acessório de carro ". Claro que sim, mas bicicletas, ônibus etc. também o usam. Em resumo, não tome essa interface como uma coisa de interface do usuário.

Dito isto, isso não significa necessariamente que acredito que o Modelo deva fornecer notificações. De fato, como regra geral, o modelo não deve implementar essa interface, a menos que seja necessário. Na maioria dos casos, em que nenhum dado do servidor é enviado por push para o aplicativo cliente, o modelo pode ficar obsoleto. Mas se estiver ouvindo os dados do mercado financeiro, não vejo por que o modelo não pode implementar a interface. Como exemplo, e se eu tiver uma lógica que não seja da interface do usuário, como um serviço que, quando recebe um preço de compra ou de venda por um determinado valor, emite um alerta (por exemplo, por meio de um email) ou faz um pedido? Esta poderia ser uma possível solução limpa.

No entanto, existem diferentes maneiras de conseguir as coisas, mas eu sempre argumentaria a favor da simplicidade e evitaria redundância.

O que é melhor? Definir eventos em uma coleção ou alterações de propriedade no modelo de visualização e propagá-lo para o modelo ou fazer com que a visualização atualize intrinsecamente o modelo (por meio do modelo de visualização)?

A linha inferior sempre que você vê alguém dizendo que " você não pode fazer isso ou aquilo " é um sinal de que eles não sabem do que estão falando.

Realmente depende do seu caso e, de fato, o MVVM é uma estrutura com muitos problemas e ainda estou para ver uma implementação comum do MVVM em todos os aspectos.

Eu gostaria de ter mais tempo para explicar os vários tipos de MVVM e algumas soluções para problemas comuns - principalmente fornecidos por outros desenvolvedores, mas acho que terei que fazê-lo outra vez.

Paulo Sousa
fonte
7
Pense desta maneira. Se você, como desenvolvedor, consome uma .dll com Modelos em você, certamente não os reescreve para dar suporte ao INotifyPropertyChanged.
Lee Treveil
2
Concordo plenamente com você. Como eu, você também pode ficar satisfeito ao descobrir que a documentação oficial do MVVM < msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx > (seção Modelo) concorda conosco. :-)
Noldorin
"No entanto, existem diferentes maneiras de conseguir as coisas, mas eu sempre argumentaria a favor da simplicidade e evitaria redundância." Muito importante.
Bastien Vandamme
1
INotifyPropertyChangedfaz parte do System.ComponentModelespaço para nome que é " comportamento em tempo de execução e tempo de design de componentes e controles ". NÃO USE INotifyPropertyChanged em modelos, apenas em modelos de exibição. Link para docs: docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Igor Popov
1
Post antigo, eu sei, mas geralmente volto a ele ao iniciar um novo projeto usando o MVVM. Recentemente, comecei a aplicar o Princípio da Responsabilidade Única com muito mais rigor. Um modelo é ter uma responsabilidade. Ser modelo. Assim que você adiciona INotifyPropertyChanged ao modelo, ele não segue mais o Princípio de Responsabilidade Única. Todo o motivo pelo qual o ViewModel existe é permitir que o modelo seja o modelo, que ele tenha uma única responsabilidade.
Rhyous 16/03/19
13

Acho que o MVVM é muito mal nomeado e chamar o ViewModel de ViewModel faz com que muitos percam um recurso importante de uma arquitetura bem projetada, que é um DataController que controla os dados, independentemente de quem estiver tentando tocá-los.

Se você pensa no View-Model como mais um DataController e implementa uma arquitetura em que o DataController é o único item que toca nos dados, você nunca tocaria diretamente nos dados, mas sempre o utilizaria. O DataController é útil para a interface do usuário, mas não necessariamente apenas para a interface do usuário. É para camada de negócios, camada de interface do usuário, etc ...

DataModel -------- DataController ------ View
                  /
Business --------/

Você acaba com um modelo como este. Até a empresa só deve tocar nos dados usando o ViewModel. Então seu enigma simplesmente desaparece.

Rhyous
fonte
3
Isso é ótimo se seus dados forem alterados apenas quando o DataController os alterar. Se os dados vierem de um banco de dados ou de algum outro armazenamento de dados que possa fornecer outro caminho para a mudança, talvez seja necessário informar o VIEWMODEL (DataController no seu padrão) e o VIEW quando isso acontecer. Você pode pesquisar usando o DataController ou enviar de algum processo externo para o seu DataModel e permitir que o DataModel envie notificações de alterações ao seu DataController.
ScottCher
4
Você está exatamente certo. Os padrões de design são de nível muito alto. Na maioria das vezes, o padrão de design leva você a fazer as coisas certas, mas de vez em quando elas o desviam do caminho. Você nunca deve evitar fazer algo certo, pois está fora do seu padrão de design.
Rhyous 17/05/19
Você também enviaria o seu DataController à medida que ele controla e o modelo de dados e solicita a atualização.
Rhyous 16/03/19
Além disso, o modelo no MVVM deve ser mantido específico conforme necessário pela interface do usuário (por exemplo, DTO). Portanto, qualquer DB ou lógica comercial complexa deve ocorrer em uma camada diferente e, em seguida, dados brutos devem ser fornecidos por meio do modelo de visualização.
Codinome Jack
9

Depende de como você implementou seu modelo. Minha empresa usa objetos de negócios semelhantes aos objetos CSLA da Lhotka e faz uso extensivo de INotifyPropertyChangedtodo o modelo de negócios.

Nosso mecanismo de validação depende muito de ser notificado de que as propriedades são alteradas por esse mecanismo e funciona muito bem. Obviamente, se você estiver usando uma implementação diferente de objetos de negócios em que a notificação de alterações não seja tão crítica para a operação, poderá ter outros métodos para detectar alterações em seu modelo de negócios.

Também temos modelos de exibição que propagam as alterações do modelo quando necessário, mas os modelos de exibição estão ouvindo as alterações subjacentes do modelo.

Steve Mitcham
fonte
3
Como exatamente você propaga OnPropertyChanged do Modelo para OnPropertyChanged do ViewModel? Eu tenho um problema quando o ViewModel tem nomes de propriedades diferentes do modelo - algum tipo de mapeamento de nome para nome seria necessário, certo?
9788 Martin Konicek
Não é nada sofisticado, simplesmente encaminhamos os eventos. Suponho que se os nomes fossem diferentes, uma tabela de pesquisa poderia ser usada. Se a mudança não fosse um mapeamento individual, você poderia simplesmente conectar o evento e acionar os eventos necessários no manipulador.
911 Steve Mitcham
6

Concordo com a resposta de Paulo, a implementação INotifyPropertyChangedem modelos é totalmente aceitável e até sugerida pela Microsoft -

Normalmente, o modelo implementa os recursos que facilitam a ligação à visualização. Isso geralmente significa que ele suporta notificação alterada de propriedade e coleção através das interfaces INotifyPropertyChangede INotifyCollectionChanged. As classes de modelos que representam coleções de objetos geralmente derivam da ObservableCollection<T>classe, que fornece uma implementação da INotifyCollectionChangedinterface.

Embora seja você quem decide se deseja ou não esse tipo de implementação, lembre-se:

E se suas classes de modelo não implementarem as interfaces necessárias?

Às vezes você vai precisar de trabalhar com objetos de modelo que não implementam as INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, ou INotifyDataErrorInfointerfaces. Nesses casos, o modelo de vista pode precisar envolver os objetos do modelo e expor as propriedades necessárias à vista. Os valores para essas propriedades serão fornecidos diretamente pelos objetos de modelo. O modelo de visualização implementará as interfaces necessárias para as propriedades que ele expõe, para que a visualização possa facilmente vincular dados a elas.

Retirado de - http://msdn.microsoft.com/en-us/library/gg405484(PandP.40).aspx

Já trabalhei em alguns projetos em que não implementamos INotifyPropertyChangednossos modelos e, por isso, enfrentamos muitos problemas; era necessária duplicação desnecessária de propriedades na VM e, ao mesmo tempo, tivemos que atualizar o objeto subjacente (com valores atualizados) antes de passá-los para BL / DL.

Você enfrentará problemas especialmente se precisar trabalhar com a coleção de objetos do seu modelo (por exemplo, em uma grade ou lista editável) ou modelos complexos; os objetos de modelo não serão atualizados automaticamente e você precisará gerenciar tudo isso na sua VM.

akjoshi
fonte
3

Mas, às vezes (como no texto do link da apresentação ), o modelo é serviço, que fornece alguns dados on-line ao aplicativo e é necessário implementar a notificação de que novos dados chegaram ou que os dados foram alterados usando eventos ...

Andrey Khataev
fonte
3

Eu acho que a resposta é bastante clara se você deseja aderir à MV-VM.

consulte: http://msdn.microsoft.com/en-us/library/gg405484(v=PandP.40).aspx

No padrão MVVM, a visualização encapsula a interface do usuário e qualquer lógica da interface do usuário, o modelo de visualização encapsula a lógica e o estado da apresentação e o modelo encapsula a lógica e os dados de negócios.

"A visualização interage com o modelo de visualização por meio de ligação de dados, comandos e eventos de notificação de alterações. O modelo de visualização consulta, observa e coordena atualizações no modelo, convertendo, validando e agregando dados conforme necessário para exibição na visualização".

John D
fonte
4
A citação está aberta à interpretação. Eu acho que você deve adicionar a sua interpretação, para tornar a sua resposta clara :-)
Søren Boisen
@ John D: Esse artigo fornece apenas uma interpretação do MVVM e uma maneira de implementá-lo, não define o MVVM.
akjoshi
Além disso, se você ler o artigo completo, ele definirá a classe Model assim: "Normalmente, o modelo implementa os recursos que facilitam a ligação à visualização. Isso geralmente significa que ele suporta notificações de alteração de propriedade e coleção por meio das interfaces INotifyPropertyChanged e INotifyCollectionChanged As classes de modelos que representam coleções de objetos geralmente derivam da classe ObservableCollection <T>, que fornece uma implementação da interface INotifyCollectionChanged. "
akjoshi
2

Eu diria no seu ViewModel. Não faz parte do modelo, pois o modelo é independente da interface do usuário. O Modelo deve ser "tudo, exceto negócios agnósticos"

Steve Dunn
fonte
2

A implementação do INPC nos modelos pode ser usada se os modelos estiverem claramente expostos no ViewModel. Mas, geralmente, o ViewModel agrupa os modelos são suas próprias classes para reduzir a complexidade do modelo (que não deve ser útil para a ligação). Nesse caso, o INPC deve ser implementado no ViewModel.

stéphane Boutinet
fonte
1

Eu estou usando a INotifyPropertyChangeinterface em um modelo. Na verdade, uma alteração na propriedade do modelo deve ser acionada apenas pela interface do usuário ou pelo cliente externo.

Eu notei várias vantagens e desvantagens:

Vantagens

O notificador está no modelo de negócios

  1. De acordo com o domínio, está certo. Ele deve decidir quando aumentar e quando não aumentar.

Desvantagens

O modelo possui propriedades (quantidade, taxa, comissão, total de direitos). O total da receita é calculado usando a quantidade, taxa e alteração da comissão.

  1. Ao carregar valores de db, o cálculo do total de frieght é chamado 3 vezes (qtd, taxa, comissão). Deveria ser uma vez.

  2. Se rate, qty for atribuído na camada de negócios, novamente o notificador será chamado.

  3. Deve haver uma opção para desativar isso, possivelmente na classe base. No entanto, os desenvolvedores podem se esquecer de fazer isso.

Anand Kumar
fonte
Devido a todas as desvantagens que você listou, apenas decidimos que era uma decisão ERRADA em nosso projeto WPF relativamente grande de implementar o INPC em modelos. Eles devem lidar apenas com a camada de persistência. Todas as outras coisas como validação, notificação de alteração e propriedades calculadas devem ser tratadas no ViewModel. Agora entendo claramente que a repetição de propriedades do modelo no ViewModel nem sempre é uma violação do princípio DRY.
try2fly.b4ucry 13/04
1

Eu acho que tudo depende do caso de uso.

Quando você tem um modelo simples com muitas propriedades, pode implementá-lo no INPC. Simplesmente, quero dizer que esse modelo se parece com um POCO .

Se o seu modelo for mais complexo e residir em um domínio de modelo interativo - modelos que fazem referência a modelos, inscrevendo-se em eventos de outros modelos - ter eventos de modelo implementados como INPC é um pesadelo.

Coloque-se na posição de alguma entidade modelo que precisa colaborar com outros modelos. Você tem vários eventos para se inscrever. Todos eles são implementados como INPC. Imagine os manipuladores de eventos que você possui. Uma enorme cascata de cláusulas se e / ou cláusulas de troca.

Outro problema com o INPC. Você deve projetar seus aplicativos para confiar na abstração, não na implementação. Isso geralmente é feito usando interfaces.

Vamos dar uma olhada em 2 implementações diferentes da mesma abstração:

public class ConnectionStateChangedEventArgs : EventArgs
{
    public bool IsConnected {get;set;}
}

interface IConnectionManagerINPC : INotifyPropertyChanged
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    bool IsConnected {get;}
}

interface IConnectionManager
{
    string Name {get;}
    int ConnectionsLimit {get;}
    /*

    A few more properties

    */
    event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged;
    bool IsConnected {get;}
}

Agora olhe para os dois. O que o IConnectionManagerINPC lhe diz? Que algumas de suas propriedades podem mudar. Você não sabe qual deles. De fato, o design é que apenas o IsConnected muda, pois o restante deles é somente leitura.

Pelo contrário, as intenções de IConnectionManager são claras: "Posso dizer que o valor da minha propriedade IsConnected pode ser alterado".

dzendras
fonte
1

Basta usar o INotifyPropertyChangeno seu viewmodel e não no Model,

o modelo geralmente usa o IDataErrorInfopara lidar com os erros de validação, portanto, mantenha-se no seu ViewModel e você estará no caminho da MVVM.

Adão
fonte
1
E os modelos com várias propriedades? você está repetindo código na VM.
30418 Luis
0

Suponha que a referência do objeto em sua visualização seja alterada. Como você notificará todas as propriedades a serem atualizadas para mostrar os valores corretos? Chamar OnPropertyChangedna sua visualização todas as propriedades do objeto é um lixo para o meu ponto de vista.

Então, o que eu faço é deixar que o próprio objeto para notificar ninguém quando um valor em uma propriedade mudanças, e na minha opinião, eu uso ligações como Object.Property1, Object.Property2e sobre. Dessa forma, se eu apenas quiser alterar o objeto que atualmente é mantido em minha opinião, apenas o faço OnPropertyChanged("Object").

Para evitar centenas de notificações durante o carregamento de objetos, tenho um indicador booleano privado que o defino como true durante o carregamento, verificado a partir do objeto OnPropertyChangede não faz nada.

Dummy01
fonte
0

Normalmente, o ViewModel implementará o INotifyPropertyChanged. O modelo pode ser qualquer coisa (arquivo xml, banco de dados ou mesmo objeto). Modelo é usado para fornecer os dados ao modelo de exibição, que se propaga para a exibição.

Veja aqui

Syed
fonte
1
emm .. não. O modelo não pode ser um arquivo ou banco de dados xml. E o modelo não é usado para fornecer os dados. Caso contrário, não deve ser chamado "modelo", mas "dados" ..? Modelo é usado para descrever os dados. Muito auto-explicativo, não é? :)
Taras 31/01
1
Se você tiver uma resposta melhor, pls compartilhe! estamos todos aqui para compartilhar o conhecimento e não para competir
Adam
0

imho Eu acho que o viewmodel implementa INotifyPropertyChangee o modelo pode usar a notificação em um "nível" diferente.

por exemplo, com algum serviço de documento e um objeto de documento, você tem um evento documentChanged que um modelo de exibição ouve para limpar e reconstruir a exibição. No modelo de visualização de edição, você tem uma alteração de propriedade para as propriedades do documento para suportar as visualizações. Se o serviço fizer muito com o documento ao salvar (atualizando data de alteração, último usuário e assim por diante), é fácil obter uma sobrecarga de eventos alterados por propriedade e apenas um documento alterado é suficiente.

Mas se você usar INotifyPropertyChangeem seu modelo, acho que é uma boa prática retransmiti-lo em seu viewmodel, em vez de assiná-lo diretamente em seu view. Nesse caso, quando os eventos mudam no seu modelo, você só precisa alterar o modelo de exibição e a exibição permanece intocada.

Bram
fonte
0

Todas as propriedades, que estão vinculadas à minha visualização, estão nos meus ViewModel (s). Portanto, eles devem implementar a interface INotifyPropertyChanged. Portanto, o modo de exibição recebe todas as alterações.

[Usando o kit de ferramentas MVVM Light, eu os deixei herdar do ViewModelBase.]

O Modelo mantém a lógica de negócios, mas não tem nada a ver com a exibição. Portanto, não há necessidade da interface INotifyPropertyChanged.

donotbefake
fonte