Compartilhando dados entre diferentes ViewModels

Estou tentando desenvolver um projeto MVVM simples que possui duas janelas:

  1. A primeira janela é um editor de texto, onde eu vinculo algumas propriedades como FontSize ou BackgroundColor :

seu DataContext é MainWindowViewModel :

 public class MainWindowViewModel : BindableBase { public int EditorFontSize { get { return _editorFontSize; } set { SetProperty(ref _editorFontSize, value); } } ..... 
  1. A segunda janela é a janela de opções, onde eu tenho um controle deslizante para alterar o tamanho da fonte:

seu DataContext é OptionViewModel :

 public class OptionViewModel: BindableBase { public int EditorFontSize { get { return _editorFontSize; } set { SetProperty(ref _editorFontSize, value); } } ..... 

Meu problema é que eu tenho que obter o valor do controle deslizante na janela de opções e, em seguida, eu tenho que modificar a propriedade FontSize do meu TextBlock com esse valor. Mas eu não sei como enviar o tamanho da fonte de OptionViewModel para MainViewModel .

Eu acho que devo usar:

  1. Um modelo compartilhado
  2. Um modelo em MainWindowViewModel e um ref deste modelo em OptionViewModel
  3. Outros sistemas como notifications, mensagens …

Eu espero que você possa me ajudar. É meu primeiro projeto MVVM e o inglês não é meu idioma principal: S

obrigado

Há muitas maneiras de se comunicar entre os modelos de visualização e muitos pontos que o ponto é o melhor. Você pode ver como isso é feito:

  • usando MVVMLight

  • em prisma

  • de Caliburn

Na minha opinião, a melhor abordagem é usar o padrão EventAggregator do framework Prism . O Prism simplifica o padrão do MVVM. No entanto, se você não usou Prism , você pode usar o tutorial de Rachel Lim – versão simplificada do padrão EventAggregator de Rachel Lim. . Eu recomendo fortemente a abordagem de Rachel Lim.

Se você usar o tutorial de Rachel Lim, então você deve criar uma class comum:

 public static class EventSystem {...Here Publish and Subscribe methods to event...} 

E publique um evento no seu OptionViewModel :

 eventAggregator.GetEvent().Publish( new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0” }); 

então você se inscreve no construtor de outro seu MainViewModel para um evento:

 eventAggregator.GetEvent().Subscribe(ShowNews); public void ShowNews(TickerSymbolSelectedMessage msg) { // Handle Event } 

A abordagem simplificada da Rachel Lim é a melhor abordagem que já vi. No entanto, se você quiser criar um grande aplicativo, então você deve ler este artigo por Magnus Montin e no CSharpcorner com um exemplo .

Atualização : Para versões do Prism posteriores a 5, o CompositePresentationEvent é depreciado e completamente removido na versão 6, portanto, você precisará alterá-lo para PubSubEvent tudo o mais pode permanecer o mesmo.

Outra opção é armazenar essas variables ​​”compartilhadas” em uma class SessionContext de algum tipo:

 public interface ISessionContext: INotifyPropertyChanged { int EditorFontSize { get;set; } } 

Então, injetar isso em seus modelos de visão (você está usando injeção de dependência, certo?) E registrar para o evento PropertyChanged :

 public class MainWindowViewModel { public MainWindowViewModel(ISessionContext sessionContext) { sessionContext.PropertyChanged += OnSessionContextPropertyChanged; } private void OnSessionContextPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "EditorFontSize") { this.EditorFontSize = sessionContext.EditorFontSize; } } } 

Eu fiz um grande aplicativo MVVM com o WPF. Eu tenho muitas janelas e tive o mesmo problema. Minha solução talvez não seja muito elegante, mas funciona perfeitamente.

Primeira solução : Eu fiz um ViewModel único, dividi-lo em vários arquivos usando uma class parcial.

Todos esses arquivos começam com:

 namespace MyVMNameSpace { public partial class MainWindowViewModel : DevExpress.Mvvm.ViewModelBase { ... } } 

Estou usando o DevExpress, mas, olhando seu código, você deve tentar:

 namespace MyVMNameSpace { public partial class MainWindowViewModel : BindableBase { ... } } 

Segunda solução : De qualquer forma, eu também tenho um par de ViewModel diferente para gerenciar algumas dessas janelas. Neste caso, se eu tiver algumas variables ​​para ler de um ViewModel para outro, eu configuro essas variables ​​como estáticas .

Exemplo:

  public static event EventHandler ListCOMChanged; private static List p_ListCOM; public static List ListCOM { get { return p_ListCOM; } set { p_ListCOM = value; if (ListCOMChanged != null) ListCOMChanged(null, EventArgs.Empty); } } 

Talvez a segunda solução seja mais simples e ainda esteja bem para sua necessidade.

Espero que isso esteja claro. Me pergunte mais detalhes, se quiser.

Eu não sou um pro MVVM, mas o que eu tenho trabalhado com problemas como este é, ter uma class principal que tem todos os outros modelos de exibição como propriedades e definir essa class como contexto de dados de todas as janelas, eu don ‘ Não sei se é bom ou ruim, mas para o seu caso, parece suficiente.

Para uma solução mais sofisticada, veja isto

Para o mais simples,

Você pode fazer algo assim

 public class MainViewModel : BindableBase { FirstViewModel firstViewModel; public FirstViewModel FirstViewModel { get { return firstViewModel; } set { firstViewModel = value; } } public SecondViewModel SecondViewModel { get { return secondViewModel; } set { secondViewModel = value; } } SecondViewModel secondViewModel; public MainViewModel() { firstViewModel = new FirstViewModel(); secondViewModel = new SecondViewModel(); } } 

Agora você tem que fazer outro construtor para o seu OptionWindow passando um modelo de visão.

  public SecondWindow(BindableBase viewModel) { InitializeComponent(); this.DataContext = viewModel; } 

isso é para garantir que as duas janelas funcionem na mesma instância de um modelo de exibição.

Agora, onde quer que você esteja abrindo a segunda janela, use essas duas linhas

 var window = new SecondWindow((ViewModelBase)this.DataContext); window.Show(); 

Agora você está passando o modelo de visualização da primeira janela para a segunda janela, para que eles trabalhem na mesma instância do MainViewModel .

Tudo está feito, só você tem que resolver a binding como

   

e não há necessidade de dizer que o contexto de dados da primeira janela é MainViewModel

No MVVM, os modelos são o armazenamento de dados compartilhado. Eu persistiria o tamanho da fonte no OptionsModel , que implementa INotifyPropertyChanged . Qualquer viewmodel interessado no tamanho da fonte inscreve-se no PropertyChanged .

 class OptionsModel : BindableBase { public int FontSize {get; set;} // Assuming that BindableBase makes this setter invokes NotifyPropertyChanged } 

Nos ViewModels que precisam ser atualizados quando o FontSize é alterado:

 internal void Initialize(OptionsModel model) { this.model = model; model.PropertyChanged += ModelPropertyChanged; // Initialize properties with data from the model } private void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(OptionsModel.FontSize)) { // Update properties with data from the model } }