如何在WPF的MVVM模式下从另一个窗口获取字段值?
在WPF MVVM模式下实现从子窗口获取数据
我正把C# WinForms开发的软件迁移到基于MVVM模式的WPF平台,目前处于入门阶段,遇到一个问题:如何从另一个窗口获取数据。在WinForms中我是这么实现的:
WinForms主窗体代码
public partial class MainForm : Form { public MainForm() { InitializeComponent(); } string userName = string.Empty; string userSurName = string.Empty; string userAddress = string.Empty; private void GetUserInfos() { using (var form = new UserInfosForm()) { var dialog = form.ShowDialog(); if (dialog == DialogResult.OK) { this.userName = form.userName; this.userSurName = form.userSurName; this.userAddress = form.userAddress; } } } }
WinForms数据来源窗体代码
public partial class UserInfosForm : Form { public string userName = string.Empty; public string userSurName = string.Empty; public string userAddress = string.Empty; public UserInfosForm() { InitializeComponent(); buttonOk.DialogResult = DialogResult.OK; buttonOk.Click += ButtonOk_Click; } private void ButtonOk_Click(object sender, EventArgs e) { userName = textBoxName.Text; userSurName = textBoxSurName.Text; userAddress = textBoxAddress.Text; } }
请问如何在WPF的MVVM模式下实现该功能?
实现方案
MVVM模式核心是避免视图直接耦合,通过数据绑定和ViewModel交互传递数据,同时尽量保持ViewModel的纯净性。以下是具体实现步骤:
1. 定义用户数据模型
创建承载用户信息的实体类,实现INotifyPropertyChanged以支持数据绑定:
using System.ComponentModel; using System.Runtime.CompilerServices; public class UserInfo : INotifyPropertyChanged { private string _userName; private string _userSurName; private string _userAddress; public string UserName { get => _userName; set { _userName = value; OnPropertyChanged(); } } public string UserSurName { get => _userSurName; set { _userSurName = value; OnPropertyChanged(); } } public string UserAddress { get => _userAddress; set { _userAddress = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 子窗口ViewModel
子窗口的ViewModel处理输入逻辑,提供确认/取消命令:
using System.Windows.Input; using Microsoft.Toolkit.Mvvm.Input; // 可通过NuGet安装CommunityToolkit.Mvvm简化命令实现 public class UserInfoDialogViewModel { public UserInfo UserInfo { get; set; } = new UserInfo(); public ICommand ConfirmCommand { get; } public ICommand CancelCommand { get; } // 通知主窗口关闭状态的委托 public Action<bool> OnDialogClose { get; set; } public UserInfoDialogViewModel() { ConfirmCommand = new RelayCommand(ExecuteConfirm); CancelCommand = new RelayCommand(ExecuteCancel); } private void ExecuteConfirm() { OnDialogClose?.Invoke(true); } private void ExecuteCancel() { OnDialogClose?.Invoke(false); } }
3. 子窗口视图(UserInfoDialog.xaml)
通过数据绑定关联ViewModel,无业务逻辑后置代码:
<Window x:Class="YourNamespace.UserInfoDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:YourNamespace" Title="用户信息" Width="300" Height="250" WindowStartupLocation="CenterOwner"> <Window.DataContext> <vm:UserInfoDialogViewModel /> </Window.DataContext> <StackPanel Margin="10"> <Label Content="用户名:"/> <TextBox Text="{Binding UserInfo.UserName, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5"/> <Label Content="姓氏:"/> <TextBox Text="{Binding UserInfo.UserSurName, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 5"/> <Label Content="地址:"/> <TextBox Text="{Binding UserInfo.UserAddress, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 15"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="确认" Command="{Binding ConfirmCommand}" Width="80" Margin="0 0 10 0"/> <Button Content="取消" Command="{Binding CancelCommand}" Width="80"/> </StackPanel> </StackPanel> </Window>
子窗口后置代码仅处理关闭逻辑:
public partial class UserInfoDialog : Window { private UserInfoDialogViewModel _viewModel; public UserInfoDialog() { InitializeComponent(); _viewModel = (UserInfoDialogViewModel)DataContext; _viewModel.OnDialogClose = (isConfirmed) => { DialogResult = isConfirmed; Close(); }; } // 对外暴露获取用户信息的方法 public UserInfo GetUserInfo() { return _viewModel.UserInfo; } }
4. 主窗口ViewModel
主ViewModel触发子窗口显示,并接收返回数据:
using Microsoft.Toolkit.Mvvm.ComponentModel; using Microsoft.Toolkit.Mvvm.Input; public class MainViewModel : ObservableObject { private string _userName; private string _userSurName; private string _userAddress; public string UserName { get => _userName; set => SetProperty(ref _userName, value); } public string UserSurName { get => _userSurName; set => SetProperty(ref _userSurName, value); } public string UserAddress { get => _userAddress; set => SetProperty(ref _userAddress, value); } public ICommand GetUserInfoCommand { get; } public MainViewModel() { GetUserInfoCommand = new RelayCommand(ExecuteGetUserInfo); } private void ExecuteGetUserInfo() { var dialog = new UserInfoDialog(); if (dialog.ShowDialog() == true) { var userInfo = dialog.GetUserInfo(); UserName = userInfo.UserName; UserSurName = userInfo.UserSurName; UserAddress = userInfo.UserAddress; } } }
5. 主窗口视图(MainWindow.xaml)
绑定ViewModel的属性和命令:
<Window x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:YourNamespace" Title="主窗口" Width="400" Height="300"> <Window.DataContext> <vm:MainViewModel /> </Window.DataContext> <StackPanel Margin="10"> <Button Content="获取用户信息" Command="{Binding GetUserInfoCommand}" Margin="0 0 0 15"/> <StackPanel> <TextBlock Text="用户名:"/> <TextBlock Text="{Binding UserName}" Margin="0 0 0 5"/> <TextBlock Text="姓氏:"/> <TextBlock Text="{Binding UserSurName}" Margin="0 0 0 5"/> <TextBlock Text="地址:"/> <TextBlock Text="{Binding UserAddress}"/> </StackPanel> </StackPanel> </Window>
补充说明
- 若不想使用第三方库,可自行实现
ICommand接口和INotifyPropertyChanged逻辑。 - 若要完全解耦窗口显示逻辑,可封装
IDialogService接口,让ViewModel依赖该接口,由实现类处理窗口操作,提升ViewModel的可测试性。
内容的提问来源于stack exchange,提问作者Gokhan
相关产品推荐
相关产品推荐

