65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 2 基础 - 使用 ListBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (16投票s)

2010年12月15日

CPOL

7分钟阅读

viewsIcon

114851

downloadIcon

4537

演示了如何在 ASP.NET MVC 2 中在两个 ListBox 之间移动项目。

引言

在很多方面,ASP.NET MVC 都比 Web Forms 迈进了一大步。它不是试图将非 Web 模型强加于 Web 开发框架之上,而是拥抱 HTTP 模型,并以一种与 Web 实际工作方式更加契合的方式呈现给开发人员。这很解放,但对于只熟悉 Web Forms 模型的开发人员来说,可能有点令人生畏。要有效地使用 MVC,我们需要扎实地理解 HTML 表单的工作原理,这一点 Web Forms 并不真正要求或鼓励(尽管它总是可取的)。在本文中,我将介绍如何在 MVC 中创建一个经典的 UI 结构:通过将项目从一个列表框移动到另一个列表框来指示选择。

先决条件

您至少需要安装了 MVC 2 版本的 Visual Studio 2008(或同等的 Express 版本)。我假设您对 ASP.NET 开发以及 MVC 项目的结构有基本的了解。

入门

为了配合季节气氛,我们将创建一个圣诞礼物选择器。当然,如果您不是在十二月阅读本文,那么这个主题可能不太应景。下面展示了我们圣诞主题的 UI。

ListBoxDemo.png

看看这些雪景!是不是让您感觉很节日气氛?不用说,我已将样式留给读者作为练习。这种双列表框的场景很有趣;它相当常用,在 Web Forms 中很容易实现,但实际上并不太适合 HTML 表单。为了理解原因,让我们深入了解一下列表框是如何工作的;一旦我们理解了这一点,实现我们的用户界面就会容易得多。

上面列表框(稍作简化)的 HTML 源代码如下所示。

<select multiple="multiple" name="AvailableSelected" size="6">
    <option value="1">Games Console</option>
    <option value="2">MP3 player</option>
    <option value="3">Smart Phone</option>
    <option value="4">Digital Photo frame</option>
    <option value="5">E-book reader</option>
    <option value="6">DVD Box Set</option>
</select>

select 元素代表列表本身;选项代表列表中的项目。每个选项都有一个 value 属性,这决定了当选中一个项目时,将在表单数据中发送回的值。假设用户选择了游戏机、数码相框和 DVD 套装。以下数据将被发布回服务器:AvailableSelected=1&AvailableSelected=4&AvailableSelected=6。“AvailableSelected”来自指定给 select 元素的名称;1、4 和 6 指的是所选产品的 ID。请注意,如果未选中某个项目,则不会发送有关该项目的信息。这给我们的双列表场景带来了问题:我们不那么关心哪些项目被选中,而是关心每个列表的内容。因此,我们将需要找到另一种方法来跟踪用户已选择的项目。结果证明,这并不难,但在我们继续之前,让我们花点时间思考一下这在 Web Forms 场景中是如何工作的。

Web Forms ListBox

在使用 Web Forms ListBox 时,列表框中存储的项目在表单发布后对服务器端代码是可用的。对于我们的场景,这将非常有用。我们知道表单数据中没有发送信息,那么这是如何工作的呢?ListBox 服务器控件会自动将其所有内容保存到 Web Form 的 ViewState 隐藏字段中。当表单发布回服务器时,它会从这些数据中重新创建其内容,然后检查表单数据以查看是否选中了其任何项目。无需开发人员的额外工作,状态就会在表单发布之间自动维护;这很棒,对吧?嗯,有好有坏。此方法在您遵循 Web Forms 方法论的范围内效果很好。但如果您试图跳出这个范围,您就会遇到问题。例如,尝试通过 JavaScript 操作列表项,您会发现更改不会反映在服务器代码中。更糟糕的是,除非您调整 ViewState 安全设置,否则您的应用程序可能会开始抛出异常。原本简单的修改变成了巨大的混乱。如果您不了解底层机制,则很难解决问题;我遇到过 ASP.NET 开发人员对他们在 JavaScript 中所做的更改在服务器代码中不可见的原因感到困惑。

Using the Code

现在我们已经了解了一些理论,让我们来看看示例代码的一些关键部分。代表用户可以选择的礼物的 Product 如下所示。

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public Decimal Price { get; set; }
}

