首先Orchard是一个建立在ASP.NET MVC框架上的CMS应用框架。Orchard在呈现内容的时候也遵循MVC的规律,也是通过Controller来处理Url请求并决定用那个View来呈现那种Model。不过这个Model就比较有讲究了,因为在Orchard中,一个页面上呈现的数据可能是多种多样的,有文章、有评论,有博客等等。而且这些数据都是可以通过后台设置任意组合的,也就是说我们不可能为每一个页面都创建一个类型的Model。那么在Orchard中是如何解决这一问题的呢?Orchard引入了形状(Shape)的概念,一个形状是一个动态类型的数据。引入形状的目的是为了取代原有静态的ASP.NET MVC视图的数据模型(Model),让数据模型的数据类型可以在运行时更改。这样就可以很好的解决Orchard需要处理多种不可预知的数据类型问题。

 
    本文介绍了形状的概念,并解释它们是如何工作的。本文适合Orchard模块和主题开发者阅读,所以在学习本文前你需要先了解一些基本的模块和主题开发的知识。关于模块和主题的相关知识,你可以在《Orchard学习计划》中查看相应文章,当然查看官网文档也是不错的选择。同时形状是一个动态对象,所以你也有必要先了解一下动态对象的相关信息,关于动态对象请查看《Creating and Using Dynamic Objects》。
 
 

形状介绍(Introducing Shapes)

    形状是一个动态对象,利用形状模板,可以使数据以你想要的方式呈现给用户。形状模板是一段呈现形状的标记。形状的例子有:菜单、菜单项、内容项、文档和消息等。
 
    一个形状是一个数据模型对象,它继承于Orchard.DisplayManagement.Shapes.Shape类。形状类是没有实例化的。相反,形状是在运行时由一个形状工厂创建的。默认的形状工厂是Orchard.DisplayManagement.Implementation.DefaultShapeFactory。形状是形状工厂创建的动态对象。
 
    动态对象是.NET Framework 4的一个新特性。作为一个动态对象,形状在运行时公开其成员,而不是在编译时。与此相反,一个ASP.NET MVC的模型对象是在编译时就被定义为一个静态的对象。
 
    关于形状的信息是包含在自身的ShapeMetadata 属性里面。这些信息包括形状的类型,显示类型,位置,前缀,包装,替换名称,子内容和一个是否已执行的标记。你可以通过以下方法来访问形状的元数据:
var shapeType = shapeName.Metadata.Type;
    在形状对象创建以后,形状里面的数据就可通过形状模板中的Help方法来呈现出来。一个形状模板是一个段Html标记(部分视图,partial view)是负责显示形状的。此外,你也可以通过代码来呈现形状。如:定义一个方法并加上Shape属性即可,你可以在CoreShapes.cs中找到这种写法。
 
 

创建形状(Creating Shapes)

    对于模块开发者,最常见的形状是通过驱动器将数据通过一个模板呈现出来。一个驱动器是继承Orchard.ContentManagement.Drivers.ContentPartDriver类的,并重写基类的Display和Editor方法。Display和Editor方法返回一个ContentShapeResult对象,类似于ASP.NET中Action所返回的ActionResult。ContentShape方法可以帮助你创建形状并把它放到一个ContentShapeResult对象中返回。
 
    虽然ContentShape方法有很多重载,但通常只使用它有两个参数的那个重载。这两个参数分别是形状类型(shape type)和一个用于定义形状的动态对象的表达式。形状的类型名称将用于构建一个形状并结合该形状的模板一起用于呈现。形状类型的命名规则,我们将在稍后的“命名形状和模板”一节中讨论。
 
表达式能够很好的通过一个例子来描述。以下示例显示了一个驱动器的Display方法,它返回了一个形状的结果,是用于显示一个地图部件的。
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 形状和它的属性。这个表达式添加了一个Longitude属性可以设置地图的经度,添加了一个Latitude属性用于显示地图的维度。ContentShape方法创建了Display方法所需要返回的结果对象。
 
    在接下来的例子中显示了一个完整的地图部件驱动器所要返回的形状。Display方法用于显示地图,标记了Get的Editor方法用于显示编辑地图数据的画面,标记了Post的Editor方法用于处理编辑画面所提交的数据。这两个Editor方法分别使用了不同的Editor方法重载。
using Maps.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers; namespace Maps.Drivers
{
public class MapPartDriver : 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));
} //POST
protected override DriverResult Editor(
MapPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
}

