MVVM(Model-View-ViewModel)详解(附实战案例)

        MVVM(Model-View-ViewModel)是一种软件架构模式,常用于构建客户端应用程序,特别是在WPF(Windows Presentation Foundation)和Silverlight应用中,它将应用程序分为三个主要部分:

一、项目结构

1. Model(模型)

表示应用程序的数据和业务逻辑。它包含数据对象和处理这些数据的业务规则,与用户界面无关。

2. View(视图)

负责用户界面的呈现,如WPF中的窗口、页面等。它只关注如何展示数据,不包含业务逻辑。

3.ViewModel(视图模型)

:作为View和Model之间的桥梁。它从Model获取数据并进行处理,然后提供给View使用,同时也处理View的用户交互事件并更新Model。

二、MVVM的逻辑结构:

        View和ViewModel之间通过数据绑定(Data Binding)进行通信。View可以自动反映ViewModel中数据的变化,而ViewModel可以接收View的用户输入。

         ViewModel和Model之间通过方法调用进行交互。ViewModel调用Model的方法来获取或更新数据。

具体细节和实现步骤

1. 创建Model:定义数据类和业务逻辑类。

2. 创建ViewModel:继承自 INotifyPropertyChanged 接口,以便在属性值变化时通知View。在ViewModel中定义属性和命令(Commands)。

3. 创建View:在XAML中进行数据绑定,将View的控件属性绑定到ViewModel的属性上。

4. 设置DataContext:在View的代码隐藏文件中,将View的 DataContext 设置为ViewModel的实例。

ViewModel是什么:

        ViewModel是MVVM架构中的核心部分,它包含了View所需的数据和行为。它将业务逻辑从View中分离出来,使得View只关注用户界面的呈现,而ViewModel负责处理数据和用户交互。

        在CAD中应用MVVM的简单案例代码和注释:

假设我们有一个简单的CAD应用,需要在视图中显示一个矩形的位置和大小。

效果如下:

1. Model类 (RectangleModel.cs)

 2. ViewModel类 (RectangleViewModel.cs)

// RectangleViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using CadMvvmExample;
using WpfCircleGenerator.ViewModels;

namespace CadMvvmExample
{
    /// <summary>
    /// 矩形视图模型
    /// 处理业务逻辑和数据绑定
    /// </summary>
    public class RectangleViewModel : INotifyPropertyChanged
    {
        private readonly RectangleModel _model = new()
        {
            X = 10,
            Y = 10,
            Width = 100,
            Height = 50
        };

        public double X
        {
            get => _model.X;
            set { _model.X = value; OnPropertyChanged(); }
        }

        public double Y
        {
            get => _model.Y;
            set { _model.Y = value; OnPropertyChanged(); }
        }

        public double Width
        {
            get => _model.Width;
            set
            {
                if (value <= 0) throw new ArgumentException("宽度必须大于0");
                _model.Width = value;
                OnPropertyChanged();
            }
        }

        public double Height
        {
            get => _model.Height;
            set
            {
                if (value <= 0) throw new ArgumentException("高度必须大于0");
                _model.Height = value;
                OnPropertyChanged();
            }
        }
        // 生成矩形的命令
        public ICommand GenerateRectangleCommand { get; }

        public RectangleViewModel()
        {
            GenerateRectangleCommand = new RelayCommand(GenerateRectangle);
        }

