WPF MVVM项目中如何通过ViewModel实现视图间数据共享?
实现WPF MVVM下网格间距的跨视图传递与刷新
我正在开发一个WPF应用并学习MVVM模式,主窗口包含多个视图:其中Ribbon视图用于设置用户参数,DesignSheet视图带有可配置的网格覆盖层。需求是让用户在Ribbon中选择的网格间距值,能传递到对应ViewModel,最终刷新DesignSheet视图的网格。
步骤1:完善ViewModel属性与数据同步
给两个ViewModel添加网格间距属性,并通过单例模式实现数据同步,确保Ribbon的参数变化能实时传递到DesignSheet:
修改ViewModel代码
internal class ViewModelBaseClass : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } internal class RibbonViewModel : ViewModelBaseClass { private int _gridSpacing = 10; public int GridSpacing { get => _gridSpacing; set { if (_gridSpacing != value) { _gridSpacing = value; OnPropertyChanged(nameof(GridSpacing)); // 将参数同步到DesignSheet的ViewModel DesignSheetViewModel.Instance.GridSpacing = value; } } } } internal class DesignSheetViewModel : ViewModelBaseClass { // 单例实例,用于跨ViewModel共享数据 public static DesignSheetViewModel Instance { get; } = new DesignSheetViewModel(); private int _gridSpacing = 10; public int GridSpacing { get => _gridSpacing; set { if (_gridSpacing != value) { _gridSpacing = value; OnPropertyChanged(nameof(GridSpacing)); } } } }
步骤2:绑定RibbonView的IntegerUpDown控件
修改RibbonView.xaml中的IntegerUpDown控件,将Value属性与RibbonViewModel的GridSpacing双向绑定,确保用户输入能同步到ViewModel:
<xctk:IntegerUpDown Minimum="5" Maximum="50" Width="80" Increment="5" Value="{Binding GridSpacing, Mode=TwoWay}" />
步骤3:修改DesignSheetView的数据上下文
更新DesignSheetView.xaml.cs,使用DesignSheetViewModel的单例作为数据上下文,保证和RibbonViewModel使用同一数据源:
public partial class DesignSheetView : UserControl { public DesignSheetView() { InitializeComponent(); this.DataContext = DesignSheetViewModel.Instance; } }
步骤4:实现DesignSheetView的网格动态刷新
由于网格的Viewport是Rect类型、LineGeometry的EndPoint是Point类型,需要创建转换器将整数间距转为对应类型:
创建IntToRectConverter转换器
namespace GrafcetDesignerWPF.Converters { public class IntToRectConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is int spacing ? new Rect(0, 0, spacing, spacing) : new Rect(0, 0, 20, 20); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
修改DesignSheetView的XAML代码
引用转换器并绑定相关属性,替换固定的20值,实现网格的动态刷新:
<UserControl x:Class="GrafcetDesignerWPF.Views.DesignSheetView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:GrafcetDesignerWPF.Views" xmlns:converters="clr-namespace:GrafcetDesignerWPF.Converters" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" x:Name="DesignSheetControl"> <UserControl.Resources> <converters:IntToRectConverter x:Key="IntToRectConverter" /> </UserControl.Resources> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <Canvas x:Name="DesignCanvas" Width="2000" Height="2000"> <Canvas.Background> <DrawingBrush TileMode="Tile" Viewport="{Binding GridSpacing, Converter={StaticResource IntToRectConverter}}" ViewportUnits="Absolute"> <DrawingBrush.Drawing> <GeometryDrawing Brush="Transparent"> <GeometryDrawing.Pen> <Pen Thickness="0.5" Brush="LightGray"/> </GeometryDrawing.Pen> <GeometryDrawing.Geometry> <GeometryGroup> <LineGeometry StartPoint="0,0" EndPoint="{Binding GridSpacing, StringFormat='{0},0'}" /> <LineGeometry StartPoint="0,0" EndPoint="{Binding GridSpacing, StringFormat='0,{0}'}" /> </GeometryGroup> </GeometryDrawing.Geometry> </GeometryDrawing> </DrawingBrush.Drawing> </DrawingBrush> </Canvas.Background> </Canvas> </ScrollViewer> </UserControl>
补充说明
- 单例模式适合小型项目快速实现数据同步,大型项目推荐使用事件聚合器(如Prism EventAggregator)或依赖注入来解耦ViewModel,避免直接引用。
- 双向绑定确保用户操作能实时反馈到ViewModel,而PropertyChanged事件则触发视图的自动刷新。
内容的提问来源于stack exchange,提问作者Marc Chambré
相关产品推荐
相关产品推荐