我们还将创建一个 View Model 来封装发送到/来自 View 的数据。为了填充两个列表框,我们需要两个产品数据列表:一个用于可用列表,一个用于已请求列表。当用户将项目从一个框移到另一个框时,我们需要获取列表中已选中的项目。回顾我们之前的解释,列表框数据表示为重复的键/值对,列表框的名称作为键,每个选中的 ID 作为值。幸运的是,我们不必手动解码这些数据,ASP.NET MVC 的模型绑定功能可以为我们完成。我们将把数据存储为 int 数组,这些数组将由表单数据填充。我们还需要一个地方来保存表单发布之间的状态;我们只需要记住已请求列表框中的产品 ID。我们将通过将逗号分隔的列表保存在字符串中来实现此目的。

ViewModel 类相关的部分如下所示。

public class ViewModel
{
    public List<Product> AvailableProducts { get; set; }
    public List<Product> RequestedProducts { get; set; }    

    public int[] AvailableSelected { get; set; }
    public int[] RequestedSelected { get; set; }
    public string SavedRequested { get; set; }
}

View 的列表框部分如下所示。

<%using(Html.BeginForm()){ %>
    <div>
        <table>
            <thead>
                <tr>
                    <th>Available</th><th>
                    </th><th>Requested</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td valign="top">
                        <%=Html.ListBoxFor(model => model.AvailableSelected, 
                            new MultiSelectList(Model.AvailableProducts, "Id", 
                            "Name", Model.AvailableSelected), 
                            new { size = "6" })%>
                    </td>
                    <td valign="top">
                        <input type="submit" name="add" 
                          id="add" value=">>" /><br />
                        <input type="submit" name="remove" 
                          id="remove" value="<<" />
                    </td>
                    <td valign="top">
                        <%=Html.ListBoxFor(model => model.RequestedSelected, 
                          new MultiSelectList(Model.RequestedProducts, "Id", 
                          "Name", Model.RequestedSelected))%>
                    </td>
                </tr>
            </tbody>
        </table>
        <br />
        <%=Html.HiddenFor(model=>model.SavedRequested) %>
    </div>
<%} %>

请注意 Html.ListBoxFor 的格式;第一个参数设置要使用的模型属性(数组之一);第二个参数创建一个 MultiSelectList,这是一个类似于 Web Forms 中 ListItem 类的对象集合——每个项目都有一个值、文本和选定属性。构造函数设置要用作列表的集合,以及要用作值和文本字段的属性,以及一个表示应选中的值的 IEnumerable,我们将其中一个数组传递给它。由于它是一个 MultiSelectList,因此可以同时选择和移动多个项目。一个隐藏字段存储用于在表单发布之间存储状态的信息。

接收表单值的控制器代码如下所示。

[HttpPost]
public ActionResult Index(ViewModel model, string add, string remove)
{
    //Need to clear model state or it will interfere with the updated model
    ModelState.Clear();
    RestoreSavedState(model);
    if (!string.IsNullOrEmpty(add))
        AddProducts(model);
    else if (!string.IsNullOrEmpty(remove))
        RemoveProducts(model);
    SaveState(model);
    return View(model);
}

得益于模型绑定的魔力,该方法接受 ViewModel 类型的参数。ASP.NET MVC 将自动解析传入的表单值并匹配它能匹配的所有内容。这意味着数组将具有值(如果选中了任何项目),SavedRequested 字段也将如此,但 ProductList 将为 null

字符串参数代表两个按钮。当按下提交按钮时,它的名称和值(文本)将被包含在表单值中。通过检查哪个字符串值不为空,我们可以确定按下了哪个按钮。RestoreSavedState 方法从 SavedRequested 字段获取保存的产品 ID,并使用它来从上一个往返重建已请求产品的列表。SaveState 方法获取当前状态并将其存储在字符串中,以便保存在隐藏字段中。这样,列表框功能就完成了,项目可以在它们之间自由移动。可下载的示例包含完整的应用程序,包括一些简单的验证以及已请求项目的详细信息列表。

结论

此页面的行为与典型的 Web Forms 页面相似,它会回发到同一页面并完全由服务器驱动。尽管它要求我们在处理表单输入方面付出更多努力,但由于该页面与 Web 的工作方式更加契合,因此在未来构建和增强它将更加容易。在后续文章中,我将展示如何添加一个 JavaScript 层,将大部分功能转移到客户端,同时仍然为没有 JavaScript 的浏览器保持兼容性。

历史

  • 2010 年 12 月 15 日 - 首次上传。
  • 2011 年 1 月 12 日 - 添加了后续文章的链接。
© . All rights reserved.