百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

WinForms使用MVVM模式构建应用:共用事件驱动模型和数据驱动理念

yuyutoo 2024-12-08 19:47 3 浏览 0 评论

前言

前一篇文章中WinForms 中使用 MVVM 模式构建应用:实现登录页面、页面导航及 SQLite 数据库连接完整框架搭建过程,仅仅使用了事件驱动模型,有好几个朋友留言说,要看看数据驱动是怎么编写的,这篇文章展示了相关内容,欢迎订阅观看,多多评论交流。

在传统的 WinForms 开发中,事件驱动模型(Event-Driven Model)是核心,它通过控件的事件(如点击按钮、改变文本等)触发业务逻辑。然而,MVVM 模式引入了数据驱动(Data-Driven)的理念,这种方式通过数据绑定实现视图和业务逻辑之间的解耦,使代码更清晰、更易于维护。本文将解析这两种驱动模型,并结合前面所述基于 MVVM 的 WinForms 框架示例来展示如何将两者结合起来,特别是增加数据驱动部分。

一、事件驱动模型

1. 什么是事件驱动模型?

事件驱动模型是一种基于用户交互或系统事件触发操作的编程模式。它依赖于事件的定义和监听,用户操作触发事件,然后由事件处理程序执行逻辑代码。

示例:登录按钮点击事件

public partial class LoginForm : Form
{
    private readonly LoginViewModel _viewModel;

    public LoginForm(LoginViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
    }

    private void btnLogin_Click(object sender, EventArgs e)
    {
        _viewModel.Username = tb_user.Text.Trim();
        _viewModel.Password = tb_password.Text.Trim();
        _viewModel.LoginCommand.Execute(null);
        this.Hide();
    }
}

二、数据驱动模型

1. 什么是数据驱动模型?

数据驱动模型通过数据的变化来驱动 UI 的更新。通过数据绑定机制,视图和数据模型保持同步,无需显式的事件监听和处理。当数据发生变化时,UI 自动更新。

示例:数据驱动登录表单

在 MVVM 模式中,数据驱动的核心是通过数据绑定,使视图模型(ViewModel)中的数据改变时,视图(View)能够自动更新。以下是一个简单的例子来说明这一点。

1. ViewModel 示例

视图模型使用 INotifyPropertyChanged 接口,让属性改变时通知视图。

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class LoginViewModel : INotifyPropertyChanged
{
    private string _username;
    private string _password;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Username
    {
        get => _username;
        set
        {
            if (_username != value)
            {
                _username = value;
                OnPropertyChanged();
            }
        }
    }

    public string Password
    {
        get => _password;
        set
        {
            if (_password != value)
            {
                _password = value;
                OnPropertyChanged();
            }
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. View 示例

视图绑定到 LoginViewModel,使用数据绑定让 UI 自动更新。

using System.Windows.Forms;

public partial class LoginForm : Form
{
    private readonly LoginViewModel _viewModel;

    public LoginForm(LoginViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        // 绑定数据到控件
        txtUsername.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Username), false, DataSourceUpdateMode.OnPropertyChanged);
        txtPassword.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Password), false, DataSourceUpdateMode.OnPropertyChanged);
    }
}

简要说明

  1. 1. 当用户在 txtUsernametxtPassword 输入框中输入内容时,输入值会自动更新到 LoginViewModel 中的 UsernamePassword 属性。
  2. 2. 通过数据绑定,视图中的控件和视图模型中的数据保持同步,简化了代码逻辑。

三、在 WinForms MVVM框架 中实现数据驱动

尽管 WinForms 没有内置的强大数据绑定机制(如 WPF),我们仍可以通过 INotifyPropertyChanged 接口实现数据驱动。以下是如何在示例框架中加入数据驱动功能。

1. 增加 INotifyPropertyChanged 实现

LoginViewModel 中实现 INotifyPropertyChanged 接口,让属性的改变能够通知绑定的 UI 控件。

using System.ComponentModel;
using System.Windows.Input;
using WinFormMVVM.Services;
using WinFormMVVM.Commands;
using WinFormMVVM.Views;
using System.Runtime.CompilerServices;
using System.Diagnostics;

namespace WinFormMVVM.ViewModels
{
    public class LoginViewModel : INotifyPropertyChanged
    {
        private string _username;
        private string _password;
        private bool _isPasswordInValid;
        private readonly IUserService _userService;

        public event PropertyChangedEventHandler PropertyChanged;