标记了Get的Editor方法使用ContentShape去创建一个形状用于编辑模板。在这个案例中,形状类型名称是Parts_Map_Edit并且shapeHelper对象创建了一个EditorTemplate的形状。这是一个特殊的形状,它有一个模板名称属性和一个Model属性。模板名称属性指定了一个模板的部分路径,在这个案例中,“Parts/Map”将使Orchard在Views/EditorTemplates/Parts/Map.cshtml路径中查找相应的编辑模板。Model属性设置了这个模板所用到的数据。

 
 

命名形状和模板(Naming Shapes and Templates)

    如前所述,一个形状的类型名称将用于构建一个形状并结合该形状的模板一起用于呈现。例如:假设你创建了一个名为Map的部件用于显示地图的经纬度。形状类型的名称就肯能是Parts_Map。根据命名规则,所有的部件形状需要以Parts_开头,然后就是这个部件的名称(这此例中是Map)。鉴于此名称(Parts_Map),Orchard将在views/parts/Map.cshtml路径中查找相应的模板。
 
 

下表总结了形状类型和模板的命名规则:

Applied To Shape Naming Convention Shape Type Example Template Example
Content shapes Content__[ContentType] Content__BlogPost Content-BlogPost
Content shapes Content__[Id] Content__42 Content-42
Content shapes Content__[DisplayType] Content__Summary Content.Summary
Content shapes Content_[DisplayType]__[ContentType] Content_Summary__BlogPost Content-BlogPost.Summary
Content shapes Content_[DisplayType]__[Id] Content_Summary__42 Content-42.Summary
Content.Edit shapes Content_Edit__[DisplayType] Content_Edit__Page Content-Page.Edit
Content Part templates [ShapeType]__[Id] Parts_Common_Metadata__42 Parts/Common.Metadata-42
Content Part templates [ShapeType]__[ContentType] Parts_Common_Metadata__BlogPost Parts/Common.Metadata-BlogPost
Field templates [ShapeType]__[FieldName] Fields_Common_Text__Teaser Fields/Common.Text-Teaser
Field templates [ShapeType]__[PartName] Fields_Common_Text__TeaserPart Fileds/Common.Text-TeaserPart
Field templates [ShapeType]__[ContentType]__[PartName] Fields_Common_Text__Blog__TeaserPart Fields/Common.Text-Blog-TeaserPart
Field templates [ShapeType]__[PartName]__[FieldName] Fields_Common_Text__TeaserPart__Teaser Fields/Common.Text-TeaserPart-Teaser
Field templates [ShapeType]__[ContentType]__[FieldName] Fields_Common_Text__Blog__Teaser Fields/Common.Text-Blog-Teaser
Field templates [ShapeType]__[ContentType]__[PartName]__[FieldName] Fields_Common_Text__Blog__TeaserPart__Teaser Fields/Common.Text-Blog-TeaserPart-Teaser
LocalMenu LocalMenu__[MenuName] LocalMenu__main LocalMenu-main
LocalMenuItem LocalMenuItem__[MenuName] LocalMenuItem__main LocalMenuItem-main
Menu Menu__[MenuName] Menu__main Menu-main
MenuItem MenuItem__[MenuName] MenuItem__main MenuItem-main
Resource Resource__[FileName] Resource__flower.gif Resource-flower.gif
Style Style__[FileName] Style__site.css Style-site.css
Widget Widget__[ContentType] Widget__HtmlWidget Widget-HtmlWidget
Widget Widget__[ZoneName] Widget__AsideSecond Widget-AsideSecond
Zone Zone__[ZoneName] Zone__AsideSecond Zone-AsideSecond
你应该把你的模板按照以下规则放在项目中:
  • 内容项形状模板在 views/items目录下。
  • 部件形状模板在views/parts目录下。
  • 字段形状模板在views/fields目录下。
  • 编辑模板在相应的 views/EditorTemplates/{相应模板名称} 目录下。例如:部件的编辑模板需要在 views/EditorTemplates/Parts目录下。
  • 其他的形状模板都直接在views 目录下。
注:模板的扩展名可以是任何所支持的视图引擎文件扩展名,如:cshtml,vbhtml或ascx等。
 

从模板文件名称到形状名称(From Template File Name to Shape Name)

更普遍的是,冲一个模板文件的名称到相应形状名称的映射规则如下:
  • 将点(.)和反斜杠(\)改为下划线(_)。注意这点不包含后缀名中的点,如.cshtml。形状的模板文件需要在约定的目录中(见上文)。
  • 短划线(-)改为双下划线(__)。
例如:Views/Hello.World.cshtml将用于呈现名为Hello_World的形状;Views/Hello.World-85.cshtml将用于呈现名为Hello_World__85的形状。
 

形状的可替换性呈现(Alternate Shape Rendering)

