You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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é

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:38:09