C# WPF MVVM模式技术咨询:新手转型疑问
从直接写数据库代码到MVVM:WPF新手的入门改造指南
嘿,作为刚入门编程的WPF新手,没用地设计模式直接写数据库代码太正常啦——我当初刚接触WPF的时候也这么干过,把所有逻辑都堆在后台代码里,后来维护起来简直头大😂。咱们一步步把你原来的代码改造成符合MVVM模式的结构,你就能明白为啥这种模式更适合WPF了。
先说说你原来写法的问题
你之前直接在UI后台写SqlConnection、SqlCommand这类代码,会导致:
- UI和数据逻辑高度耦合:改个按钮样式或者数据库连接字符串,都得翻同一堆代码
- 不好测试:想验证数据库查询是否正确,必须启动整个WPF应用,没法单独测试逻辑
- 代码乱糟糟:业务逻辑、UI事件、数据库操作混在一起,后期加功能越写越乱
MVVM的核心思路:分层解耦
MVVM把代码分成三个核心层:
- Model(模型):对应数据库里的实体(比如你的Product),以及数据访问逻辑(和数据库打交道的代码)
- ViewModel(视图模型):作为UI和Model的中间层,处理业务逻辑,给UI提供可绑定的数据和命令
- View(视图):就是你的XAML界面,只负责展示数据和响应用户操作,不写业务逻辑
一步步改造你的代码
1. 先写Model层:封装数据库操作和实体
首先把数据库操作抽成单独的类,再定义Product实体:
// 数据访问类:专门处理和数据库的交互 public class ProductDataAccess { // 把连接字符串抽出来,方便后期修改 private readonly string _connectionString = "Server = localhost;Database = Bilanc; Integrated Security = true"; // 调用存储过程获取产品列表的方法 public List<Product> GetProductsFromDb() { var productList = new List<Product>(); // 用using自动释放资源,避免内存泄漏 using (SqlConnection con = new SqlConnection(_connectionString)) { using (SqlCommand cmd = new SqlCommand("Product", con)) { cmd.CommandType = CommandType.StoredProcedure; con.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { // 把数据库读取到的字段映射到Product实体 var product = new Product { Id = reader.GetInt32(reader.GetOrdinal("Id")), Name = reader.GetString(reader.GetOrdinal("Name")), // 这里根据你的存储过程返回的字段,补充其他属性比如Price、Stock等 }; productList.Add(product); } } } } return productList; } } // Product实体类:对应数据库里的产品表结构 public class Product { public int Id { get; set; } public string Name { get; set; } // 按需添加其他属性:public decimal Price { get; set; } 之类的 }
2. 再写ViewModel层:连接UI和数据
ViewModel要实现INotifyPropertyChanged接口,让UI能自动响应数据变化;还要定义命令,让UI按钮能触发加载数据的逻辑:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Input; public class ProductViewModel : INotifyPropertyChanged { private readonly ProductDataAccess _dataAccess; // 用ObservableCollection代替List,这样数据变化时UI会自动更新 public ObservableCollection<Product> Products { get; set; } // 定义加载产品的命令,绑定到UI按钮 public ICommand LoadProductsCommand { get; } public ProductViewModel() { _dataAccess = new ProductDataAccess(); Products = new ObservableCollection<Product>(); // 初始化命令,关联到加载数据的方法 LoadProductsCommand = new RelayCommand(LoadProducts); } // 实际加载数据的方法 private void LoadProducts() { var products = _dataAccess.GetProductsFromDb(); Products.Clear(); foreach (var product in products) { Products.Add(product); } } // 实现INotifyPropertyChanged接口,通知UI数据变化 public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // 通用的RelayCommand实现:WPF里用来绑定UI命令的常用类 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); } }
3. 最后写View层:纯XAML绑定
你的XAML界面只需要绑定ViewModel的属性和命令,不用写任何后台逻辑:
<Window x:Class="BilancApp.ProductWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:BilancApp" Title="产品列表" Height="450" Width="800"> <!-- 设置DataContext为ViewModel,让UI能找到绑定的数据源 --> <Window.DataContext> <local:ProductViewModel /> </Window.DataContext> <StackPanel Margin="10"> <!-- 按钮绑定LoadProductsCommand,点击就会触发ViewModel里的LoadProducts方法 --> <Button Content="加载产品" Command="{Binding LoadProductsCommand}" Margin="0 0 0 10"/> <!-- DataGrid绑定Products集合,自动展示产品数据 --> <DataGrid ItemsSource="{Binding Products}" AutoGenerateColumns="True"/> </StackPanel> </Window>
这么改造的好处
- 代码更整洁:每个层只干自己的事,找问题、加功能都更方便
- 可测试性强:可以单独写测试用例,直接调用ViewModel的
LoadProducts方法,不用启动UI就能验证逻辑 - UI和逻辑分离:设计师改XAML界面,程序员改ViewModel逻辑,互不干扰
- 符合WPF的设计理念:充分利用数据绑定,不用手动去更新UI控件的内容
刚开始可能觉得MVVM有点繁琐,但写多了之后会发现它能帮你省很多麻烦,尤其是开发复杂的WPF应用时。如果觉得手动写RelayCommand麻烦,后期也可以用MvvmLight、Prism这类MVVM框架来简化开发~
内容的提问来源于stack exchange,提问作者lex
相关产品推荐
相关产品推荐

