WPF如何在窗口之间传输数据(MVVM)



我知道有很多类似的问题,我花了两个小时试图实现它们,但无法继续。所以问题看起来很简单。当我没有视图模型时,我可以将数据上下文设置为一个类,并且使用该类传输数据非常容易。但是,当存在viewmodel时,我必须将datacontext设置为该值,并且在此之后找不到返回任何值的方法。我试着为这个问题实施了无数的解决方案,但似乎都超出了我的技能水平。非常感谢你的帮助!

我代码的重要部分(这是一个我想保存的简单游戏,其中保存由用户输入命名(第一个窗口,我想从第二个窗口获得数据

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

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

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

解决方案1

如果您想为每个窗口使用专用的视图模型,您可以始终使用composition,并使实例SaveGameViewModel成为MainWindowViewModel的成员。任何有权访问MainWindowViewModel的类也将有权直接或通过委派属性访问SaveGameViewModel及其API
此示例通过将SaveGameViewModel公开为MainWindowViewModel:的公共属性来使用直接访问

保存GameViewModel.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; 
}

应用程序xaml

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

保存游戏窗口.xaml

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

主窗口.xaml

<Window DataContext="{StaticResource MainWindowViewModel}">
<Window>

主窗口.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的当前实例或其感兴趣的值:

主窗口.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;
}
}
}

主窗口.xaml

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

保存游戏窗口.xaml

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

最新更新