当前位置: 首页 > news >正文

WinForms 中使用 MVVM 模式构建应用:实现登录页面、页面导航及 SQLite 数据库连接完整框架搭建过程

前言

在传统的 WinForms 应用程序开发中,很多开发者使用事件驱动的设计模式,直接将业务逻辑编写在界面代码中。然而,随着应用程序的复杂性增加,单一的界面文件变得臃肿,难以测试和维护。借鉴 WPF 中 MVVM(Model-View-ViewModel)模式的设计思想,可以帮助我们更好地管理业务逻辑和数据绑定。本文将介绍如何在 WinForms 中构建一个 MVVM 框架的登录页面示例,并实现页面导航、SQLite 数据库连接及依赖注入管理。

一、项目设计与依赖引用

1. 新增winform项目

在这里插入图片描述

2. 创建项目结构

项目结构:按模块创建以下文件夹:

  • Models:存放数据实体类。
  • ViewModels:包含视图模型,负责处理业务逻辑和数据绑定。
  • Views:放置WinForms窗体,充当UI界面。
  • Services:用于数据库服务操作。
  • IoC:配置依赖注入容器。
  • Commands:配置执行命令。
    在这里插入图片描述
  1. 安装所需库
    • 使用 Microsoft.Extensions.DependencyInjection 来实现依赖注入。
    • 使用 Dapper 库来连接和操作 SQLite 数据库。
    • 使用SQLitePCLRaw.bundle_e_sqlite3库来处理和连接SQLite数据库。
      在这里插入图片描述

二、创建数据实体 Model

首先创建一个 User 类来表示数据库中的用户信息。我们假设用户表 Users 包含 IdUsernamePassword 三个字段。

namespace WinFormMVVM.Models
{public class User{public int Id { get; set; }public string Username { get; set; }public string Password { get; set; }}
}

三、创建服务层 Service

创建一个初始化数据库的服务类DatabaseInitializer,配置默认用户和密码

