WPF как передавать данные между windows (MVVM)? - PullRequest
0 голосов
/ 17 апреля 2020

Я знаю, что есть много похожих вопросов, и я потратил два часа, пытаясь их реализовать, но не могу продолжить. Так что проблема кажется простой. Когда у меня нет модели представления, я могу установить datacontext для класса, и с этим классом очень легко передавать данные. Но когда есть viewmodel, я должен установить datacontext на это и не могу найти способ вернуть какое-либо значение после этого. Я пытался реализовать бесчисленные решения проблемы, но кажется, что они выше моего уровня квалификации. Большое спасибо за вашу помощь!

Важные части моего кода (это простая игра, которую я хочу сохранить, где сохранение называется userinput) Первое окно, где я хочу получить данные из второе окно

             case Key.Escape: {
                    Thread t = new Thread(() => {
                        SaveGameWindow pw = new SaveGameWindow();  //the second window
                        if ((pw.ShowDialog() == true) && (pw.Name != string.Empty)) //pw.Name always empty
                        {
                            ILogicSaveGame l = new LogicSaveGame();
                            l.Write(pw.Name, "saved_games.txt");
                            MessageBox.Show("game saved");
                        }
                    });
                    t.SetApartmentState(ApartmentState.STA);
                    t.Start();

XAML (отныне все принадлежит SaveGameWindow):

 <Window.Resources>
    <local:SaveGameViewModel x:Key="my_viewmodel"/>
</Window.Resources>
<Grid DataContext="{StaticResource my_viewmodel}">
 <TextBox Text="{Binding Name}"/> //i want to acces this in the first window
  <Button Command="{Binding CloseCommand}"
                Content="Save"/>

Код позади:

    private readonly SaveGameViewModel vm;
    public SaveGameWindow()
    {
        this.InitializeComponent();
        this.vm = this.FindResource("my_viewmodel") as SaveGameViewModel;
        if (this.vm.CloseAction == null)
        {
            this.vm.CloseAction = new Action(() => this.Close());
        }
    }

Viewmodel

 public class SaveGameViewModel : ViewModelBase
 {
    public SaveGameViewModel()
    {
        this.CloseCommand = new RelayCommand(() => this.Close());
    }

    public string Name { get; set; }

    public ICommand CloseCommand { get; private set; }

    public Action CloseAction { get; set; }

    private void Close()
    {
        this.CloseAction();
    }
}

Я использую galasoft mvvmlightlibs

1 Ответ

1 голос
/ 17 апреля 2020

Есть много решений этой проблемы. Самое простое решение - использовать модель общего представления как для windows, так и для привязки данных. Поскольку оба windows будут совместно использовать один и тот же DataContext, оба будут иметь доступ к одним и тем же данным или экземпляру модели, просто ссылаясь на их свойство DataContext.

Но если вы предпочитаете иметь отдельные модели представления, вы бы выберите другое решение.

Решение 1

Если вы хотите использовать выделенную модель представления для каждого окна, вы всегда можете использовать композицию и сделать, например, экземпляр SaveGameViewModel членом MainWindowViewModel. Любой класс, имеющий доступ к MainWindowViewModel, также будет иметь доступ к SaveGameViewModel и его API, либо напрямую, либо через делегирование свойств.
В этом примере используется прямой доступ, предоставив SaveGameViewModel в качестве свойства publi c для MainWindowViewModel:

SaveGameViewModel.cs

public class SaveGameViewModel : INotifyPropertyChanged
{
  private string name;   
  public string Name
  {
    get => this.name;
    set 
    { 
      this.name = value; 
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

MainWindowViewModel.cs

public class MainWindowViewModel : INotifyPropertyChanged
{      
  public SaveGameViewModel SaveGameViewModel { get; set; }

  // Allow to create an instance using XAML
  public MainWindowViewModel() {}

  // Allow to create an instance using C#
  public MainWindowViewModel(SaveGameViewModel saveGameViewModel) 
    => this.SaveGameViewModel = saveGameViewModel; 
}

App .xaml

<Application>
  <Application.Resources>
    <MainWindowViewModel x:Key="MainWindowViewModel">
      <MainWindowViewModel.SaveGameViewModel>
        <SaveGameViewModel />
      </MainWindowViewModel.SaveGameViewModel>
    </MainWindowViewModel>
  </Application.Resources>
</Application>

SaveGameWindow.xaml

<Window DataContext="{Binding Source={StaticResource MainWindowViewModel}, Path=SaveGameViewModel}">
  <TextBox Text="{Binding Name}" />
<Window>

MainWindow.xaml

<Window DataContext="{StaticResource MainWindowViewModel}">

<Window>

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private void OnKeyPressed(object sender, KeyEventArgs e)
  {
    if (e.Key == Key.Escape)
    {
      var mainWindowViewModel = this.DataContext as MainWindowViewModel;
      string saveGameName = mainWindowViewModel.SaveGameViewModel.Name;
    }
  }
}

Решение 2

Поскольку вы просто показываете диалог, вы можете сохранить текущий экземпляр SaveGameViewModel или интересующие его значения после закрытия диалогового окна:

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private SaveGameViewModel CurrentSaveGameViewModel { get; set; }
  private bool IsSaveGameValid { get; set; }

  private void ShowDialog_OnSaveButtonClick(object sender, RoutedEventArgs e)
  {        
    var saveGameDialog = new SaveGameWindow();
    this.IsSaveGameValid = saveGameDialog.ShowDialog ?? false;

    this.CurrentSaveGameViewModel = saveGameDialog.DataContext as SaveGameViewModel;
  }

  private void OnKeyPressed(object sender, KeyEventArgs e)
  {
    if (e.Key == Key.Escape && this.IsSaveGameValid)
    {
      string saveGameName = this.CurrentSaveGameViewModel.Name;
    }
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <MainWindowViewModel />
  </Window.DataContext>
<Window>

SaveGameWindow.xaml

<Window>
  <Window.DataContext>
    <SaveGameViewModel />
  </Window.DataContext>

  <TextBox Text="{Binding Name}" />
<Window>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...