请求将WPF C#代码后置转换为MVVM模式的完整实现指导
将代码后置的WPF示例转换为MVVM模式
需求说明
我的目标是把这个采用代码后置的简单WPF示例转换成MVVM模式,这能帮我理解之后怎么为自己的需求编写MVVM代码。我在学习MVVM时有盲区,希望能拿到完整可直接运行的文件,只说“把这段代码放进ViewModel”对我没用,因为构建完整的ViewModel正是我需要的。我花了很多时间看不符合当前需求的在线教程,所以来这里求助!这个示例里,用户选三个RadioButton中的一个,TextBlock和Label会显示对应的字符串。
原代码文件
MainWindow.xaml
<Window x:Class="VMTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:VMTest" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel> <RadioButton x:Name="rbRed" Content="Red Means Stop" Checked="RbRed_Checked"/> <RadioButton x:Name="rbYellow" Content="Yellow Means Go Faster" Checked="RbYellow_Checked"/> <RadioButton x:Name="rbGreen" Content="Green Mean Go" Checked="RbGreen_Checked"/> </StackPanel> <TextBlock x:Name="myTextBlock" Grid.Row="1"/> <Label x:Name="myLabel" Grid.Row="2"/> </Grid> </Window>
MainWindow.xaml.cs
using System.Windows; namespace VMTest { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private readonly string LabelString1 = "1234567890"; private readonly string LabelString2 = "ABCDEFGHIJ"; private readonly string LabelString3 = "abcdefghij"; public MainWindow() { InitializeComponent(); } private void RbRed_Checked(object sender, RoutedEventArgs e) { myTextBlock.Text = rbRed.Content.ToString(); myLabel.Content = LabelString1; } private void RbYellow_Checked(object sender, RoutedEventArgs e) { myTextBlock.Text = rbYellow.Content.ToString(); myLabel.Content = LabelString2; } private void RbGreen_Checked(object sender, RoutedEventArgs e) { myTextBlock.Text = rbGreen.Content.ToString(); myLabel.Content = LabelString3; } } }
MVVM转换后的完整文件
1. ViewModel基类:ViewModelBase.cs
using System.ComponentModel; using System.Runtime.CompilerServices; using System.Collections.Generic; namespace VMTest { public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } } }
2. 主ViewModel:MainViewModel.cs
namespace VMTest { public class MainViewModel : ViewModelBase { private string _selectedRadioContent = string.Empty; private string _labelContent = string.Empty; private readonly Dictionary<string, string> _radioMapping = new() { {"Red Means Stop", "1234567890"}, {"Yellow Means Go Faster", "ABCDEFGHIJ"}, {"Green Mean Go", "abcdefghij"} }; public string SelectedRadioContent { get => _selectedRadioContent; set { if (SetField(ref _selectedRadioContent, value)) { if (_radioMapping.TryGetValue(value, out var labelText)) { LabelContent = labelText; } } } } public string LabelContent { get => _labelContent; set => SetField(ref _labelContent, value); } } }
3. 修改后的MainWindow.xaml
<Window x:Class="VMTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:VMTest" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <local:MainViewModel/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel> <RadioButton Content="Red Means Stop" IsChecked="{Binding SelectedRadioContent, Converter={StaticResource RadioButtonConverter}, ConverterParameter='Red Means Stop'}"/> <RadioButton Content="Yellow Means Go Faster" IsChecked="{Binding SelectedRadioContent, Converter={StaticResource RadioButtonConverter}, ConverterParameter='Yellow Means Go Faster'}"/> <RadioButton Content="Green Mean Go" IsChecked="{Binding SelectedRadioContent, Converter={StaticResource RadioButtonConverter}, ConverterParameter='Green Mean Go'}"/> </StackPanel> <TextBlock Text="{Binding SelectedRadioContent}" Grid.Row="1"/> <Label Content="{Binding LabelContent}" Grid.Row="2"/> </Grid> <Window.Resources> <local:RadioButtonToSelectionConverter x:Key="RadioButtonConverter"/> </Window.Resources> </Window>
4. 绑定转换器:RadioButtonToSelectionConverter.cs
using System.Globalization; using System.Windows.Data; namespace VMTest { public class RadioButtonToSelectionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value?.ToString() == parameter?.ToString(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? parameter : Binding.DoNothing; } } }
5. 修改后的MainWindow.xaml.cs
using System.Windows; namespace VMTest { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
关键说明
- ViewModelBase:实现
INotifyPropertyChanged接口,作为所有ViewModel的基类,负责属性变更通知,确保UI能实时响应数据变化。 - MainViewModel:封装所有业务逻辑和数据,通过
SelectedRadioContent属性接收RadioButton的选中状态,自动同步更新LabelContent。 - RadioButton转换器:解决RadioButton与字符串属性的双向绑定问题,将IsChecked状态转换为对应的选中内容。
- UI绑定:移除了原有的事件处理逻辑,所有UI元素通过数据绑定与ViewModel关联,实现UI视图与业务逻辑的完全分离。
内容的提问来源于stack exchange,提问作者ZioGio
相关产品推荐
相关产品推荐