        private void GenerateRectangle()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            using (doc.LockDocument())//锁文档
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                try
                {
                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    // 根据输入的参数创建矩形
                    Point3d p1 = new Point3d(X, Y, 0);
                    Point3d p2 = new Point3d(X + Width, Y, 0);
                    Point3d p3 = new Point3d(X + Width, Y + Height, 0);
                    Point3d p4 = new Point3d(X, Y + Height, 0);

                    Polyline rect = new Polyline(4);
                    rect.AddVertexAt(0, new Point2d(p1.X, p1.Y), 0, 0, 0);
                    rect.AddVertexAt(1, new Point2d(p2.X, p2.Y), 0, 0, 0);
                    rect.AddVertexAt(2, new Point2d(p3.X, p3.Y), 0, 0, 0);
                    rect.AddVertexAt(3, new Point2d(p4.X, p4.Y), 0, 0, 0);
                    rect.Closed = true;

                    // 将矩形添加到模型空间
                    btr.AppendEntity(rect);
                    tr.AddNewlyCreatedDBObject(rect, true);

                    tr.Commit();
                    ed.WriteMessage("\n矩形已生成。\n");
                    ed.ZoomExtentsX(rect); // 缩放到矩形范围
                }
                catch (Exception ex)
                {
                    ed.WriteMessage($"\n生成矩形时出错: {ex.Message}");
                    tr.Abort();
                }
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    // 简单的命令实现类
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;

        public RelayCommand(Action execute)
        {
            _execute = execute;
        }

        public bool CanExecute(object? parameter)
        {
            return true;
        }

        public event EventHandler? CanExecuteChanged;

        public void Execute(object? parameter)
        {
            _execute();
        }
    }

    /// <summary>
    /// 矩形数据模型
    /// 纯粹的数据实体,不包含任何逻辑
    /// </summary>
    public class RectangleModel
    {
        /// <summary>矩形左上角X坐标</summary>
        public double X { get; set; }

        /// <summary>矩形左上角Y坐标</summary>
        public double Y { get; set; }

        /// <summary>矩形宽度(必须大于0)</summary>
        public double Width { get; set; }

        /// <summary>矩形高度(必须大于0)</summary>
        public double Height { get; set; }
    }
}

3. View类 (MainWindow.xaml.cs) 

// MainWindow.xaml.cs
using System.Windows;
using CadMvvmExample;

namespace CadMvvmExample
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // 备用设置DataContext的方式(如果不在XAML中设置)
            // this.DataContext = new RectangleViewModel();
        }
    }
}

.XAML视图 (MainWindow.xaml)

<!-- MainWindow.xaml -->
<Window x:Class="CadMvvmExample.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:vm="clr-namespace:CadMvvmExample"
        mc:Ignorable="d"
        Title="CAD矩形绘制工具(作者@山水)" 
        Height="200" 
        Width="350"
        WindowStartupLocation="CenterScreen">

    <Window.DataContext>
        <!-- 显式设置ViewModel实例 -->
        <vm:RectangleViewModel />
    </Window.DataContext>

    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- X坐标输入 -->
        <TextBlock Grid.Row="0" Grid.Column="0" 
                   Text="X坐标:" 
                   Margin="0,0,10,5"
                   VerticalAlignment="Center"/>
        <TextBox Grid.Row="0" Grid.Column="1"
                 Text="{Binding X, UpdateSourceTrigger=PropertyChanged}"
                 VerticalAlignment="Center"
                 Margin="0,0,0,5"/>

        <!-- Y坐标输入 -->
        <TextBlock Grid.Row="1" Grid.Column="0" 
                   Text="Y坐标:" 
                   Margin="0,0,10,5"
                   VerticalAlignment="Center"/>
        <TextBox Grid.Row="1" Grid.Column="1"
                 Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
                 VerticalAlignment="Center"
                 Margin="0,0,0,5"/>

        <!-- 宽度输入 -->
        <TextBlock Grid.Row="2" Grid.Column="0" 
                   Text="宽度:" 
                   Margin="0,0,10,5"
                   VerticalAlignment="Center"/>
        <TextBox Grid.Row="2" Grid.Column="1"
                 Text="{Binding Width, UpdateSourceTrigger=PropertyChanged}"
                 VerticalAlignment="Center"
                 Margin="0,0,0,5"/>

        <!-- 高度输入 -->
        <TextBlock Grid.Row="3" Grid.Column="0" 
                   Text="高度:" 
                   Margin="0,0,10,5"
                   VerticalAlignment="Center"/>
        <TextBox Grid.Row="3" Grid.Column="1"
                 Text="{Binding Height, UpdateSourceTrigger=PropertyChanged}"
                 VerticalAlignment="Center"
                 Margin="0,0,0,5"/>

        <!-- 绘制按钮 -->
        <Button Grid.Row="4" Grid.ColumnSpan="2"
                Content="绘制矩形"
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"
                Margin="0,10,0,0"
                Padding="15,5"
                FontWeight="Bold"
                Command="{Binding GenerateRectangleCommand}"/>
    </Grid>
