ASP.NET MVC 2用于拆分日期时间字段的自定义编辑器模板

时间:2010-01-25 17:45:05

标签: asp.net-mvc asp.net-mvc-2

在我正在构建的网站中,我需要根据属性将日期时间属性拆分为不同的组合。例子:

会员视图具有出生日期属性,需要在视图中显示为单独的日/月/年下拉列表。

信用卡视图有一个到期日属性,需要显示为单独的月/年下拉列表。

游览视图只有一个时间属性,需要单独的小时和分钟作为文本框。

这些方案中的每一个都需要验证,理想情况下还需要客户端验证。

我已经查看了各种选项,例如自定义绑定,自定义属性,现在正在查看自定义编辑器模板,但到目前为止,我找不到合适的解决方案。

这似乎是一项常见的任务,但搜索网络显示的内容很少,涵盖了所有内容(尤其是验证元素)。

所以我的问题是还有其他人设法完成上述工作吗?

(手指交叉!)

2 个答案:

答案 0 :(得分:13)

好的,我会尝试让你在90%的路上。这实际上是MVC 2的一个巨大而复杂的部分,几乎不可能在这个答案框中回答。

首先,您应该访问Brad Wilsons博客并深入了解如何自定义默认的MVC 2模板。这应该让你更清楚地理解所有运动部件。

http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

现在我将开始一个简单的例子,说明如何创建一个人为的约会视图模型,我们希望确保所提供的值不会及时返回。现在不要注意属性,我们会到达那里。

这是我正在使用的ViewModel:

public class AppointmentViewModel
{
    [Required]
    public string Name { get; set; }

    [CantGoBackwardsInTime]
    public DateRange DateRange { get; set; }
}

public class DateRange
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    [Required]
    public int Price { get; set; }
}

我已将此添加到默认的HomeController(没什么特别的):

   public ActionResult Appointment()
    {
        return View(new AppointmentViewModel());
    }

    [HttpPost]
    public ActionResult Appointment(AppointmentViewModel appointment)
    {
        return View(appointment);
    }

这是我的观点:

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Appointment
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Add Appointment</h2>
     <%= Html.ValidationSummary() %>
    <% using( Html.BeginForm()) { %>
    <%= Html.EditorForModel() %>
    <input type="submit" value="Save Changes" />
    <%} %>
</asp:Content>

第1步:设置舞台

您要做的第一件事是从博客条目中获取“默认模板”。在这种情况下,重要的是位于/Views/Shared/EditorTemplates/Object.asxc中的那个.Object.ascx是整个操作的基石。所有Html.Editor *****方法最终都会调用它。

现在,我们必须更改的第一个默认功能是Object.ascx

中的这一行
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
    <%= ViewData.ModelMetadata.SimpleDisplayText%>
<% }

这就是说“不显示任何嵌套的复杂类型”,我们不希望如此。改变那个&gt; 1至a> 2.现在,在对象图中查看模型将为其创建模板,而不是仅创建占位符文本。

暂时保留其他所有内容。

*第2步:覆盖模板**

如果您阅读博客条目,希望您现在可以理解Editor ***和Display方法将如何自动调用View / Shared / EditorTemplates和DisplayTemplates中的模板。将它们视为调用Html.RenderPartial(“TYPENAME”,MyType)它们不是,但它在概念上足够接近。

因此,如果您运行此解决方案并转到正确的URL,您会注意到MVC 2将两次调用Object.ascx,一次用于AppointmentViewModel,再次用于属性DateRange。开箱即用只是呈现相同的表单字段集合。

让我们说我们想让我们的模板在我们的DateRange编辑器中用红色边框框。我们想要做的是使用短循环MVC 2来调用自定义DateTime.ascx模板而不是Object.ascx,这就像在View / Shared / EditorTemplates / DateRange.ascx中添加我们自己的模板一样简单。在这种情况下,我刚刚使用Object.ascx生成的内容使用我们的DateRange模型并将代码粘贴到新的DateRange.ascx中:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<div style="border: 1px solid #900">
    <div class="editor-label"><label for="DateRange">DateRange</label></div>            
        <div class="editor-field">            
            <div class="editor-label"><label for="DateRange_Start">Start</label>
        </div>


        <div class="editor-field">
            <input class="text-box single-line" id="DateRange_Start" name="DateRange.Start" type="text" value="" />            
        </div>

        <div class="editor-label"><label for="DateRange_End">End</label></div>

        <div class="editor-field">
            <input class="text-box single-line" id="DateRange_End" name="DateRange.End" type="text" value="" />            
        </div>

        <div class="editor-label"><label for="DateRange_Price">Price</label></div>

        <div class="editor-field">
            <input class="text-box single-line" id="DateRange_Price" name="DateRange.Price" type="text" value="" />

        </div>       
    </div>
</div>

瓦剌!

现在,当您运行解决方案时,您应该在DateRange周围看到一个红色框。其余的自定义由您决定!你可以添加jQuery datepicker框。在您的情况下,您可以将两个字段放在一个div中,以便它们按照水平排列。在这一点上,天空是极限。

第3步:验证:

验证的工作方式与您期望的方式非常相似。你的内部有[必需]属性 DateRange类型与任何其他验证属性的工作方式相同。

现在你看到我做了一个不能倒退的时间属性,我把它放在了AppointmentViewModel的DateRange属性上。要创建这些特定于类型的验证属性,您只需继承并实现基本ValidationAttribute:

public class CantGoBackwardsInTime : ValidationAttribute
{
    public override string FormatErrorMessage(string name)
    {
        return "Your date range can't go backwards in time";
        //return base.FormatErrorMessage(name);
    }

    public override bool IsValid(object value)
    {
        if (!(value is DateRange))
            throw new InvalidOperationException("This attributes can only be used on DateRange types!");

        var dateRange = value as DateRange;

        return dateRange.End > dateRange.Start;
    }
}

现在,如果您添加此属性并装饰您的属性,您应该会看到自定义CantGoBackwardsInTime验证属性中提供的错误消息。

如果您遇到任何问题,我会更新并清理更多内容,但这应该可以让您开始使用。 (以为我可以在睡觉之前把它搞清楚)只是警告:MVC 2的新编辑是世界上最棒的东西,并且具有提供MVC 2超级RAD功能的巨大潜力;但除了Brad Wilsons博客之外,几乎没有人知道这些信息。只要坚持下去,如果你需要,也不要害怕窥探MVC 2源代码。

答案 1 :(得分:0)

您是否尝试在一个页面上执行此操作?还是一种方法?我不太确定你想怎么做,所以如果我理解你的话,这就是我的镜头。

因此,例如,你可以为生日期间做这样的事情。

在你看来

<%= Html.DropDownList("Birthday")%>
<%= Html.DropDownList("BirthMonth")%>
<%= Html.DropDownList("BirthYear")%>

在你的控制器某处有类似的东西。也许你可以将它们组合成一个循环或类似的东西。

List<int> month = new List<int>()
for(int i = 0; i < 12, i++)
{
    month.add(i + 1);
}

ViewData["BirthMonth"] = new SelectList(month);


int days = DateTime.DaysInMonth(Year, Month);

    // do a another for loop add it to viewsData =  ViewData["Birthday"] .
    // do a another for loop for years and add it do another viewdata = ViewData["BirthYear"].

所以我得到的是你可以在服务器上做一些事情来获取你想要的日期,然后通过ViewData将它添加到下拉列表中。