c# /WinForms中的模型-视图-控制器(MVC)模式

介绍 本文使用c# /WinForm演示了。net中的MVC模式。 这是一个简单的“用户信息管理器”应用程序,它根据 模型-视图-控制器(MVC)模式。 该应用程序显示联系人列表,并允许您添加、修改和删除 现有的联系人。其思想是将用户界面分离到视图(创建)中 显示,在需要时调用模型来获取信息)和控制器 (响应用户请求,必要时与视图和控制器交互)。 MVC模式的主要优点是松散耦合。所有的层都是分开的 有自己的功能。用其他类型替换层很容易 的层。换句话说,MVC模式将UI行为分解为不同的部分 为了增加重用的可能性和可测试性。我用的是Visual Studio 2010年的终极版。net 4.0用于创建这个应用程序。 背景 模型-视图-控制器,顾名思义,考虑三个方面: 模型:它应该负责应用程序域视图的数据:它在用户界面控制器中显示模型:它是MVC的核心,是将模型和视图联系在一起的中介,也就是说,它接收用户输入,操作模型和视图。导致视图更新 ,,,,,,,,,,,,,,,,, 有关MVC的更多信息,请参阅*下面的文章 解决方案 用户信息管理器是一个应用程序,可以在其中存储客户的联系方式 信息。应用程序显示联系人列表和 允许您添加、修改和删除现有的联系人。首先,所有客户都有一个ID 名字,姓和性。这个应用程序的操作员用来维护他的列表的屏幕 顾客可能会是这样的: ,,,,,, 客户列表可以查看,添加,删除和更新(目前是只包含vipP成员, 但如果你想成为俱乐部的一员,只需问" src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />…没有问题,它是免费的)。 添加新用户后,他的ID不能再更改了。 类图 在系统的设计中,许多类被识别并组合在一个类图中,这有助于确定对象之间的关系。 组件的描述 控制器的一部分 为了将逻辑从视图中分离出来,我们必须让视图尽可能地感到无能为力,因此我们宁愿让控制器来完成所有艰苦的工作,而只给视图一些不需要进一步处理的简单命令。 根据我们的设计,我们通过定义一个接口IUsersView来做到这一点,视图必须实现这个接口。 这个接口只包含我们需要使用的属性/方法的签名。 隐藏,复制Code

using System;
using WinFormMVC.Model;

namespace WinFormMVC.Controller
{
    public interface IUsersView
    {
        void SetController(UsersController controller);
        void ClearGrid();
        void AddUserToGrid(User user);
        void UpdateGridWithChangedUser(User user);
        void RemoveUserFromGrid(User user);
        string GetIdOfSelectedUserInGrid();
        void SetSelectedUserInGrid(User user);

        string FirstName     { get; set; }
        string LastName      { get; set; }
        string ID    { get; set; }
        string Department    { get; set; }
        User.SexOfPerson Sex { get; set; }  
        bool CanModifyID     {      set; }
    }
}

现在我们有了一个相当不错的接口,其中有许多方法。 即使MVC模式正式声明控制器应该接收事件并对视图采取行动,让视图订阅事件并将处理委托给控制器通常更实际、更容易。 最后,我将展示控制器的实际实现(参见UsersController类)。他将模型(用户类)与视图(UserView类)挂钩。 隐藏,收缩,复制Code

public class UsersController
{
    //Notice we only use the interfaces. This makes the test more 
    //robust to changes in the system.
    IUsersView _view;
    IList      _users;
    User       _selectedUser;

    //The UsersController depends on abstractions(interfaces).
    //It's easier than ever to change the behavior of a concrete class. 
    //Instead of creating concrete objects in UsersController class, 
    //we pass the objects to the constructor of UsersController
    public UsersController(IUsersView view, IList users)
    {
       _view = view;
       _users = users;
       view.SetController(this);
    }

    public IList Users
    {
       get { return ArrayList.ReadOnly(_users); }
    }

    private void updateViewDetailValues(User usr)
    {
       _view.FirstName   =  usr.FirstName;
       _view.LastName    =  usr.LastName;
       _view.ID  =  usr.ID;
       _view.Department  =  usr.Department;
       _view.Sex =  usr.Sex;
    }

    private void updateUserWithViewValues(User usr)
    {
       usr.FirstName     =  _view.FirstName;
       usr.LastName      =  _view.LastName;
       usr.ID    =  _view.ID;
       usr.Department    =  _view.Department;
       usr.Sex   =  _view.Sex;
    }

    public void LoadView()
    {
       _view.ClearGrid();
       foreach (User usr in _users)
       _view.AddUserToGrid(usr);

       _view.SetSelectedUserInGrid((User)_users[0]);
    }

    public void SelectedUserChanged(string selectedUserId)
    {
       foreach (User usr in this._users)
       {
           if (usr.ID == selectedUserId)
           {
               _selectedUser = usr;
               updateViewDetailValues(usr);
               _view.SetSelectedUserInGrid(usr);
               this._view.CanModifyID = false;
               break;
           }
       }
    }

