c# - 如何在 Windows 窗体应用程序中使用 C#.NET 正确实现 MVC

标签 c# .net winforms model-view-controller

我一直在网上寻找 .NET 中 MVC 设置的示例实现。我找到了很多例子,但它们在某些方面似乎都不同。我有一本关于设计模式的书,它描述了 MVC 起源于 Smalltalk,所以我读了几个人讨论它在该语言中的实现。下面是我编写的一个示例项目,利用我收集到的内容是一个正确的实现,但我对一些细节感到困惑。

我遇到的一个问题是构建对象的正确顺序。这是我的 Program.cs 中的实现

Model mdl = new Model();
Controller ctrl = new Controller(mdl);
Application.Run(new Form1(ctrl, mdl));

观点: 我立即有几个不确定的问题。首先,如果 View 应该只从模型中读取数据进行更新,但包含对它的引用,那么是什么阻止我从视​​图中调用 Controller 对模型所做的调用?程序员是否应该忽略他们暴露于模型成员函数的事实?我的另一个想法是,通知 View 模型更新的事件可能会发送某种状态对象,以便 View 使用它来更新自身。

public interface IView
{
    double TopSpeed { get; }
    double ZeroTo60 { get; }

    int VehicleID { get; }
    string VehicleName { get; }
}

/// <summary>
/// Assume the form has the following controls
/// A button with a click event OnSaveClicked
/// A combobox with a selected index changed event OnSelectedIndexChanged
/// A textbox that displays the vehicles top speed named mTextTopSpeed
/// A textbox that displays the vehicles zero to 60 time named mTextZeroTo60
/// </summary>

public partial class Form1 : Form, IView
{
    private IController mController;
    private IModel mModel;

    public Form1(IController controller, IModel model)
    {
        InitializeComponent();

        mController = controller;
        mController.SetListener(this);
        mModel = model;
        mModel.ModelChanged += new ModelUpdated(mModel_ModelChanged);
    }

    void mModel_ModelChanged(object sender, EventArgs e)
    {
        mTextTopSpeed.Text = mModel.TopSpeed.ToString();
        mTextZeroTo60.Text = mModel.ZeroTo60.ToString();
    }

    public double TopSpeed { get { return Double.Parse(mTextTopSpeed.Text); } }

    public double ZeroTo60 { get { return Double.Parse(mTextZeroTo60.Text); } }

    public int VehicleID { get { return (int)mComboVehicles.SelectedValue; } }

    public string VehicleName { get { return mComboVehicles.SelectedText; } }

    #region Form Events

    private void OnFormLoad(object sender, EventArgs e)
    {
        mComboVehicles.ValueMember = "Key";
        mComboVehicles.DisplayMember = "Value";
        mComboVehicles.DataSource = new BindingSource(mModel.VehicleList, null);
    }

    private void OnSelectedIndexChanged(object sender, EventArgs e)
    {
        mController.OnSelectedVehicleChanged();
    }

    private void OnSaveClicked(object sender, EventArgs e)
    {
        mController.OnUpdateVehicle();
    }

    #endregion
}

Controller : 我实现 Controller 的方式的唯一真正问题是,在没有明确分配 View 的情况下构建 Controller 对我来说似乎有点奇怪。我可以完全忽略 View ,但这意味着我会将参数传递给 Controller ​​的函数以更新模型,这似乎完全忽略了要点。

public interface IController
{
    void OnUpdateVehicle();
    void OnSelectedVehicleChanged();
    void SetListener(IView view);
}

class Controller : IController
{
    private IModel mModel;
    private IView mView = null;

    public Controller(IModel model)
    {
        mModel = model;
    }

    public void OnUpdateVehicle()
    {
        if(mView == null)
            return;

        mModel.UpdateVehicle(mView.VehicleID, mView.TopSpeed, mView.ZeroTo60);
    }

    public void SetListener(IView view)
    {
        mView = view;
    }

    public void OnSelectedVehicleChanged()
    {
        if (mView == null)
            return;
        mModel.SelectVehicle(mView.VehicleID);
    }
}