using System.Data;
using Dapper;
using SQLitePCL;namespace WinFormMVVM.Services
{public class DatabaseInitializer{private readonly IDbConnection _dbConnection;public DatabaseInitializer(IDbConnection dbConnection){_dbConnection = dbConnection;}public void InitializeDatabase(){Batteries.Init();const string createTableQuery = @"CREATE TABLE IF NOT EXISTS Users (Id INTEGER PRIMARY KEY AUTOINCREMENT,Username TEXT NOT NULL,Password TEXT NOT NULL);";const string insertUserQuery = @"INSERT INTO Users (Username, Password) VALUES (@Username, @Password)";_dbConnection.Open();_dbConnection.Execute(createTableQuery);// 检查是否已有用户数据,若无则添加var existingUser = _dbConnection.QueryFirstOrDefault("SELECT * FROM Users WHERE Username = @Username", new { Username = "admin" });if (existingUser == null){_dbConnection.Execute(insertUserQuery, new { Username = "admin", Password = "password123" });}_dbConnection.Close();}}
}

Services 文件夹中创建 IUserService 接口及其实现 UserService,用于从 SQLite 数据库中查询用户信息。

using System.Data;
using Dapper;
using WinFormMVVM.Models;namespace WinFormMVVM.Services
{public interface IUserService{User GetUserByUsername(string username);}public class UserService : IUserService{private readonly IDbConnection _dbConnection;public UserService(IDbConnection dbConnection){_dbConnection = dbConnection;}public User GetUserByUsername(string username){string sql = "SELECT * FROM Users WHERE Username = @Username";return _dbConnection.QuerySingleOrDefault<User>(sql, new { Username = username });}}
}

四、Commands命令实现类

RelayCommand 是一种常用的命令实现类,通常在 MVVM 模式中用于实现 ICommand 接口,但 WinForms 中并没有自带该类。如果需要使用它,可以自己定义一个简单的 RelayCommand 实现,或从一些 MVVM 库(如 CommunityToolkit.Mvvm)中引入。以下是一个自定义 RelayCommand 类的实现:

using System.Windows.Input;namespace WinFormMVVM.Commands
{public class RelayCommand : ICommand{private readonly Action _execute;private readonly Func<bool> _canExecute;public event EventHandler CanExecuteChanged;public RelayCommand(Action execute, Func<bool> canExecute = null){_execute = execute ?? throw new ArgumentNullException(nameof(execute));_canExecute = canExecute;}public bool CanExecute(object parameter){return _canExecute == null || _canExecute();}public void Execute(object parameter){_execute();}public void RaiseCanExecuteChanged(){CanExecuteChanged?.Invoke(this, EventArgs.Empty);}}
}

五、创建 ViewModel 类

在 MVVM 模式中,ViewModel 负责处理业务逻辑并将数据传递给视图。这里创建 LoginViewModel 类来处理登录逻辑:

using System.ComponentModel;
using System.Windows.Input;
using WinFormMVVM.Models;
using WinFormMVVM.Services;namespace WinFormMVVM.ViewModels
{public class LoginViewModel : INotifyPropertyChanged{private readonly IUserService _userService;public event PropertyChangedEventHandler PropertyChanged;public string Username { get; set; }public string Password { get; set; }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{// 显示登录失败的消息}}}
}

LoginViewModel 通过 _userService 获取用户信息,验证成功后跳转到主页面 MainForm

六、配置 IoC 容器

IoC 文件夹中创建 IoCContainer 静态类,通过依赖注入容器来管理 IDbConnectionIUserService 和其他ViewModel等依赖关系。

using Microsoft.Data.Sqlite;
using Microsoft.Extensions.DependencyInjection;
using System.Data;
using WinFormMVVM.Services;
using WinFormMVVM.ViewModels;namespace WinFormMVVM.IoC
{public static class IoCContainer{public static ServiceProvider Configure(){var services = new ServiceCollection();services.AddSingleton<IDbConnection>(sp =>new SqliteConnection("Data Source=./database.db")); // 设置SQLite数据库路径services.AddSingleton<DatabaseInitializer>();services.AddTransient<IUserService, UserService>();services.AddSingleton<LoginViewModel>();return services.BuildServiceProvider();}}
}

在这里使用了 SQLiteConnection 连接到本地的 SQLite 数据库,连接字符串 Data Source=./database.db 可以根据实际情况修改。

七、创建 View 和绑定 ViewModel

  1. 登录页面(LoginForm):创建一个 LoginForm 窗体,通过构造函数注入 LoginViewModel 实例并绑定到表单。
    在这里插入图片描述
using WinFormMVVM.ViewModels;namespace WinFormMVVM.Views
{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();}}
}

八、设置程序入口并启动依赖注入

Program.cs 文件中配置依赖注入容器,并通过容器注入 LoginViewModel 进入应用的启动界面 LoginForm

using System;
using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
using WinFormMVVM.IoC;
using WinFormMVVM.ViewModels;
using WinFormMVVM.Views;namespace WinFormMVVM
{static class Program{[STAThread]static void Main(){var serviceProvider = IoCContainer.Configure();Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);var loginViewModel = serviceProvider.GetService<LoginViewModel>();var loginForm = new LoginForm(loginViewModel);Application.Run(loginForm);}}
}

创建主页面 MainForm.cs

namespace WinFormMVVM.Views
{public partial class MainForm : Form{public MainForm(){InitializeComponent();}}
}

九、执行程序

通过启动程序,可以实现sqlite数据库自动创建,并且初始化默认数据,同时,通过依赖注入实现服务的运行,和页面ViewModel的注册。输入正确的账号密码,即可登录成功。
在这里插入图片描述

十、总结

本文介绍了如何在 WinForms 中应用 MVVM 模式,并通过 SQLite 进行数据持久化处理。通过引入依赖注入容器,服务类与视图模型的依赖关系可以在应用程序运行时被动态配置,实现了良好的解耦。这样设计的应用不仅具备更好的扩展性和可维护性,还更利于测试和重构。

借助上述框架,可以更清晰地组织 WinForms 项目,将应用逻辑、数据操作、UI 展示解耦,提升代码质量。

相关文章:

WinForms 中使用 MVVM 模式构建应用:实现登录页面、页面导航及 SQLite 数据库连接完整框架搭建过程

前言 在传统的 WinForms 应用程序开发中&#xff0c;很多开发者使用事件驱动的设计模式&#xff0c;直接将业务逻辑编写在界面代码中。然而&#xff0c;随着应用程序的复杂性增加&#xff0c;单一的界面文件变得臃肿&#xff0c;难以测试和维护。借鉴 WPF 中 MVVM&#xff08;…...

Chrome调试工具(查看CSS属性)

来说说这个Chrome调试工具吧&#xff0c;梦回gdb&#xff0c;但是它没有gdb难 打开浏览器 有两种方式可以直接打开Chrome调试工具 直接按F12 鼠标右键页面 --- 检查元素 什么mc玩家是鸣潮 标签页含义 &#x1f912; elements查看标签结构&#xff08;展示html文件&#…...

MQTT从入门到精通之MQTT入门

MQTT入门 1 MQTT概述 1.1 MQTT简介 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;由IBM于1999年开发的一种基于**"发布订阅模式"的轻量级的消息传输协议**&#xff01; 发布订阅模式是一种传统的客户端-服务器架构的替代方案&#xff0c;因为…...

Hadoop生态系统主要包括哪些组件以及它们的作用

Hadoop生态系统是一个开源的大数据处理框架&#xff0c;它主要由一系列组件构成&#xff0c;每个组件都承担着不同的功能和作用。以下是Hadoop生态系统的主要组件及其作用的详细解释&#xff1a; HDFS&#xff08;Hadoop Distributed File System&#xff09; 作用&#xff1a…...

OpenResty 1.27.1.1 已经正式发布

OpenResty 1.27.1.1 已经正式发布&#xff0c;这是一个基于 NGINX 和 LuaJIT 的 web 平台。以下是关于此次发布的一些重点信息和更新内容&#xff1a; 下载与安装 你可以在此处下载最新版本的 OpenResty。提供了便携式源代码分发、Win32/Win64 二进制分发以及为 Ubuntu、Debi…...

定高虚拟列表:让大数据渲染变得轻松

定高虚拟列表 基本认识 在数据如潮水般涌来的今天&#xff0c;如何高效地展示和管理这些数据成为了开发者们面临的一大挑战&#xff0c;传统的列表渲染方式在处理大量数据时&#xff0c;往往会导致页面卡顿、滚动不流畅等问题&#xff0c;严重影响用户体验&#xff08;在页面…...

python request与grequests该如何选择

requests & grequests requests 和 grequests 是Python中用于发送HTTP请求的不同库。requests 是一个同步、阻塞式库&#xff0c;而 grequests 是基于 requests 封装的异步非阻塞库&#xff0c;它利用了 gevent 库提供的协程机制&#xff0c;能够并发发送多个请求。 选择…...

Unity3D UI 拖拽

Unity3D 实现 UI 元素拖拽功能。 UI 拖拽 通常画布上的 UI 元素都是固定位置的&#xff0c;我们可以通过实现拖拽接口&#xff0c;让 UI 元素可以被拖拽到其他位置。 拖拽接口 创建一个脚本 UIDrag.cs&#xff0c;在默认继承的 MonoBehaviour 后面&#xff0c;再继承三个接…...

介绍一下memcpy(c基础)

memcpy函数void *memcpy(void *dest, const void *src, size_t n); dest&#xff1a;指向目标内存区域的指针&#xff0c;即复制的目的地。src&#xff1a;指向源内存区域的指针&#xff0c;即要被复制的内容的来源。n&#xff1a;要复制的字节数 主要功能是将src所指向的内存…...

【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0

目录 一、相关面试题 1. HTTP 与 HTTPS 有哪些区别&#xff1f; 2. HTTPS 的工作原理&#xff1f;&#xff08;https 是怎么建立连接的&#xff09; &#xff08;1&#xff09;ClientHello &#xff08;2&#xff09;SeverHello &#xff08;3&#xff09;客户端回应 &a…...

python-23-一篇文章帮你理解Python推导式

python-23-一篇文章帮你理解Python推导式 一.简介 在 Python 中&#xff0c;推导式&#xff08;Comprehensions&#xff09;是一个简洁的语法&#xff0c;用于通过某种可迭代对象快速生成新的对象&#xff08;如列表、字典、集合等&#xff01;来开始我们今天的日拱一卒&…...

WPF中如何简单的使用CommunityToolkit.Mvvm创建一个项目并进行 增删改查

目录 开始前准备的数据库dbblog如下&#xff1a; 第一步&#xff1a;创建项目后下载四个NuGet程序包 第二步&#xff1a;删除原本的MainWindow.XAML文件 并创建如下的目录结构 然后在View文件夹下面创建Login.XAML和Main.XAML 并且在App.XAML中将启动项改为Login.X…...

CesiumJS 案例 P15:检测标记、鼠标点击移动标记、鼠标拖动标记

CesiumJS CesiumJS API&#xff1a;https://cesium.com/learn/cesiumjs/ref-doc/index.html CesiumJS 是一个开源的 JavaScript 库&#xff0c;它用于在网页中创建和控制 3D 地球仪&#xff08;地图&#xff09; 一、检测标记 <!DOCTYPE html> <html lang"en&…...

Webserver(4.9)本地套接字的通信

目录 本地套接字 本地套接字 TCP\UDP实现不同主机、网络通信 本地套接字实现本地的进程间的通信&#xff0c;类似的&#xff0c;一般采用TCP的通信流程 生成套接字文件 #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h&…...

[IAA系列] Image Aesthetic Assessment

Preface 本文旨在记录个人结合AI工具对IAA这个领域的一些了解&#xff0c;主要是通过论文阅读的方式加深对领域的了解。有什么问题&#xff0c;欢迎在评论区提出并讨论。 什么是IAA Image Aesthetic Assessment&#xff08;图像美学评估&#xff09;是一种评估图像在视觉上的…...

基于springboot的高校科研管理系统(源码+调试+LW)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据你想解决的问题&#xff0c;今天给…...

Flutter环境配置

配置环境变量 PUB_HOSTED_URLhttps://pub.flutter-io.cn FLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 这个命令是用来配置 Flutter 的镜像源地址&#xff0c;主要是为了解决在中国大陆地区访问 Flutter 官方资源较慢的问题 具体的操作做如下: 右键点击"此…...

Rip动态路由及Rip动态路由优化

动态路由Rip Tip&#xff1a;Rip动态路由实现多个路由间不同网段通信。 本次实验目的&#xff0c;通过给ar1,ar2,ar3配置rip动态路由&#xff0c;实现pc1 ping通 pc2。 AR1配置如下&#xff1a; <Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]…...

双路快速排序和三路排序算法

双路快速排序 一、概念及其介绍 双路快速排序算法是随机化快速排序的改进版本&#xff0c;partition 过程使用两个索引值&#xff08;i、j&#xff09;用来遍历数组&#xff0c;将 <v 的元素放在索引i所指向位置的左边&#xff0c;而将 >v 的元素放在索引j所指向位置的…...

SQL server增删改查语句和实例

在 SQL Server 中&#xff0c;增删改查操作分别对应 INSERT、DELETE、UPDATE 和 SELECT 语句。以下是具体介绍及实例&#xff1a; 一、插入数据&#xff08;INSERT&#xff09; 语法&#xff1a; INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, val…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...