一个shape是一个动态数据模型,shape的目的是用来代替ASP.NET MVC中静态的视图模型,Orchard使用的模型可以在运行时更新。本文将介绍shape的概念以及如何使用它。如果你还不知道module的基本概念和开发可以看我之前写的文章,这里就不再重复,如果不知道.Net的动态对象的可以看看Creating and Using Dynamic Objects。
介绍Shapes
Shapes是一个动态数据模型,它使用shape templates来展现UI,Shape templates是一个标记片段。Shapes的示例包括menus, menu items, content items, documents, and messages。Shape是一个派生自Orchard.DisplayManagement.Shapes.Shape 类的数据模型对象。Shape 类不会实例化,而是在运行时通过shape工厂来生成。缺省的shape工厂是Orchard.DisplayManagement.Implementation.DefaultShapeFactory。shape工厂生成的shape是一个动态对象。
注意: Dynamic objects是.NET 4.0才有的功能。作为一个动态对象,shape在运行时暴露它的成员,而不是在编译期。而在ASP.NET MVC中模型对象是一个在编译器定义的静态对象。
Shape信息由shape的ShapeMetadata属性来描述,这些信息包括shape的type、display type、position、prefix、wrappers、alternates、child content和一个WasExecuted 布尔值。我们可以通过以下代码来获取shape的元信息:
当shape对象生成之后,我们如何展现它呢?shape template是一个HEML代码片段,它负责显示shape。我们还可以不使用shape template而直接通过使用shape attribute (Orchard.DisplayManagement.ShapeAttribute) 来写代码显示shape。
Creating Shapes
对于模块开发人员来说,需要知道我们是在driver中把数据转化成模板来显示的。Driver派生自Orchard.ContentManagement.Drivers.ContentPartDriver,一般都会重载类的Display 和Editor 方法。Display 和Editor 方法返回一个ContentShapeResult对象,这个对象与ASP.NET MVC中的ActionResult类似。
ContentShape 方法生成一个shape并且把它返回在一个ContentShapeResult 对象中。ContentShape是overloaded的方法,最常用的是传入两个参数(shape 类型和定义shape的动态函数表达式)。动态函数表达式定义这个shape,shape类型命名这个shape并且绑定这个shape到展现模板上。(Shape类型命名规约在本文后面会介绍)
以下是一个driver的Display方法示例,这个方法用来显示一个Map part:
protected override DriverResult Display(
MapPart part, string displayType, dynamic shapeHelper)
{
return ContentShape( " Parts_Map " ,
() => shapeHelper.Parts_Map(
Longitude: part.Longitude,
Latitude: part.Latitude));
}
这个表达式使用一个动态对象shapeHelper来定义一个Parts_Map shape 和它的属性。表达式在shape上添加了一个Longitude 属性,并且与part的Longitude 属性绑定起来;同样增加了一个Latitude 属性。Display返回ContentShape方法生成的对象。
下面的示例演示了一个关于Map part的完整的driver 类,Display 方法用来显示地图,Editor 方法标记了"GET",用来编辑地图信息,标记了"POST"的Editor方法用来重新显示用户提供值的编辑视图。
using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
namespace Maps.Drivers
{
public class MapDriver : ContentPartDriver < MapPart >
{
protected override DriverResult Display(
MapPart part, string displayType, dynamic shapeHelper)
{
return ContentShape( " Parts_Map " , () => shapeHelper.Parts_Map(
Longitude: part.Longitude,
Latitude: part.Latitude));
}
// GET
protected override DriverResult Editor(
MapPart part, dynamic shapeHelper)
{
return ContentShape( " Parts_Map_Edit " ,
() => shapeHelper.EditorTemplate(
TemplateName: " Parts/Map " ,
Model: part,
Prefix: Prefix));
}
// POST
protected override DriverResult Editor(
MapPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null , null );
return Editor(part, shapeHelper);
}
}
}
标记"GET"的Editor方法使用ContentShape方法来生成一个编辑模板代表的shape对象。在这个例子当中,类型名是Parts_Map_Edit,shapeHelper 对象生成一个EditorTemplate shape。这是一个有一个TemplateName 属性和Model 属性的shape。TemplateName 属性表示模板的部分路径,"Parts/Map" 会告诉Orchard去"Views/EditorTemplates/Parts/Map.cshtml"中查找模板。
这里的Model 属性代表part的不带文件扩展名的模型文件名。
Shapes and Templates命名约定
命名约定是很多架构现在采用的一种方式,它可以减少一些复杂性,只要保证遵守一定的约定即可。假使我们要生成一个显示地图的Map part,它又精度和纬度两个属性。对shape type的命名可能就是Parts_Map。如果shape命名为Parts_Map,那么Orchard 会在views/parts/Map.cshtml中查找模板
下表汇总了shape type和模板的命名约定:
应用到 |
Template命名约定 |
Template示例 |
Shape Type 示例 |
Zone |
Zone-(zone name) |
Zone-AsideSecond.cshtml |
Zone__AsideSecond |
Menu |
Menu-(menu name) |
Menu-main.cshtml |
Menu__main |
MenuItem |
MenuItem-(menu name) |
MenuItem-main.cshtml |
MenuItem__main |
Content |
Content-(content type) |
Content-BlogPost.cshtml |
Content__BlogPost |
Content |
Content-(content id) |
Content-42.cshtml |
Content__42 |
Content |
Content.(display type) |
Content.Summary.cshtml |
Content_Summary__Page |
Content |
Content-(content type).(display type) |
Content-Page.Summary.cshtml |
Content_Summary__Page |
Content.Edit |
Content-(content type).Edit |
Content-Page.Edit.cshtml |
Content_Edit__Page |
Widget |
Widget-(content type) |
Widget-HtmlWidget.cshtml |
Widget__HtmlWidget |
Widget |
Widget-(zone name) |
Widget-AsideSecond.cshtml |
Widget__AsideSecond |
Fields/Common.Text |
Fields/Common.Text-(text field name) |
Fields/Common.Text-Location.cshtml |
Fields_Common_Text__Location |
模板在项目中的位置应该遵守以下规则:
- Content item shape 模板放在views/items 目录
- Parts_ shape 模板放在views/parts 目录
- Fields_ shape 模板放在views/fields 目录下
- EditorTemplate shape 模板放在views/EditorTemplates/template name 目录。例如,模板名是"Parts/Routable.RoutePart" 的EditorTemplate放在views/EditorTemplates/Parts/Routable.RoutePart.cshtml
- 其他shape模板放在views 目录下
注意:模板扩展名可以是任意支持的视图引擎支持的扩展名,例如.cshtml, .vbhtml, .ascx.
模板文件名到shape名的映射
以下规则表示从一个模板文件名到对应shape名的映射规则:
- (.) 和 (\) 变成 (_):例如在foo目录下的bar.cshtml 对应的是foo_bar
- (-) 变成两个下划线 (__):例如Views/Hello.World.cshtml对应的shape名称为Hello_World,Views/Hello.World-85.cshtml 对应的是Hello_World__85
使用模板显示Shapes
Shape template是显示shape的一个代码片段,在Orchard中缺省的视图引擎是Razor,在后面文章中我会单独介绍Razor语法。下面示例把一个Map part显示为图片:
< img alt = " Location " border = " 1 " src = " http://maps.google.com/maps/api/staticmap?
& zoom = 14
& size = 256x256
& maptype = satellite & markers = color:blue | @Model.Latitude,@Model.Longitude
& sensor = false " />
这个示例显示一个img 元素,并设定好带参数的src 属性,在query字符串中, @Model代表传入到模板中的shape。因此, @Model.Latitude 代表shape的Latitude 属性, @Model.Longitude 代表shape的Longitude属性。
下面我们再显示一个编辑模板,这个模板可以输入经度和纬度值:
@model Maps.Models.MapPart
< fieldset >
< legend > Map Fields legend >
< div class = " editor-label " >
@Html.LabelFor(model => model.Latitude)
div >
< div class = " editor-field " >
@Html.TextBoxFor(model => model.Latitude)
@Html.ValidationMessageFor(model => model.Latitude)
div >
< div class = " editor-label " >
@Html.LabelFor(model => model.Longitude)
div >
< div class = " editor-field " >
@Html.TextBoxFor(model => model.Longitude)
@Html.ValidationMessageFor(model => model.Longitude)
div >
fieldset >
@Html.LabelFor 表达式生成一个shape属性的label, @Html.TextBoxFor 生成输入shape属性的文本框, @Html.ValidationMessageFor 生成非法提示信息框。
生成一个shape的方法
另一种生成并展现shape的方法时生成一个同时定义和展现shape的方法。这个方法必须标记Shape 属性(Orchard.DisplayManagement.ShapeAttribute)。这个方法不适用模板,而是直接返回一个IHtmlString 对象,这个对象包括展现shape的标记。
下面示例是DateTimeRelative shape,这个shape 输入之前的日期,返回与现在日期的差隔时间:
public class DateTimeShapes : IDependency
{
private readonly IClock _clock;
public DateTimeShapes(IClock clock)
{
_clock = clock;
T = NullLocalizer.Instance;
}
public Localizer T { get ; set ; }
[Shape]
public IHtmlString DateTimeRelative(HtmlHelper Html, DateTime dateTimeUtc)
{
var time = _clock.UtcNow - dateTimeUtc;
if (time.TotalDays > 7 )
return Html.DateTime(dateTimeUtc, T( " 'on' MMM d yyyy 'at' h:mm tt " ));
if (time.TotalHours > 24 )
return T.Plural( " 1 day ago " , " {0} days ago " , time.Days);
if (time.TotalMinutes > 60 )
return T.Plural( " 1 hour ago " , " {0} hours ago " , time.Hours);
if (time.TotalSeconds > 60 )
return T.Plural( " 1 minute ago " , " {0} minutes ago " , time.Minutes);
if (time.TotalSeconds > 10 )
return T.Plural( " 1 second ago " , " {0} seconds ago " , time.Seconds);
return T( " a moment ago " );
}
}
参考:Accessing and rendering shapes
推荐:你可能需要的在线电子书
我的新浪围脖: http://t.sina.com.cn/openexpressapp 敏捷个人sina围裙:http://q.t.sina.com.cn/135484
欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]
var shapeType = shapeName.Metadata.Type;