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还有一个好处就是可用验证输入数据合理性

( ̄▽ ̄)"

 

 

posted @ 2020-11-25 16:18  ScaleZ  阅读(672)  评论(0编辑  收藏  举报