在 ASP.NET Web Forms 应用程序中实现 MVC 模式。






4.69/5 (30投票s)
本教程描述了 MVC 模式,并说明了如何在 ASP.Net Web Forms 应用程序中实现 MVC 模式。
引言
本教程通过图示向技术人员和非技术人员描述了 MVC 模式。本教程还通过示例展示了如何在 ASP.NET Web Forms 应用程序中实现 MVC 模式。
目录
- 第一部分: MVC 模式(Model-View-Controller)简介。
- 第二部分: 使用 MVC 模式的 ASP.NET Web Forms 项目示例。
第一部分: MVC 模式简介
什么是设计模式?
根据我的理解,模式是在软件开发中针对重复出现问题的解决方案。模式是使用面向对象原则实现的。它是对特定问题的已批准解决方案。例如,观察者模式用于通知依赖于某个对象的对象,而这些对象又依赖于该对象。观察者模式在 MVC 实现中使用。
什么是 MVC?
模型-视图-控制器 (MVC) 是一种流行的 UI 设计模式。它用于表示表示层。该模式分离了应用程序代码。MVC 模式有三个组件,分别称为模型 (Model)、视图 (View) 和控制器 (Controller)。下图说明了 MVC 模式的组件及其关系,即使非技术读者也能清楚理解。
图: MVC 模式的组件及其关系(非技术图例)
上图清楚地展示了 MVC 的主要组件及其关系。让我们详细描述一下。视图 (View): 视图表示用户界面。它显示的数据来自模型组件。当控制器更新模型时,视图会收到模型的更新通知。
- 控制器 (Controller): 控制器充当视图和模型之间的中介。当用户触发事件时,视图会接收该事件并将其交给控制器。然后控制器进行数据验证,如果有效,则更新模型。它还会在需要时更新视图。
- 模型 (Model): 模型包含应用程序的数据。当控制器更改模型数据时,模型会通知视图进行更新,然后视图从模型获取更新的数据并更新 UI。
下图是 MVC 模式的 UML 类图。
上图展示了类及其之间的关系。在这里,控制器和视图类之间存在双向关系。模型和视图之间也存在双向关系,而控制器和模型之间是单向关系。
下图是 MVC 模式的序列图。
上面的序列图从用户请求开始,当用户在 UI 上触发事件时。视图向控制器发送 `RequestUpdate` 消息,并将自身作为参数传递。控制器向模型发送 `UpdateData` 消息,并将更新后的值作为参数传递。在模型本身更新后,它使用 `NotifyUpdate` 消息通知视图,并将自身作为参数传递。收到通知后,视图会使用更新后的值更新 UI,并从模型获取更新的值。
以上就是关于 MVC 模式及其组件模型以及它们如何通过方法调用进行交互的内容。
第二部分: 使用 MVC 模式的 ASP.NET Web Forms 项目示例
概述
以下屏幕截图提供了我将要解释的 ASP.NET Web Forms 应用程序的快速概览。当您从 Visual Studio 运行该应用程序时,将看到以下输出。
项目描述
该示例应用程序显示了 Windows 操作系统配置要求,用户可以更新这些配置。
现在,是时候逐步深入项目了。
- 创建一个 ASP.NET Web Forms 项目,如下图所示的解决方案资源管理器屏幕截图。
描述: 在新创建的项目中,我添加了三个名为 Controllers、Models 和 Views 的文件夹,分别用于存放控制器、模型和视图类。
2. 在文件 `Windows98Controller.cs` 中创建一个控制器类。以下代码显示了控制器类。
public class Window98Controller
{
private Window98Model Model;
private Windows98View View;
public Window98Controller(Window98Model paramModel, Windows98View paramView)
{
Model = paramModel;
View = paramView;
}
public void InitializedConponent(bool ispostback)
{
if(!ispostback)
View.InitializedView(Model);
}
public Window98Controller()
{
}
public void RequestUpdate(Windows98View view)
{
if (Model != null)
{
Model.UpdateModel(view.Ram,view.Disk,view.CPUSpeed);
}
}
}
描述: 构造函数 `Window98Controller (Window98Model paramModel, Windows98View paramView)` 用于将模型和视图传递给控制器,以便在编译时建立它们之间的关系并初始化值。`RequestUpdate()` 方法由视图调用,它会调用模型的 `UpdateModel()` 方法。
3. 在文件 `Windows98Model.cs` 中创建一个模型类。以下代码显示了模型类
public class Window98Model
{
private ArrayList aList = new ArrayList();
public Window98Model(string paramName,decimal paramMinRam,
decimal paramMinDisk, decimal paramCpuSpeedd)
{
MinRam = paramMinRam;
MinDisk = paramMinDisk;
CPUSpeed = paramCpuSpeedd;
Name = paramName;
Ram = paramMinRam;
Disk = paramMinDisk;
}
public string Name{ get; set;}
public decimal Ram { get; set; }
public decimal Disk { get; set; }
public decimal MinRam { get; set; }
public decimal MinDisk { get; set; }
public decimal CPUSpeed { get; set; }
public void UpdateModel(decimal paramRam, decimal paramDisk, decimal paramCPUSpace)
{
Ram += paramRam;
Disk += paramDisk;
CPUSpeed += paramCPUSpace;
this.NotifyObservers();
}
public void AddObserver(Windows98View paramView)
{
aList.Add(paramView);
}
public void RemoveObserver(Windows98View paramView)
{
aList.Remove(paramView);
}
public void NotifyObservers()
{
foreach (Windows98View view in aList)
{
view.Update(this);
}
}
}
描述: 这里使用构造函数来初始化模型。`UpdateModel()` 方法用于更新模型,由控制器调用。`AddObserver()` 和 `RemoveObserver()` 方法分别用于将视图添加为模型的观察者和从模型中删除观察者。`NotifyObservers()` 方法用于遍历观察者列表并通知所有观察者。这里只使用了一个观察者。
4. 创建一个名为 `Windows98View.ascx` 的用户控件。
视图的 HTML 部分如下所示。
<%@ Control Language=C# AutoEventWireup=true CodeBehind=Windows98View.ascx.cs
Inherits=MVCForWinForms.Views.Windows98View %>
<div style="height: 30px; background-color: rgb(0, 148, 255); color: white; margin-bottom: 10px;">
<asp:label style="display: block;"
text="Windows 98 System Configuration" runat="server" id="label1" />
</div>
<span style="float: left; font-weight: bold; color: rgb(0, 148, 255);">Current Status: </span>
<span>Ram:</span><asp:label runat="server" id="lblRam" /><span>GB</span>
<span>Disk:</span><asp:label runat="server" id="lblDisk" /><span>GB</span>
<span>CPU Speed:</span><asp:label runat="server" id="lblcpuspeed" /><span>GHz</span>
<table style="padding: 10px 0px; float: left; width: 365px;">
<tbody><tr>
<td style="font-weight: bold;">RAM:</td>
<td><asp:textbox runat="server" id="txtRam" /></td>
<td>GB</td>
</tr>
<tr>
<td style="font-weight: bold;">Disk Space:</td>
<td><asp:textbox runat="server" id="txtDisk" /></td>
<td>GB</td>
</tr>
<tr>
<td style="font-weight: bold;">Processor:</td>
<td><asp:textbox runat="server" id="txtProcessor" /></td>
<td>GHz</td>
</tr>
<tr>
<td>
<asp:button onclick="btnUpdate_Click"
text="Update" runat="server" id="btnUpdate">
</asp:button></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
视图的代码隐藏部分如下所示。
public partial class Windows98View : System.Web.UI.UserControl
{
private Window98Controller Control;
private Window98Model Model = new Window98Model("Windows 98", 2, 10, 2);
protected void Page_Load(object sender, EventArgs e)
{
Control = new Window98Controller(Model,this);
Model.AddObserver(this);
Control.InitializedConponent(IsPostBack);
}
public decimal Ram { get; set; }
public decimal Disk { get; set; }
public decimal CPUSpeed { get; set; }
protected void btnUpdate_Click(object sender, EventArgs e)
{
CPUSpeed = Convert.ToDecimal(txtProcessor.Text.ToString());
Ram = Convert.ToDecimal(txtRam.Text.ToString());
Disk = Convert.ToDecimal(txtDisk.Text.ToString());
Control.RequestUpdate(this);
}
public void Update(Window98Model paramModel)
{
UpdateInterface(paramModel);
}
public void UpdateInterface(Window98Model auto)
{
if (Ram != auto.Ram)
{
lblRam.Text = auto.Ram.ToString("n2");
}
if (Disk != auto.Disk)
{
lblDisk.Text = auto.Disk.ToString("n2");
}
if (CPUSpeed != auto.CPUSpeed)
{
lblcpuspeed.Text = auto.CPUSpeed.ToString("n2");
}
}
public void InitializedView(Window98Model paramModel)
{
lblRam.Text = paramModel.Ram.ToString("n2");
lblDisk.Text = paramModel.Disk.ToString("n2");
lblcpuspeed.Text = paramModel.CPUSpeed.ToString("n2");
}
}
描述: 这里创建了一个具有初始值的模型对象,并将其传递给其控制器类,然后调用其 `AddObserver()` 方法将视图绑定为观察者。
注意: 这里没有使用关系数据库。模型通过将其数据传递到其构造函数来初始化,而视图则使用该数据进行初始化。
5. 最后,创建一个页面 `SystemCheck.aspx`,在该页面上运行用户控件。页面的 HTML 部分如下所示。
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="SystemCheck.aspx.cs" Inherits="MVCForWinForms.SystemCheck" %>
<%@ Register Src="~/Views/Windows98View.ascx" TagPrefix="uc3" TagName="Windows98" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<style type="text/css">
body {
font-family:Verdana;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div style="border:1px solid; width:500px; height:500px; margin:auto auto;">
<uc3:Windows98 runat="server" id="Windows98" />
</div>
</form>
</body>
</html>
结论
此示例包含了视图、模型和控制器之间的耦合。为了重用模型和进行单元测试,我们应该减少耦合和对视图的依赖。在接下来的教程中,我将描述如何减少耦合并为其他操作系统配置重用模型,以及如何进行单元测试。