Orchard:生成一个自定义字段类型
在Orchard中定义新的content types时可以使用字段,现在Orchard只内置了一个文本类型字段,在实际应用中,我们肯定会遇到添加自己的字段类型,然后在新的content types上使用。这篇文章将教给大家的是在http://orcharddatetimefield.codeplex.com中的自定义日期时间数据类型。如果对如何生成一个模块不清楚,可以先看看本系列的前几篇文章。
目标
这个练习做完之后,我们将新增一个新的字段类型,使用界面如下图所示:有一个日期和时间编辑器
| 
 
  | 
生成一个模块
现在在Modules目录下有一个新的目录:CustomFields,更改module.txt:
View Code 
Name: CustomFields
AntiForgery: enabled
Author: Me
Website: http://orcharddatetimefield.codeplex.com
Version: 0.6.1
OrchardVersion: 0.8.0
Description: A bunch of custom fields for use in your custom content types.
Features:
CustomFields:
Description: Custom fields for Orchard.
Category: Fields
DateTimeField:
Description: A date and time field with a friendly UI.
Category: Fields
Dependencies: CustomFields, Orchard.jQuery, Common, Settings
这里我们定义了两个features,因为这个模块最后包括多个字段,我们想要区分这个模块的默认功能和日期字段功能,这个也正好可以演示一下分类和依赖的使用。
生成字段
在CustomFields目录下生成目录Fields,添加文件DateTimeField.cs:
View Code 
using System.Globalization;
using Orchard.ContentManagement;
using Orchard.ContentManagement.FieldStorage;
using Orchard.Environment.Extensions;
namespace CustomFields.DateTimeField.Fields
{
    [OrchardFeature("DateTimeField")]
    public class DateTimeField : ContentField
    {
        public DateTime? DateTime
        {
            get
            {
                var value = Storage.Get<string>();
                DateTime parsedDateTime;
                if (System.DateTime.TryParse(value, CultureInfo.InvariantCulture,
                    DateTimeStyles.AdjustToUniversal, out parsedDateTime))
                {
                    return parsedDateTime;
                }
                return null;
            }
            set
            {
                Storage.Set(value == null ?
                    String.Empty :
                    value.Value.ToString(CultureInfo.InvariantCulture));
            }
        }
    }
}
这个自定义字段DateTimeField是从ContentField继承下来的,ContentField提供一些字段值存储等服务。这个字段将存储为strings。
生成一个view model
生成一个或多个view models虽然不是强制的,但是一个好的实践。View model将被用来封装model用于展现而用的模型。在一个新的ViewModels目录下添加文件DateTimeFieldViewModel.cs:
View Code 
namespace CustomFields.DateTimeField.ViewModels
{
public class DateTimeFieldViewModel
{
public string Name { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public bool ShowDate { get; set; }
public bool ShowTime { get; set; }
}
}
生成字段设置
Create a Settings folder and add the following DateTimeFieldSettings.cs file to it:
View Code 
namespace CustomFields.DateTimeField.Settings
{
public enum DateTimeFieldDisplays
{
DateAndTime,
DateOnly,
TimeOnly
}
public class DateTimeFieldSettings
{
public DateTimeFieldDisplays Display { get; set; }
}
}
写一个driver
在Drivers目录下添加文件DateTimeFieldDriver.cs:
View Code 
using CustomFields.DateTimeField.ViewModels;
using JetBrains.Annotations;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using CustomFields.DateTimeField.Settings;
using Orchard.Localization;
namespace CustomFields.DateTimeField.Drivers
{
    [UsedImplicitly]
    public class DateTimeFieldDriver : ContentFieldDriver<Fields.DateTimeField>
    {
        public IOrchardServices Services { get; set; }
        // EditorTemplates/Fields/Custom.DateTime.cshtml
        private const string TemplateName = "Fields/Custom.DateTime";
        public DateTimeFieldDriver(IOrchardServices services)
        {
            Services = services;
            T = NullLocalizer.Instance;
        }
        public Localizer T { get; set; }
        private static string GetPrefix(ContentField field, ContentPart part)
        {
            // handles spaces in field names
            return (part.PartDefinition.Name + "." + field.Name)
                   .Replace(" ", "_");
        }
        protected override DriverResult Display(
            ContentPart part, Fields.DateTimeField field,
            string displayType, dynamic shapeHelper)
        {
            var settings = field.PartFieldDefinition.Settings
                                .GetModel<DateTimeFieldSettings>();
            var value = field.DateTime;
            return ContentShape("Fields_Custom_DateTime", // key in Shape Table
                // this is the actual Shape which will be resolved
                // (Fields/Custom.DateTime.cshtml)
                    s =>
                    s.Name(field.Name)
                     .Date(value.HasValue ?
                         value.Value.ToLocalTime().ToShortDateString() :
                         String.Empty)
                     .Time(value.HasValue ?
                         value.Value.ToLocalTime().ToShortTimeString() :
                         String.Empty)
                     .ShowDate(
                         settings.Display == DateTimeFieldDisplays.DateAndTime ||
                         settings.Display == DateTimeFieldDisplays.DateOnly)
                     .ShowTime(
                         settings.Display == DateTimeFieldDisplays.DateAndTime ||
                         settings.Display == DateTimeFieldDisplays.TimeOnly)
                );
        }
        protected override DriverResult Editor(ContentPart part,
                                               Fields.DateTimeField field,
                                               dynamic shapeHelper)
        {
            var settings = field.PartFieldDefinition.Settings
                                .GetModel<DateTimeFieldSettings>();
            var value = field.DateTime;
            if (value.HasValue)
            {
                value = value.Value.ToLocalTime();
            }
            var viewModel = new DateTimeFieldViewModel
            {
                Name = field.Name,
                Date = value.HasValue ?
                       value.Value.ToLocalTime().ToShortDateString() : "",
                Time = value.HasValue ?
                       value.Value.ToLocalTime().ToShortTimeString() : "",
                ShowDate =
                    settings.Display == DateTimeFieldDisplays.DateAndTime ||
                    settings.Display == DateTimeFieldDisplays.DateOnly,
                ShowTime =
                    settings.Display == DateTimeFieldDisplays.DateAndTime ||
                    settings.Display == DateTimeFieldDisplays.TimeOnly
            };
            return ContentShape("Fields_Custom_DateTime_Edit",
                () => shapeHelper.EditorTemplate(
                          TemplateName: TemplateName,
                          Model: viewModel,
                          Prefix: GetPrefix(field, part)));
        }
        protected override DriverResult Editor(ContentPart part,
                                               Fields.DateTimeField field,
                                               IUpdateModel updater,
                                               dynamic shapeHelper)
        {
            var viewModel = new DateTimeFieldViewModel();
            if (updater.TryUpdateModel(viewModel,
                                       GetPrefix(field, part), null, null))
            {
                DateTime value;
                var settings = field.PartFieldDefinition.Settings
                                    .GetModel<DateTimeFieldSettings>();
                if (settings.Display == DateTimeFieldDisplays.DateOnly)
                {
                    viewModel.Time = DateTime.Now.ToShortTimeString();
                }
                if (settings.Display == DateTimeFieldDisplays.TimeOnly)
                {
                    viewModel.Date = DateTime.Now.ToShortDateString();
                }
                if (DateTime.TryParse(
                        viewModel.Date + " " + viewModel.Time, out value))
                {
                    field.DateTime = value.ToUniversalTime();
                }
                else
                {
                    updater.AddModelError(GetPrefix(field, part),
                                          T("{0} is an invalid date and time",
                                          field.Name));
                    field.DateTime = null;
                }
            }
            return Editor(part, field, shapeHelper);
        }
    }
}
一开始通过依赖注入获取localizer 依赖对象,以便我们能够通过代码获取本地化字符。静态的方法GetPrefix是一个用来定义字段类型示例在数据库中唯一列名的前缀。
Display和Editor获取字段的设置和值来构建shapes。
写模板
在Views目录下生成一个Fields和EditorTemplates目录,然后在EditorTemplates下生成一个Fields目录。
在Views/Fields下新增文件Custom.DateTime.cshtml:
View Code 
<p class="text-field"><span class="name">@Model.Name:</span>
@if(Model.ShowDate) { <text>@Model.Date</text> }
@if(Model.ShowTime) { <text>@Model.Time</text> }
</p>
View Code 
@{
Style.Include("datetime.css");
Style.Require("jQueryUI_DatePicker");
Style.Require("jQueryUtils_TimePicker");
Style.Require("jQueryUI_Orchard");
Script.Require("jQuery");
Script.Require("jQueryUtils");
Script.Require("jQueryUI_Core");
Script.Require("jQueryUI_Widget");
Script.Require("jQueryUI_DatePicker");
Script.Require("jQueryUtils_TimePicker");
}
<fieldset>
  <label for="@Html.FieldIdFor(m => Model.Date)">@Model.Name</label>
  @if ( Model.ShowDate ) {
  <label class="forpicker"
    for="@Html.FieldIdFor(m => Model.Date)">@T("Date")</label>
  <span class="date">@Html.EditorFor(m => m.Date)</span>
  }
  @if ( Model.ShowTime ) {
  <label class="forpicker"
    for="@Html.FieldIdFor(m => Model.Time)">@T("Time")</label>
  <span class="time">@Html.EditorFor(m => m.Time)</span>
  }
  @if(Model.ShowDate) { <text>@Html.ValidationMessageFor(m=>m.Date)</text> }
  @if(Model.ShowTime) { <text>@Html.ValidationMessageFor(m=>m.Time)</text> }
</fieldset>
@using(Script.Foot()) {
<script type="text/javascript">
    $(function () {
        $("#@Html.FieldIdFor(m => Model.Date)").datepicker();
        $("#@Html.FieldIdFor(m => Model.Time)").timepickr();
    });
</script>
}
View Code 
<Placement>
  <Place Fields_Custom_DateTime_Edit="Content:2.5"/>
  <Place Fields_Custom_DateTime="Content:2.5"/>
</Placement>
管理字段设置
		
View Code 
<Placement>
<Place Fields_Custom_DateTime_Edit="Content:2.5"/>
<Place Fields_Custom_DateTime="Content:2.5"/>
</Placement>
到现在还没有完全做完,我们还需要关心如何管理和持久化字段的设置,在Settings目录下增加文件DateTimeFieldEditorEvents.cs:
View Code 
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.ViewModels;
namespace CustomFields.DateTimeField.Settings
{
public class DateTimeFieldEditorEvents : ContentDefinitionEditorEventsBase
{
public override IEnumerable<TemplateViewModel>
PartFieldEditor(ContentPartFieldDefinition definition)
{
if (definition.FieldDefinition.Name == "DateTimeField")
{
var model = definition.Settings.GetModel<DateTimeFieldSettings>();
yield return DefinitionTemplate(model);
}
}
public override IEnumerable<TemplateViewModel> PartFieldEditorUpdate(
ContentPartFieldDefinitionBuilder builder, IUpdateModel updateModel)
{
var model = new DateTimeFieldSettings();
if (updateModel.TryUpdateModel(
model, "DateTimeFieldSettings", null, null))
{
builder.WithSetting("DateTimeFieldSettings.Display",
model.Display.ToString());
}
yield return DefinitionTemplate(model);
}
}
}
生成目录Views\DefinitionTemplates,添加字段的编辑摸板文件DateTimeFieldSettings.cshtml:
View Code 
@model CustomFields.DateTimeField.Settings.DateTimeFieldSettings
@using CustomFields.DateTimeField.Settings;
<fieldset>
<label for="@Html.FieldIdFor(m => m.Display)"
class="forcheckbox">@T("Display options")</label>
<select id="@Html.FieldIdFor(m => m.Display)"
name="@Html.FieldNameFor(m => m.Display)">
@Html.SelectOption(DateTimeFieldDisplays.DateAndTime,
Model.Display == DateTimeFieldDisplays.DateAndTime,
T("Date and time").ToString())
@Html.SelectOption(DateTimeFieldDisplays.DateOnly,
Model.Display == DateTimeFieldDisplays.DateOnly,
T("Date only").ToString())
@Html.SelectOption(DateTimeFieldDisplays.TimeOnly,
Model.Display == DateTimeFieldDisplays.TimeOnly,
T("Time only").ToString())
</select>
@Html.ValidationMessageFor(m => m.Display)
</fieldset>
更新项目文件
打开项目文件CustomFields.csproj:
View Code 
<Compile Include="Drivers\DateTimeFieldDriver.cs" />
<Compile Include="Fields\DateTimeField.cs" />
<Compile Include="Settings\DateTimeFieldEditorEvents.cs" />
<Compile Include="Settings\DateTimeFieldSettings.cs" />
<Compile Include="ViewModels\DateTimeFieldViewModel.cs" />
添加样式
Create a Styles directory and create the following datetime.css:
View Code 
html.dyn label.forpicker {
display:none;
}
html.dyn input.hinted {
color:#ccc;
font-style:italic;
}
.date input{
width:10em;
}
.time input {
width:6em;
}
使用字段
为了能够使用心得字段,首先必须打开Orchard.ContentTypes功能,还需要打开DateTimeField 功能。

	

	
参考:Writing a content field http://orcharddatetimefield.codeplex.com/
推荐:你可能需要的在线电子书
 
		
我的新浪围脖: http://t.sina.com.cn/openexpressapp
欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]

