WPF如何在windows(MVVM)之间传输数据?

WPF如何在windows(MVVM)之间传输数据?,wpf,mvvm,Wpf,Mvvm,我知道有很多类似的问题,到目前为止,我花了两个小时试图实现它们,但无法继续。所以问题似乎很简单。当我没有viewmodel时,我可以将datacontext设置为一个类,使用该类传输数据非常容易。但是当存在viewmodel时,我必须将datacontext设置为该值,并且找不到在该值之后返回任何值的方法。我试图实施无数解决问题的方案,但它们似乎超出了我的技能水平。非常感谢你的帮助 我的代码的重要部分(这是一个简单的游戏,我想保存,其中保存由userinput命名)是第一个窗口,我想从第二个窗口

我知道有很多类似的问题,到目前为止,我花了两个小时试图实现它们,但无法继续。所以问题似乎很简单。当我没有viewmodel时,我可以将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):

视图模型

 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

这个问题有很多解决方案。最简单的解决方案是对windows和数据绑定使用共享视图模型。由于两个窗口将共享相同的
DataContext
,因此两个窗口都可以通过引用其
DataContext
属性来访问相同的数据或模型实例

但是,如果您更喜欢使用单独的视图模型,则可以选择不同的解决方案

解决方案1 如果要为每个窗口使用专用的视图模型,可以始终使用合成,并使实例
SaveGameViewModel
成为
MainWindowViewModel
的成员。任何可以访问
MainWindowViewModel
的类也可以直接或通过委托属性访问
SaveGameViewModel
及其API。
此示例通过将
SaveGameViewModel
公开为
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));
  }
}
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; 
}
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;
    }
  }
}
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;
    }
  }
}
MainWindowViewModel.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));
  }
}
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; 
}
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;
    }
  }
}
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;
    }
  }
}
App.xaml

<Application>
  <Application.Resources>
    <MainWindowViewModel x:Key="MainWindowViewModel">
      <MainWindowViewModel.SaveGameViewModel>
        <SaveGameViewModel />
      </MainWindowViewModel.SaveGameViewModel>
    </MainWindowViewModel>
  </Application.Resources>
</Application>
<Window DataContext="{Binding Source={StaticResource MainWindowViewModel}, Path=SaveGameViewModel}">
  <TextBox Text="{Binding Name}" />
<Window>
<Window DataContext="{StaticResource MainWindowViewModel}">

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

  <TextBox Text="{Binding Name}" />
<Window>

解决方案2 由于您只是显示一个对话框,因此可以在对话框关闭后存储
SaveGameViewModel
的当前实例或其感兴趣的值:

MainWindow.xaml.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));
  }
}
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; 
}
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;
    }
  }
}
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;
    }
  }
}
main window.xaml

<Application>
  <Application.Resources>
    <MainWindowViewModel x:Key="MainWindowViewModel">
      <MainWindowViewModel.SaveGameViewModel>
        <SaveGameViewModel />
      </MainWindowViewModel.SaveGameViewModel>
    </MainWindowViewModel>
  </Application.Resources>
</Application>
<Window DataContext="{Binding Source={StaticResource MainWindowViewModel}, Path=SaveGameViewModel}">
  <TextBox Text="{Binding Name}" />
<Window>
<Window DataContext="{StaticResource MainWindowViewModel}">

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

  <TextBox Text="{Binding Name}" />
<Window>

SaveGameWindow.xaml

<Application>
  <Application.Resources>
    <MainWindowViewModel x:Key="MainWindowViewModel">
      <MainWindowViewModel.SaveGameViewModel>
        <SaveGameViewModel />
      </MainWindowViewModel.SaveGameViewModel>
    </MainWindowViewModel>
  </Application.Resources>
</Application>
<Window DataContext="{Binding Source={StaticResource MainWindowViewModel}, Path=SaveGameViewModel}">
  <TextBox Text="{Binding Name}" />
<Window>
<Window DataContext="{StaticResource MainWindowViewModel}">

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

  <TextBox Text="{Binding Name}" />
<Window>