模型: 在我的表单中,我有一个组合框,它是我的伪数据库中给出的车辆列表。因此,我觉得我的表单实际上应该实现多个 View /模型。一个特定于列出具有相应 Controller /模型的可能车辆的 View ,以及一个用于显示有关具有自己的 Controller /模型的所选车辆的信息的 View 。

public delegate void ModelUpdated(object sender, EventArgs e);

public interface IModel
{
    event ModelUpdated ModelChanged;

    void UpdateVehicle(int id, double topSpeed, double zeroTo60);
    void SelectVehicle(int id);

    double TopSpeed { get; }
    double ZeroTo60 { get; }
    IDictionary<int, string> VehicleList { get; }
}

// class for the sake of a pseudo database object
class Vehicle
{
    public int ID { get; set; }
    public string Name { get; set; }
    public double TopSpeed { get; set; }
    public double ZeroTo60 { get; set; }

    public Vehicle(int id, string name, double topSpeed, double zeroTo60)
    {
        ID = id;
        Name = name;
        TopSpeed = topSpeed;
        ZeroTo60 = zeroTo60;
    }
}


class Model : IModel
{
    private List<Vehicle> mVehicles = new List<Vehicle>()
    {
        new Vehicle(1, "Civic", 120.0, 5.0),
        new Vehicle(2, "Batmobile", 9000.0, 1.0),
        new Vehicle(3, "Tricycle", 5.0, 0.0)
    };

    private Vehicle mCurrentVehicle;

    public Model()
    {
        mCurrentVehicle = mVehicles[0];
    }

    public event ModelUpdated ModelChanged;

    public void OnModelChanged()
    {
        if (ModelChanged != null)
        {
            ModelChanged(this, new EventArgs());
        }
    }

    public double TopSpeed { get { return mCurrentVehicle.TopSpeed; } }

    public double ZeroTo60 { get { return mCurrentVehicle.ZeroTo60; } }

    public IDictionary<int, string> VehicleList
    {
        get 
        {
            Dictionary<int, string> vDict = new Dictionary<int, string>();
            foreach (Vehicle v in mVehicles)
            {
                vDict.Add(v.ID, v.Name);
            }

            return vDict as IDictionary<int, string>;
        }
    }

    #region Pseudo Database Calls

    public void SelectVehicle(int id)
    {
        foreach (Vehicle v in mVehicles)
        {
            if (v.ID == id)
            {
                mCurrentVehicle = v;
                OnModelChanged(); // send notification to registered views
                break;
            }
        }
    }

    public void UpdateVehicle(int id, double topSpeed, double zeroTo60)
    {
        foreach (Vehicle v in mVehicles)
        {
            if (v.ID == id)
            {
                mCurrentVehicle.TopSpeed = topSpeed;
                mCurrentVehicle.ZeroTo60 = zeroTo60;
                OnModelChanged(); // send notification to registered views
                break;
            }
        }
    }

    #endregion
}

在此 tl;dr 的结论中,我想我正在寻找的是关于我在这里所做的是否代表真正的 MVC 实现的一些指导,也许有人可以阐明上述问题.任何建议将不胜感激。

最佳答案

我们会根据你想做什么来决定。您目前有一个监督 Controller 的实现。如果您希望从 View (和任何数据绑定(bind))中删除模型,您可以改为实现被动 View 模式。参见 this有关更多差异的文章。

Passive View and Supervising Controller
(来源:microsoft.com)

Martin Fowler 是国王(GUI Architectures)。

关于c# - 如何在 Windows 窗体应用程序中使用 C#.NET 正确实现 MVC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6712099/

相关文章:

c# - 按下控制键时 RichTextBox 选择错误

.net - 使用 JSON 传递 .NET System.DateTime 以便客户端 javascript 可以构造 javascript Date 对象的最佳方法是什么?

c# - XAML 中的对象初始值设定项

c# - 从子窗体调用父窗体的点击事件,父窗体代码winform中没有任何代码更改

苹果或水果的 C# 类?

c# - 接口(interface)的动态实现

c# - 以编程方式设置 Crystal 报表列的字体样式?

.net - OrderBy、GetNewBindingList 和 Linq to SQL

c# - 如何从另一个类访问 form1 变量?

c# - 在 SQL Server 和 C# 中运行查询的结果不同