</Window>

4.Command类

// Commands.cs
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Windows;
using System.Windows.Forms.Integration;
using WpfCircleGenerator.Views;
[assembly: ExtensionApplication(typeof(CadMvvmExample.MyExtension))]
[assembly: CommandClass(typeof(CadMvvmExample.CadCommands))]

namespace CadMvvmExample
{
    public class MyExtension : IExtensionApplication
    {
        public void Initialize()
        {
            Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n插件已加载!输入\"xx\"运行。(山水qq443440204)\n");
        }
        public void Terminate() { }
    }
    public static class Dimensions
    {
        public const int Pheight = 1300; // 面板高度
        public const int Pwidth = 400; // 面板宽度
    }
    /// <summary>
    /// AutoCAD命令定义类
    /// </summary>
    public class CadCommands
    {
        private static MainWindow _mainWindow;
        /// <summary>
        /// 注册"XX"命令 - 弹出矩形参数窗口
        /// </summary>
        [CommandMethod("XX")]
        public static void ShowRectangleWindow()
        {
            if (_mainWindow == null )
            {
                // 定义一个具名的事件处理程序
                EventHandler idleHandler = null;
                idleHandler = (s, e) =>
                {
                    _mainWindow = new MainWindow();

                    // 设置AutoCAD主窗口为Owner
                    var interopHelper = new System.Windows.Interop.WindowInteropHelper(_mainWindow);
                    interopHelper.Owner = Application.MainWindow.Handle;

                    _mainWindow.Show();

                    // 单次执行,移除事件处理程序
                    Application.Idle -= idleHandler;
                };

                // 为Idle事件添加事件处理程序
                Application.Idle += idleHandler;
            }
            else
            {
                // 如果窗口已经打开,将其激活并显示在最前面 切换可是状态
                _mainWindow.Activate();
              /*  if (_mainWindow.Visibility == System.Windows.Visibility.Visible)
                {
                    _mainWindow.Hide();
                }
                else
                {
                    _mainWindow.Show();
                }*/
            }
        }
    }
}

        通过这个完整的MVVM(Model-View-ViewModel)示例,我们展示了如何将WPF的数据绑定机制与CAD二次开发相结合,实现清晰的分层架构。Model负责数据存储,ViewModel处理业务逻辑并通知UI更新,View则专注于界面呈现,三者各司其职,显著提升了代码的可维护性和可测试性。

三、关键收获

  1. 解耦与复用:ViewModel完全独立于View,可轻松适配不同界面或平台(如AutoCAD插件或独立WPF应用)。

  2. 实时响应:通过INotifyPropertyChangedUpdateSourceTrigger=PropertyChanged,实现了数据的双向实时同步。

  3. 扩展性强:在此基础上,可进一步集成命令(ICommand)、验证逻辑或CAD绘图功能。

四、未来优化方向

  • 输入验证:为属性添加范围检查(如宽度/高度不能为负值)。

  • CAD集成:在ViewModel中调用AutoCAD API,将矩形数据直接绘制到模型空间。

  • UI增强:结合ObservableCollection实现多矩形管理,并添加可视化操作面板。

MVVM模式不仅适用于WPF应用,在CAD二次开发中同样能大幅提升代码质量。希望本文能为您在复杂项目中应用分层架构提供实践参考。

进一步学习建议

  • 探索PrismMVVM Light框架简化MVVM实现

  • 研究AutoCAD .NET API与WPF的交互最佳实践

  • 尝试将此模式扩展到三维实体建模场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山水CAD插件定制

你的鼓励是我创作最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值