        public string Username
        {
            get => _username;
            set
            {
                if (_username != value)
                {
                    _username = value;
                    OnPropertyChanged();
                }
            }
        }
        public string Password
        {
            get => _password;
            set
            {
                if (_password != value)
                {
                    _password = value;
                    ValidatePassword(); // 验证密码
                    OnPropertyChanged();
                }
            }
        }

        public bool IsPasswordInValid
        {
            get => _isPasswordInValid;
            private set
            {
                if (_isPasswordInValid != value)
                {
                    _isPasswordInValid = value;
                    OnPropertyChanged();
                }
            }
        }
        private void ValidatePassword()
        {
            // 检查密码是否包含大写字母和小写字母
            IsPasswordInValid = string.IsNullOrEmpty(_password) ||(!(_password.Any(char.IsUpper) &&
                              _password.Any(char.IsLower)));
            Trace.WriteLine(IsPasswordInValid);
        }
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public ICommand LoginCommand { get; }

        public LoginViewModel(IUserService userService)
        {
            _userService = userService;
            LoginCommand = new RelayCommand(Login);
        }

        private void Login()
        {
            var user = _userService.GetUserByUsername(Username);
            if (user != null && user.Password == Password)
            {
                MainForm mainForm = new MainForm();
                mainForm.Show();
            }
            else
            {
                MessageBox.Show("显示登录失败的消息!");
            }
        }
    }
}

2. 在视图中绑定数据

我们可以通过 DataBindings 来将控件属性绑定到 ViewModel

示例:绑定数据到文本框

using WinFormMVVM.ViewModels;

namespace WinFormMVVM.Views
{
    public partial class LoginForm : Form
    {
        private readonly LoginViewModel _viewModel;

        public LoginForm(LoginViewModel viewModel)
        {
            InitializeComponent();
            _viewModel = viewModel;
            tb_user.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Username), false, DataSourceUpdateMode.OnPropertyChanged);
            tb_password.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Password), false, DataSourceUpdateMode.OnPropertyChanged);
            lblPasswordValidation.DataBindings.Add("Visible", _viewModel, nameof(_viewModel.IsPasswordInValid), false, DataSourceUpdateMode.OnPropertyChanged);
            lblPasswordValidation.Text = "密码必须包含大小写字母";

        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            _viewModel.LoginCommand.Execute(null);
            this.Hide();
        }
    }
}

执行结果:

四、Winforms中支持数据绑定的控件

在 WinForms 中,许多控件都支持数据绑定,可以实现视图和数据的同步。以下是常用的支持数据绑定的控件:

1.TextBox

  • ? 用途: 用于显示和编辑文本。
  • ? 绑定属性: Text
txtUsername.DataBindings.Add("Text", viewModel, "Username", false, DataSourceUpdateMode.OnPropertyChanged);

2.Label

  • ? 用途: 用于显示只读文本。
  • ? 绑定属性: Text
lblMessage.DataBindings.Add("Text", viewModel, "StatusMessage");

3.CheckBox

  • ? 用途: 用于表示布尔值(选中或未选中)。
  • ? 绑定属性: Checked
chkRememberMe.DataBindings.Add("Checked", viewModel, "RememberMe");

4.ComboBox

  • ? 用途: 显示下拉列表。
  • ? 绑定属性: SelectedValue, SelectedItem, Text
cmbOptions.DataBindings.Add("SelectedValue", viewModel, "SelectedOption");
cmbOptions.DataSource = viewModel.Options; // 绑定数据源

5. ListBox

  • ? 用途: 显示列表项。
  • ? 绑定属性: SelectedItem, SelectedValue
lstItems.DataBindings.Add("SelectedItem", viewModel, "SelectedItem");
lstItems.DataSource = viewModel.Items; // 绑定数据源

6. DataGridView

  • ? 用途: 显示表格数据。
  • ? 绑定属性: DataSource
dataGridView.DataBindings.Add("DataSource", viewModel, "TableData");

7.PictureBox

  • ? 用途: 显示图片。
  • ? 绑定属性: Image
pictureBox.DataBindings.Add("Image", viewModel, "ProfilePicture");

8. ProgressBar

  • ? 用途: 显示任务进度。
  • ? 绑定属性: Value
progressBar.DataBindings.Add("Value", viewModel, "Progress");

9.DateTimePicker

  • ? 用途: 显示和选择日期和时间。
  • ? 绑定属性: Value
dateTimePicker.DataBindings.Add("Value", viewModel, "SelectedDate");

10.TrackBar

  • ? 用途: 显示和选择数值范围中的值。
  • ? 绑定属性: Value
trackBar.DataBindings.Add("Value", viewModel, "VolumeLevel");

