winform 里使用MVVM模式
https://www.cnblogs.com/bluedoctor/p/6060278.html
让WinForms焕发新春 火热的MVVM框架
MVVM最早是在WPF开发中了解到的,现在流行的web前端框架Vue也借鉴了这种思路:Viewmodel Binder View,我个人的话说就是:视图就是数据,数据就是视图。
在web端使用Vue.js能很方便使数据与element进行双向绑定,使得前后端的耦合度大大的降低;
重新回到winform,使用MVVM来做项目,顺便记录下,注意:只是viewModel与view的双向绑定,viewmodel与Model转换自己想方法。
先来2个Viewmodel,操作重点是Student,继承INotifyPropertyChanged,当ViewModel的属性发生改变,即执行了set访问器时,触发PropertyChanged事件,通知前端
public class Room { public int Id { get; set; } string _number; string _building; int _max; public string Number { get; set; } public string Building { get; set; } public string Address { get { if (Number != null && Building != null) { return Building + Number; } return ""; } } public int Max { get; set; } }
public class Student : INotifyPropertyChanged { private string _name = string.Empty; private DateTime _brithday = DateTime.Parse("1753-01-01 00:00:00.000"); private double _high; private decimal _money; private bool _enable = true; private int? _roomId; private Room _room; private double _qty; private decimal _price; public int Id { get; set; } public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } } public DateTime Brithday { get { return _brithday; } set { _brithday = value; OnPropertyChanged("Brithday"); } } public double High { get { return _high; } set { _high = value; OnPropertyChanged("High"); } } public decimal Money { get { return _money; } set { _money = value; OnPropertyChanged("Money"); } } public bool Enable { get { return _enable; } set { _enable = value; OnPropertyChanged("Enable"); } } public int? RoomId { get { if (Room != null) { _roomId = Room.Id; } return _roomId; } set { } } public Room Room { get { return _room; } set { _room = value; OnPropertyChanged("Room"); } } public double Qty { get { return _qty; } set { _qty = value; OnPropertyChanged("Qty"); OnPropertyChanged("Account"); } } public decimal Price { get { return _price; } set { _price = value; OnPropertyChanged("Price"); OnPropertyChanged("Account"); } } public decimal Account { get { if (_qty != 0 && _price != 0) { var s = decimal.Parse(_qty.ToString()) * _price; return s; } return 0; } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string proName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(proName)); } }
然后是winform界面,要对student对象操作,把这些textbox,checkbox,combobox的tag设置为对应student对象的属性名
思路:通过找到窗体内所有控件,如果tag属性与studnet的属性相同,则把控件与student对象属性绑定;
先上后台代码:
public partial class MVVM : Form { private Student VModel; private List<Room> Rooms = new List<Room>() { new Room {Id=0, Building="",Number="请选择..." } }; public MVVM() { InitializeComponent(); #region 模拟数据 VModel = new Student { //属性赋值表示以修改模式打开这个页面,如果是新增模式,把属性都注释掉 //已测试,修改、新增模式都可通过 Id = 1, Name = "Jack", Brithday = DateTime.Now.AddYears(-22), Enable = true, High = 175.4, Money = 10000000000.5m, Room = new Room { Id = 2, Building = "A栋", Number = "102", Max = 8 }, RoomId = 2 }; if (VModel.Room != null) Rooms.Add(VModel.Room); #endregion } /// <summary> /// 递归获取所有控件 /// </summary> /// <param name="ctl"></param> /// <returns></returns> private List<Control> GetAllControl(Control ctl) { List<Control> result = new List<Control>(); if (ctl.HasChildren) { if (!(ctl is Form)) result.Add(ctl); foreach (Control item in ctl.Controls) { result.AddRange(GetAllControl(item)); } } else { result.Add(ctl); } return result; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); this.cbxRoom.DisplayMember = "Address"; this.cbxRoom.ValueMember = "Id"; this.cbxRoom.DataSource = Rooms; List<Control> list = GetAllControl(this); var propes = VModel.GetType().GetProperties(); try { foreach (Control item in list) { var prop = propes.FirstOrDefault(a => item.Tag != null && a.Name == item.Tag.ToString()); if (prop != null) { if (item is TextBox) item.DataBindings.Add(new Binding("Text", VModel, prop.Name)); if (item is CheckBox) item.DataBindings.Add(new Binding("Checked", VModel, prop.Name)); if (item is ComboBox) item.DataBindings.Add(new Binding("SelectedItem", VModel, prop.Name)); } } } catch (Exception) { throw; } } /// <summary> /// 查看 button /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { MessageBox.Show(Newtonsoft.Json.JsonConvert.SerializeObject(VModel)); } /// <summary> /// 更改属性 button /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { #region 测试更改viewmodel属性,视图是否会更新 VModel.Name = "Bob"; Room r6 = Rooms.FirstOrDefault(a => a.Id == 6); if (r6 != null) VModel.Room = r6; VModel.Qty = 120; VModel.Price = 12; #endregion } /// <summary> /// 标识是否已从服务器获取到数据 /// </summary> private bool IsLoadRooms = false; /// <summary> /// room combobox DropDown事件处理,第一次从服务器获取数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void cbxRoom_DropDown(object sender, EventArgs e) { if (!IsLoadRooms) { #region 模拟请求服务器得到数据 for (int i = 0; i < 10; i++) { Room r = new Room { Building = "A栋", Max = 8, Number = "10" + (i + 1), Id = i + 1 }; if (!Rooms.Exists(a => a.Id == r.Id)) { Rooms.Add(r); } } #endregion //重新绑定数据源 this.cbxRoom.DataSource = null; this.cbxRoom.DisplayMember = "Address"; this.cbxRoom.ValueMember = "Id"; this.cbxRoom.DataSource = Rooms; IsLoadRooms = true; } } }
说明:
1、GetAllControl 方法获取所有控件,toolstrip里 的toolstripButton是不能获取到的,还有其他的一些控件,这里不谈论;
2、当前页面是进行对象进行新增、修改、参看等操作的页面
3、VModel的Room属性类型为其他对象,需要对其进行操作时,第一次从服务器端请求数据源,保存到当前的Rooms List里;
4、不同控件绑定的属性是不同的,如name属性类型为string,可用放到textbox里绑定,即textbox.text=name,同理,checkbox绑定checked 对象类型为bool,combobox是数据源选择,默认给一个提示选择item,然后让他绑定数据源rooms,新增操作时,vmodel.Room是null,所有当前combobox.selecteditem还是默认第一个;以修改模式打开时vmodel.Room有值时,经过binding后,selecteditem就是当前vmodel.Room,当然前提是把Room属性值放到rooms中;
现在运行试试
效果还不错,对了,binding还有一个好处就是可用验证输入数据合理性
( ̄▽ ̄)"