    public void AddNewUser()
    {
       _selectedUser = new User(""   /*firstname*/, 
       ""  /*lastname*/, 
       "" /*id*/, 
       ""/*department*/,
       User.SexOfPerson.Male/*sex*/);
     
       this.updateViewDetailValues(_selectedUser);
       this._view.CanModifyID = true;
    }

    public void RemoveUser()
    {
       string id = this._view.GetIdOfSelectedUserInGrid();
       User userToRemove = null;

        if (id != "")
        {
            foreach (User usr in this._users)
            {
                if (usr.ID == id)
                {
                    userToRemove = usr;
                break;
                }
            }

            if (userToRemove != null)
            {
                int newSelectedIndex = this._users.IndexOf(userToRemove);
                this._users.Remove(userToRemove);
                this._view.RemoveUserFromGrid(userToRemove);

            if (newSelectedIndex > -1 && newSelectedIndex < _users.Count)
            {
                this._view.SetSelectedUserInGrid((User)_users[newSelectedIndex]);
            }
        }
    }
}

public void Save()
{
   updateUserWithViewValues(_selectedUser);
   if (!this._users.Contains(_selectedUser))
   {
       //Add new user
       this._users.Add(_selectedUser);
       this._view.AddUserToGrid(_selectedUser);
   }
   else
   {
       //Update existing user
       this._view.UpdateGridWithChangedUser(_selectedUser);
   }
   _view.SetSelectedUserInGrid(_selectedUser);
   this._view.CanModifyID = false;

}
}

控制器类非常重要,并且是应用程序的中心。保持它的轻便、敏捷和与程序的其他组件松散耦合是非常重要的。 一部分的观点 本节将重点讨论加载带有用户列表的视图的场景。 如前所述,我们的视图必须实现IUsersView接口。实现的一个子集如下面的代码所示: 隐藏,复制Code

namespace WinFormMVC.View
{
    public partial class UsersView : Form, IUsersView
    {

UsersView的SetController()成员函数允许我们告诉视图它必须将事件转发给哪个控制器实例,所有的事件处理程序只需调用控制器上相应的“event”方法。正如你在这里看到的,UsersView也依赖于抽象… 隐藏,复制Code

public void SetController(UsersController controller)
{
    _controller = controller;
}

我们也使用几个方法实现从IUsersView接口使用用户对象: 隐藏,收缩,复制Code

public void AddUserToGrid(User usr)
{
    ListViewItem parent;
    parent = this.grdUsers.Items.Add(usr.ID);
    parent.SubItems.Add(usr.FirstName);
    parent.SubItems.Add(usr.LastName);
    parent.SubItems.Add(usr.Department);
    parent.SubItems.Add(Enum.GetName(typeof(User.SexOfPerson), usr.Sex));
}

public void UpdateGridWithChangedUser(User usr)
{
    ListViewItem rowToUpdate = null;

    foreach (ListViewItem row in this.grdUsers.Items)
    {
        if (row.Text == usr.ID)
        {
            rowToUpdate = row;
        }
    }

    if (rowToUpdate != null)
    {
        rowToUpdate.Text = usr.ID;
        rowToUpdate.SubItems[1].Text = usr.FirstName;
        rowToUpdate.SubItems[2].Text = usr.LastName;
        rowToUpdate.SubItems[3].Text = usr.Department;
        rowToUpdate.SubItems[4].Text = Enum.GetName(typeof(User.SexOfPerson), usr.Sex);
    }
}

public void RemoveUserFromGrid(User usr)
{

    ListViewItem rowToRemove = null;

    foreach (ListViewItem row in this.grdUsers.Items)
    {
        if (row.Text == usr.ID)
        {
            rowToRemove = row;
        }
    }

    if (rowToRemove != null)
    {
        this.grdUsers.Items.Remove(rowToRemove);
        this.grdUsers.Focus();
    }
}

public string GetIdOfSelectedUserInGrid()
{
    if (this.grdUsers.SelectedItems.Count > 0)
        return this.grdUsers.SelectedItems[0].Text;
    else
        return "";
}

public void SetSelectedUserInGrid(User usr)
{
    foreach (ListViewItem row in this.grdUsers.Items)
    {
        if (row.Text == usr.ID)
        {
            row.Selected = true;
        }
    }
}

public string FirstName 
{
    get { return this.txtFirstName.Text; }
    set { this.txtFirstName.Text = value; }
}

public string LastName 
{
    get { return this.txtLastName.Text; }
    set { this.txtLastName.Text = value; }
}

public string ID
{
    get { return this.txtID.Text; }
    set { this.txtID.Text = value; }
}


public string Department 
{
    get { return this.txtDepartment.Text; }
    set { this.txtDepartment.Text = value; }
}

public User.SexOfPerson Sex
{
    get
    {
        if (this.rdMale.Checked)
            return User.SexOfPerson.Male;
        else
            return User.SexOfPerson.Female;
    }
    set
    {
        if (value == User.SexOfPerson.Male)
            this.rdMale.Checked = true;
        else
            this.rdFamele.Checked = true;
    }
}

public bool CanModifyID
{
    set { this.txtID.Enabled = value; }
}