如前所述,一个Html widget在AsideSecond 区域显示的时候可以通过一个widget.cshtml模板,或一个widget-htmlwidget.cshtml模板,或一个widget-asidesecond.cshtml模板,只要它们在当前主题中。当存在各种可能呈现同一内容的模板,这些就可称作为形状的可替换性,这些可替换的模板能让显示更加丰富多彩。
一组替换可对应于相的同形状,他们只是通过加一个有双下划线的后缀。如:Hello_World,Hello_World__85和Hello_World__DarkBlue就是一组可替换Hello_World形状的形状。而Hello_World_Summary就不属于这组可替换Hello_World的形状。这就是下划线(_)和双下划线(__)的不同。
 

那一个可用替换将被呈现(Which Alternate Will Be Rendered?)

如有一个形状有多个可用的替换,那么那个将被选择为最终呈现的。例如:Hello_World,在主题中给出的附加可选模板超出了默认的模板(例如hello.world.cshtml)。系统将会选择一个最特别的模板做为该形状的可替换模板。所以,如果hello.world-orange.cshtml模板存在他将比 hello.world.cshtml 模板优先采用。
 
内置内容项的可替换模板
通过上表可知模板还能为某一内容项单独定义。如一个显示内容摘要形状的模板(Content_Summary),系统可使用内容类型和内容Id作为替换,比如:Content_Summary__Page 和 Content_Summary__42。更多关于替换规则的信息,可以查看《Alternates》一文。
 

使用模板呈现形状(Built-In Content Item Alternates)

一个形状模板是一段用于呈现形状的标记语言片段。在Orchard中默认使用Razor视图引擎。因此,形状的模板默认使用Razor语法,关于Razor语法的介绍可以查看《Template File Syntax Guide》。
下面是一段显示地图部件的模板:
<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标签,这个图片用于呈现相应经纬度的地图。在相应的图片地址中,@Model代表形状传递给模板的数据。因此,@Model.Latitude和@Model.Longitude就分别是这个形状的两个属性,存储了经纬值。
 
接下来的一段代码,是地图部件用于编辑画面的模板,这个模板允许用户输入地图的经纬值。
@model Maps.Models.MapPart

<fieldset>
<legend>Map Fields</legend> <div class="editor-label">
@Html.LabelFor(model => model.Longitude)
</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表达式用于创建一个lable标签来显示形状属性的名称。@Html.TextBoxFor表达式用于创建一个input标签,让用户可以输入相应的值。@Html.ValidationMessageFor表达式则是用于显示验证相应属性输入出错信息的。
 

更多关于模板和Razor语法的信息,可查看《Template Files and their Locations》。

 

包装(Wrappers)

包装可以让你在形状周围添加一些标记来自定义形状呈现。比如:Document.cshtml是一个Layout 形状的包装,因为它在Layout形状外围有一些特殊的标记。更多关于Document.cshtml和Layout的关系可以查看《Template File Syntax Guide》。
 
通常,你添加一个包装文件到你主题的Views目录下。例如:为Widget添加一个包装,就可以添加一个Widget.Wrapper.cshtml文件到你主题的Views目录下。如果你使用Shape Tracing工具,你就可以看见一个形状都有哪些可用的包装名称了。你也可以在placement.info文件中为形状指定一个包装。有关更多如何指定包装的知识,可以查看《理解Orchard中的placement.info文件》。
 

创建一个形状方法(Creating a Shape Method)

另外一个呈现形状的方法就是通过为形状添加一个方法来定义并呈现形状。这个方法必须有一个Shape属性(Orchard.DisplayManagement.ShapeAttribute类)。这个方法还必须返回一个IHtmlString对象,而不是用模板来展现。这个返回的对象包含了要呈现形状的Html代码。
 
下面的代码显示了DateTimeRelative 形状。这个形状可以让一个Datetime值显示为一个已过多少时间的字符串,如:1分钟前,2小时前之类的。
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");
}
}
 
 

==========================================

作者:二十四画生

出处: http://esshs.cnblogs.com/

转载请注明来源于博客园——二十四画生的Blog,并保留有原文链接。

