【问题标题】:ASP.NET MVC 2 Custom editor templates for splitting datetime fieldsASP.NET MVC 2 用于拆分日期时间字段的自定义编辑器模板
【发布时间】:2010-11-13 00:02:27
【问题描述】:

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

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

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

游览视图具有仅时间属性,其中需要单独的小时和分钟作为文本框。

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

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

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

所以我的问题是有其他人设法完成上述任务吗?

(手指交叉!)

【问题讨论】:

  • "但到目前为止,我还没有找到合适的解决方案。"这是什么意思?
  • 您好 jfar,我对 c# 和 mvc 还很陌生,因此一直依靠 google 搜索来找到解决方案的良好起点。主要的是hanselman.com/blog/…,但我不知道如何添加验证(服务器端或客户端)。
  • 哦,好吧,我还以为是别的东西。除非其他人回答,否则我今晚会发布答案。我一直在做这种事情。
  • 期待看到它,我已经在这个上扯了一段时间了!谢谢

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


【解决方案1】:

好的,我会努力让你完成 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%>
<% }

这就是“不显示任何嵌套的复杂类型”,我们不希望这样。将 > 1 更改为 > 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 日期选择器框。在您的情况下,您可以将两个字段放在一个 div 中,以便它们水平排列。在这一点上,天空是极限。

第 3 步:验证:

验证的工作方式与您期望的差不多。您的 [Required] 属性 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 Wilson 的博客之外,几乎没有什么可了解的信息。只要坚持下去,如果你也需要,不要害怕偷看 MVC 2 源代码。

【讨论】:

  • 感谢这个 jfar,它确实给了我一些可以使用的东西,但是我更倾向于将实际的日期时间对象拆分为其组件输入(年、月和日的单独输入示例)而不是多个完整的日期时间实例。你认为这仍然是正确的方法吗?作为此解决方案的一部分,您将如何添加客户端验证?
  • 好吧。您只需制作自己的 DateTime 模板,该模板将拆分 UI。就客户端验证而言,只需在您的自定义 DateTime.ascx 模板中包含 javascript。
  • 好的,我让它与我所追求的组合之一一起工作(月和年仅在两个单独的输入中)所以非常感谢您的帮助,我会将其标记为已回答。我现在想知道的一件事是我拥有它,以便自定义验证属性采用 CardDate 对象(int 月和 int 年)并尝试形成有效日期。如果失败,它会正确返回 false,说明 carddate 无效。一件事,是否可以从属性中使 cardtype 对象中的两个字段都无效,以便它们在视图上显示为无效状态?希望这是有道理的!
  • 嗯,我还有一个问题。如果我不将 require 属性应用于 cardtype 属性,那么将它们留空不应引发错误,但 customattribute 不知道这一点并引发错误。您知道如何从自定义属性中访问数据注释,以便在引发错误之前进行检查吗? (对不起所有的问题!)
【解决方案2】:

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

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

在你看来

<%= 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 将其添加到下拉列表中。

【讨论】:

  • 嗨 chobo2,感谢您的输入,但这并不是真正朝着正确的方向发展。我正在寻找一种方法来绑定到模型上的 datetime 属性并仍然进行验证等。上面评论中的链接应该可以更好地了解我想要完成的工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
  • 2012-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-08
相关资源
最近更新 更多