        ...
}

模型的一部分 这个用户类是一个模型类。在这个例子中,User是一个没有行为的极其简单的域类,而在一个真实的域模型中,您可能拥有更多的功能域类。 模型独立于用户界面。它不知道它是通过文本、图形还是网络界面使用的。 模型仅以结构化格式保存内存中的状态。如您所见,该类只包含私有数据成员和客户端代码可用的公共接口(属性) 隐藏,收缩,复制Code

using System;

namespace WinFormMVC.Model
{
 public class User
 {
   public enum SexOfPerson
   {
      Male   = 1,
      Female = 2
   }

   private string    _FirstName;
   public string FirstName 
   {
    get { return _FirstName; } 
    set 
    { 
     if (value.Length > 50)
      Console.WriteLine("Error! FirstName must be less than 51 characters!"); 
     else
      _FirstName = value; 
    } 
   }

   private string _LastName;
   public string LastName
   {
     get { return _LastName; }
     set
     {
      if (value.Length > 50)
       Console.WriteLine("Error! LastName must be less than 51 characters!");
      else
       _LastName = value;
    }
   }

   private string _ID;
   public string ID
   {
    get { return _ID; }
    set
    {
      if (value.Length > 9)
        Console.WriteLine("Error! ID must be less than 10 characters!");
      else
       _ID = value;
    }
   }

   private string _Department;
   public string Department
   {
       get { return _Department; }
       set { _Department = value; }
   }

   private SexOfPerson _Sex;
   public SexOfPerson Sex
   {
       get { return _Sex; }
       set { _Sex = value; }
   }


   public User(string firstname, string lastname, string id, string department, SexOfPerson sex)
   {
       FirstName   = firstname;
       LastName    = lastname;
       ID  = id;
       Department  = department;
       Sex = sex;
   }
 } 

} 

的一部分客户 现在是展示如何有效使用MVC范例的好时机,也就是MVC架构的代码组件(请参见usemvcappe .csproj) 隐藏,收缩,复制Code

using System.Collections;
using  WinFormMVC.Model;
using  WinFormMVC.View;
using  WinFormMVC.Controller;

namespace UseMVCApplication
{
static class Program
{
    /// The main entry point for the application.
    [STAThread]
    static void Main()
    {
        //Here we are creating a View
        UsersView view = new UsersView();
        view.Visible = false;

        //Here we are creating a list of users
        IList users = new ArrayList();
    
        //Here we are add our "commoners" in the list of users
        users.Add(new User("Vladimir",   "Putin",     
     "122",    "Government of Russia",
            User.SexOfPerson.Male));
        users.Add(new User("Barack",     "Obama",
             "123",    "Government of USA",      
             User.SexOfPerson.Male));
        users.Add(new User("Stephen",    "Harper",    
            "124",    "Government of Canada",      
            User.SexOfPerson.Male));
        users.Add(new User("Jean",       "Charest", 
            "125",    "Government of Quebec",    
             User.SexOfPerson.Male));
        users.Add(new User("David",      "Cameron",
            "126",    "Government of United Kingdom",
            User.SexOfPerson.Male));
        users.Add(new User("Angela",     "Merkel",
            "127",    "Government of Germany",
            User.SexOfPerson.Female));
        users.Add(new User("Nikolas",    "Sarkozy",
            "128",    "Government of France",
            User.SexOfPerson.Male));
        users.Add(new User("Silvio",     "Berlusconi",
            "129",    "Government of Italy",
            User.SexOfPerson.Male));
        users.Add(new User("Yoshihiko",  "Noda",
            "130",    "Government of Japan",
            User.SexOfPerson.Male));
     
        //Here we are creating a Controller and passing two
        //parameters: View and list of users (models)
        UsersController controller = new UsersController(view, users);
        controller.LoadView();
        view.ShowDialog();
        }
    }
}

为什么它是好的? 使用MVC模式的主要优点是,它使得用户界面的可测试性的代码是一个结构化的方法在设计用户界面的过程中,它本身有助于编写干净、可测试的代码,易于维护和扩展 模型-视图-控制器是一种经过良好验证的设计模式 解决数据(模型)与用户界面(视图)分离的问题 用户界面的变化不会影响数据处理, 数据可以在不影响/改变UI的情况下进行更改。MVC 通过将数据访问和业务逻辑层与UI分离来解决这个问题 和用户交互,通过引入中间组件:控制器。 该MVC体系结构允许在组件中创建可重用组件 灵活的程序设计(组件修改容易) 历史 2012年5月10日:入职 本文转载于:http://www.diyabc.com/frontweb/news5051.html