WinForms 中的大多数控件都支持通过 DataBindings 来实现数据驱动的功能。通过使用 DataBindings.Add 方法,可以将视图模型中的属性与控件的属性绑定,从而在视图模型的数据发生变化时自动更新视图。

五、事件驱动与数据驱动结合的优势

1. 清晰的职责划分

  • ? 事件驱动:专注于用户交互和事件处理。
  • ? 数据驱动:专注于数据变化和 UI 同步。

2. 降低代码耦合

  • ? 视图和逻辑解耦,视图模型仅关心业务逻辑,视图关心展示。

3. 更易于测试

  • ? 视图模型独立于 UI,易于编写单元测试。

六、总结

在 WinForms 中实现 MVVM 模式,可以结合事件驱动和数据驱动模型,发挥各自的优势。通过 INotifyPropertyChanged 和数据绑定,我们可以实现更加简洁、可维护的 WinForms 应用。

如果本文对你有帮助,我将非常荣幸。

如果你对本文有其他的看法,欢迎留言交流。

如果你喜欢我的文章,谢谢三连,点赞,关注,转发吧!!!

相关推荐

TCP协议原理,有这一篇就够了

先亮出这篇文章的思维导图:TCP作为传输层的协议,是一个软件工程师素养的体现,也是面试中经常被问到的知识点。在此,我将TCP核心的一些问题梳理了一下,希望能帮到各位。001.能不能说一说TC...

Win10专业版无线网络老是掉线的问题

有一位电脑基地的用户,使用...

学习计算机网络需要掌握以下几方面基础知识

计算机基础知识操作系统:了解常见操作系统(如Windows、Linux)的基本操作和网络配置,例如如何设置IP地址、子网掩码、网关和DNS服务器等,以及如何通过命令行工具(如ping、tr...

网络工程师的圣经!世界级网工手绘268张图让TCP/IP直接通俗易懂

要把知识通俗地讲明白,真的不容易。——读者说TCP/IP从字面意义上讲,有人可能会认为TCP/IP是指TCP和IP两种协议。实际生活当中有时候也确实就是这两种协议。然而在很多情况下,它只是...

三分钟了解通信知识TCP与IP协议(含“通信技术”资料分享)

TCP/IPTCP/IP分层模型①应用层...

网闸与防火墙:网络安全设备的差异与应用

在网络安全领域,网闸(安全隔离网闸,GAP)和防火墙(Firewall)是两类重要的防护设备。尽管它们都服务于网络安全防护,但在设计理念、技术原理、安全效能及适用场景等方面存在显著差异,以下从五个维度...

S7-300的TCP/IP通信

一、首先在项目中创建2个S7-300的站点;二、硬件组态中,设置合适的TCP/IP地址,在同一网段内;...

西门子S7-1500 PLC的 MODBUS TCP通信

MODBUSTCP使MODBUS_RTU协议运行于以太网,MODBUSTCP使用TCP/IP和以太网在站点间传送MODBUS报文,MODBUSTCP结合了以太网物理网络和网络标准TC...

系统规划与管理师新版备考必备:第7章考点思维导图解析

备考系统规划与管理师的小伙伴们,福利又来啦!今天为大家带来《系统规划与管理师(第2版)》第7章考点的思维导图,助你高效梳理重点,让备考更有方向!...

TCP/IP、Http、Socket 有何区别与联系?

HTTP协议对应于应用层,Socket则是对TCP/IP协议的封装和应用(程序员层面上)。HTTP是应用层协议,主要解决如何包装数据。而我们平时说的最多的Socket是什么呢?实际上...

西门子PLC串口协议与以太网通信协议对比

西门子plc品牌众多,通信协议的类型就更多了,具体可分为串口协议和以太网通信协议两大类。...

网络编程懒人入门(十三):一泡尿的时间,快速搞懂TCP和UDP的区别

本文引用了作者Fundebug的“一文搞懂TCP与UDP的区别”一文的内容,感谢无私分享。1、引言...

程序员必备的学习笔记《TCP/IP详解(一)》

为什么会有TCP/IP协议在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样...

一文读懂TCP/IP协议工作原理和工作流程

简述本文主要介绍TCP/IP协议工作原理和工作流程。含义TCP/IP协议,英文全称TransmissionControlProtocol/InternetProtocol,包含了一系列构成互联网...

如何在 Windows 10 和 Windows 11 上重置 TCP/IP 堆栈

传输控制协议/Internet协议,通常称为TCP/IP,是您的WindowsPC如何与Internet上的其他设备进行通信的关键部分。但是当事情出错时会发生什么?你如何解决它?幸运的...

取消回复欢迎 发表评论: