开发字段

你能在下面的地址,找到该例子的代码:

http://orcharddatetimefield.codeplex.com/

最终效果

我们要创建一个日期和时间选择字段,最终实现下面的效果:

创建模块

执行下面的命令,创建模块:

codegen module CustomFields /IncludeInSolution:true

更改Module.txt文件:

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

我们发现,在这个module.txt文件中,我们定义了两个特性,这是因为,在这个模块中,我们提供了2种字段。

字段模型

在模块的根目录中,我们创建Fields文件夹,在Fields文件夹中,创建DateTimeField.cs文件:

using System;

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));

}

}

}

}

我们定义的字段,继承自ContentField,他为我们提供了一些功能,比如保存字段数据,字段数据最终会保存为字符串类型,实际需要的类型和字符串类型的转换,是自动完成的。

创建视图模型

在根目录中,创建ViewModels  文件夹,在ViewModels  文件夹中,创建DateTimeFieldViewModel.cs  文件:

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; }

}

}

创建字段配置

为了字段能够灵活适应不同的场景,我们需要给字段添加配置,这样,后台管理内容类型的时候,可以对字段进行配置,以便按照我们需要的样子显示。

在根目录下面,创建Settings文件夹,在Settings文件夹中,创建DateTimeFieldSettings.cs 文件:

namespace CustomFields.DateTimeField.Settings {

 

public enum DateTimeFieldDisplays {

DateAndTime,

DateOnly,

TimeOnly

}

 

public class DateTimeFieldSettings {

public DateTimeFieldDisplays Display { get; set; }

}

}

我们创建了一个DateTimeFieldSettings类,他只有一个Display属性,是个枚举类型。

创建 Driver

和元件一样,一个字段也有一个Driver,用于显示和编辑内容。

Drivers文件夹下,创建 DateTimeFieldDriver.cs文件:

using System;

using JetBrains.Annotations;

using Orchard;

using Orchard.ContentManagement;

using Orchard.ContentManagement.Drivers;

using CustomFields.DateTimeField.Settings;

using CustomFields.DateTimeField.ViewModels;

using Orchard.ContentManagement.Handlers;

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

field.Name, // used to differentiate shapes in placement.info overrides, e.g. Fields_Common_Text-DIFFERENTIATOR

// 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);

}

 

protected override void Importing(ContentPart part, Fields.DateTimeField field,

ImportContentContext context) {

 

var importedText = context.Attribute(GetPrefix(field, part), "DateTime");

if (importedText != null) {

field.Storage.Set(null, importedText);

}

}

 

protected override void Exporting(ContentPart part, Fields.DateTimeField field,

ExportContentContext context) {

context.Element(GetPrefix(field, part))

.SetAttributeValue("DateTime", field.Storage.Get<string>(null));

}

}

}

这个Driver继承自ContentFieldDriver<DateTimeField> 。

我们通过依赖注入实例化了T对象,这样,我们通过调用T()方法,可以本地化代码中的字符串。

我们有两个行为, Display 和 Editor,用来创建显示和渲染时候的Shape。

注意: UsedImplicitly 特性只有当你安装了Resharper插件的时候才有用,可以去掉。

shapeHelper 提供了创建Shape的方法。

第二个 Editor 方法会在用户提交编辑页面的时候触发。他将提交的数据保存到字段,然后调用第一个 Editor 返回编辑页面。

创建模板

我们需要创建模板,来决定如何显示字段。

Views文件夹中,创建FieldsEditorTemplates/Fields 文件夹,在两个文件夹中都创建Custom.DateTime.cshtml视图。

Fields/Custom.DateTime.cshtml:

<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>

这个模板显示了字段的名称,根据配置,显示日期或者时间。

 

Views/EditorTemplates/Fields :

@model CustomFields.DateTimeField.ViewModels.DateTimeFieldViewModel

 

@{

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>

}

这个模板注册了一些脚本和样式。然后通过脚本,实现了日期时间的编辑。

 

为了字段的显示,我们需要在根目录创建placement.info文件,决定字段渲染时的位置:

<Placement>

<Place Fields_Custom_DateTime_Edit="Content:2.5"/>

<Place Fields_Custom_DateTime="Content:2.5"/>

</Placement>

管理字段设置

在根目录的Settings文件夹中,创建 DateTimeFieldEditorEvents.cs 文件:

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 (builder.FieldType != "DateTimeField") {

yield break;

}

 

if (updateModel.TryUpdateModel(

model, "DateTimeFieldSettings", null, null)) {

builder.WithSetting("DateTimeFieldSettings.Display",

model.Display.ToString());

}

 

yield return DefinitionTemplate(model);

}

}

}

这个和Driver的Editor方法很相似。第一个方法获取配置,渲染页面,第二个方法获取提交的数据,更改配置,然后调用第一个方法。

 

Views->DefinitionTemplates文件夹下面,创建 DateTimeFieldSettings.cshtml 文件:

@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>

这个模板创建了一个下拉框,用于用户选择显示方式。

样式

在 Styles 文件夹创建一个datetime.css文件:

html.dyn label.forpicker {

display:none;

}

 

html.dyn input.hinted {

color:#ccc;

font-style:italic;

}

.date input{

width:10em;

}

.time input {

width:6em;

}

使用字段

首先,我们需要启用特性,然后再后台的内容类型管理页面,我们就可以选中一个内容类型,比如"Page",在Page的编辑页面,我们点击"Fields"旁边的Add,选择我们开发的DateTime字段:

 

我们需要显示时间和日期,所以,在字段配置的地方,选择"Date and time"。

在创建内容项的地方,我们可以看到字段的效果了:

 

字段的显示效果如下:

posted @ 2015-09-10 14:56  争世不悔  阅读(230)  评论(0编辑  收藏  举报