Orchard是如何呈现内容的的更多相关文章

  1. WPF界面设计技巧(5)—自定义列表项呈现内容

    原文:WPF界面设计技巧(5)-自定义列表项呈现内容 接续上次的程序,稍微改动一下原有样式,并添加一个数据模板,我们就可以达成下面这样的显示功能: 鼠标悬停于文件列表项上,会在工具提示中显示图像缩略图 ...

  2. 第二篇:呈现内容_第二节:WebControl呈现

    一.WebControl的呈现过程 WebControl派生自Control类,所以WebControl的呈现功能基于Control的呈现逻辑之上,但有了比较大的扩展. 首先,WebControl重写 ...

  3. 第二篇:呈现内容_第一节:Control呈现

    一.Control的呈现过程 在上个章节““生死有序”的控件生命周期”中,我们提到Render是控件开发的主角,但在控件树的“合成模式(Composite)”部分这位主角却缺席了(戏份太多的缘由).哦 ...

  4. Orchard运用 - 定制呈现最新博客文章

    每个博客系统为了吸引更多访问量,一般都会在首页或侧边栏列举一些最新文章/随笔以获取更多点击.其实也就是查询出最新的几篇文章并按照简练的方式呈现,比如一般都只有标题及其对应的链接,有时也会标注一下作者和 ...

  5. 第二篇:呈现内容_第三节:CompositeControl呈现

    一.CompositeControl的呈现过程 CompositeControl派生自WebControls,重写了Render(HtmlTextWriter writer)方法.在调用基类WebCo ...

  6. Orchard学习资料,适合入门上手

    名词解释: http://www.cnblogs.com/esshs/archive/2011/06/01/2067501.html   Orchard如何工作: http://www.cnblogs ...

  7. [翻译]Orchard如何工作

    Orchard一直是博主心中神一般的存在,由于水平比较菜,Orchard代码又比较复杂看了几次都不了了之了.这次下定决心要搞懂其工作原理,争取可以在自己的项目中有所应用.为了入门先到官网去学习一下相关 ...

  8. Orchard 微软CMS项目介绍

    我之前的项目中使用了Orchard, 它依据依赖注入的思想而做的模块化让我深深为之着迷,这里开始宣传一下这个架构. 包含的概念非常之多,我现在也不甚了解.Orchard就是自己想控制它改变它的话需要非 ...

  9. Orchard官方文档翻译(十一) 使用Tags组织文本

    原文地址:http://docs.orchardproject.net/Documentation/Organizing-content-with-tags 想要查看文档目录请用力点击这里 最近想要学 ...

随机推荐

  1. 树莓派3 之 安装Mysql服务

    需求 在树莓派上 安装Mysql 服务,并开启远程访问 步骤 安装 mysql server $ sudo apt-get install mysql-server 我以为中间会让我提示输入 数据库r ...

  2. jsp (二) 练习

    package cn.sasa.serv; import java.io.IOException; import java.sql.SQLException; import java.util.Lis ...

  3. Elasticsearch学习笔记(四)ElasticSearch分布式机制

    一.Elasticsearch对复杂分布式机制透明的隐藏特性 1.分片机制: (1)index包含多个shard,每个shard都是一个最小工作单元,承载部分数据,lucene实例,完整的建立索引和处 ...

  4. Jenkins+Jmeter持续集成笔记(二:ANT执行Jmeter脚本)

    Jmeter接口测试脚本运行后生成的是jtl(xml)格式的文件,这些文件不具备可读性,所以我们要把他转化为可以阅读的html格式报告. Ant是一个功能强大的打包编译工具.我们使用他的目的是将xml ...

  5. python基础(13)-面向对象

    类 类的定义和使用 # class Person: def __init__(self, name, age, gender): self.name = name self.age = age sel ...

  6. sklearn countvectorizer坑

    但是如果修改为['驴 福 记 ( 阜通 店 )','...',...]执行vectorizer.fit_transform就正常.原因是,创建CountVectorizer实例时,有一个默认参数ana ...

  7. 使用 Asp.net core 2.0 + Angular 4 构建车辆管理的Web应用程序

    https://www.codeproject.com/Articles/1210559/Asp-net-core-Angular-Build-from-scratch-a-web

  8. 细说@Html.ActionLink()的用法

    一.@Html.ActionLink()概述 在MVC的Rasor视图引擎中,微软采用一种全新的方式来表示从前的超链接方式,它代替了从前的繁杂的超链接标签,让代码看起来更加简洁.通过浏览器依然会解析成 ...

  9. C++中的对象初始化

    当对象在创建时获得了一个特定的值,我们说这个对象被初始化.初始化不是赋值,初始化的含义是创建变量赋予其一个初始值,而赋值的含义是把当前值擦除,而以一个新值来替代.对象初始化可以分为默认初始化.直接初始 ...

  10. extends 与implements的区别和用法

    1. 在类的声明中,通过关键字extends来创建一个类的子类.一个类通过关键字implements声明自己使用一个或者多个接口. extends 是继承某个类, 继承之后可以使用父类的方法, 也可以 ...