Orchard是如何呈现内容的

首先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");
    }
}
时间: 2024-10-18 12:27:40

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

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

一.WebControl的呈现过程 WebControl派生自Control类,所以WebControl的呈现功能基于Control的呈现逻辑之上,但有了比较大的扩展. 首先,WebControl重写了Render(HtmlTextWriter writer)方法,将呈现的逻辑一分为三:RenderBeginTag().RenderContents().RenderEndTag().WebControl的这种设计基于一种假设:每个WebControl最终生成一个HTML控件(当然这个HTML控件

第二篇:呈现内容_第一节:Control&ldquo;画皮&rdquo;之旅

一.Control的呈现过程 在上个章节""生死有序"的控件生命周期"中,我们提到Render是控件开发的主角,但在控件树的"合成模式(Composite)"部分这位主角却缺席了(戏份太多的缘由).哦,好吧.主角现在登场. 1)控件树呈现的"合成模式(Composite)" 控件树的呈现过程是一个华丽的大圈,它从RenderControl(HtmlTextWriter writer)开始.从RenderChildrenInte

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

一.CompositeControl的呈现过程 CompositeControl派生自WebControls,重写了Render(HtmlTextWriter writer)方法.在调用基类WebControl的Render(HtmlTextWriter writer)方法前,先调用了EnsureChildControls()方法,以确保创建子控件. protected internal override void Render(HtmlTextWriter writer) { if (base

第二篇:呈现内容_第四节:个性化自定义控件

一.特性(Attribute): ①DefaultProperty:(例:[DefaultProperty("Text")]) DefaultProperty是用于设置控件的默认属性.例子中[DefaultProperty("Text")],就是当你选择这个控件的时候,在属性窗口中自动被选中的是Text属性. ②ToolboxData:(例:[ToolboxData("<{0}:NonEmptyBox runat=server></{0}

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

原文地址:http://docs.orchardproject.net/Documentation/Organizing-content-with-tags 想要查看文档目录请用力点击这里 最近想要学习了解orchard,但却没有找到相关的中文文档,只有英文文档.于是决定自行翻译,以便日后方便翻阅. 转载请注明原作者与出处. 本人英文水平有限,错误之处欢迎指出以便修正 使用Tags组织文本 Content Orchard中的文本内容可以通过tags标签来分类.我肯可以通过URL中附加的关键字来进

Orchard

Orchard工作原理 概述 本文翻译仅供学习之用,了解Orchard工作原理设计思想.技术点及关键词,如有缺漏请不吝指正.鉴于能力有限定有诸多曲解或不完整的地方,请海涵.不定时完善整理. CMS不像常规的web程序,它更像一个程序容器. 设计系统时采用一个开放类型的架构,扩展作为首要特性是必需的. 架构图: 基础: 主要使用以下现有的框架和类库 ASP.NET MVC: web开发框架. MVC 关注分离 NHibernate: ORM工具. 将Orchard 内容持久化 .通过移除模块开发的

详细分析Orchard的Content、Drivers, Shapes and Placement 类型

本文原文来自:http://skywalkersoftwaredevelopment.net/blog/a-closer-look-at-content-types-drivers-shapes-and-placement 在本文中,我们将看看那些引让我夜不能寐的概念,因为我的生活不能找出与:shapes, content types, parts, fields, drivers and placement 等类型对应的东西.如果你有些使用Orchard的经验,但仍然觉得有点笨拙的控制shap

Orchard 与 ABP架构比较 (aspnetboilerplate)

前言:  ABP框架经常在一些.NET群中听群友提起,以前也浏览过官网,大致了解它是一个框架,直到今天本人才正式下载源码入门 ...   经过两个小时的ABP中文文档入门(感谢各位辛勤的翻译者) ,大致了解到 ABP框架提供了一些类似 Orchard的功能,如 日志,多租户,事件总线,多语言等. 由于刚刚接触ABP框架,下面来谈谈我对它的基本理解,不当之处敬请各位指正 :     Abp Orchard 作用 项目定位 开发框架 内容管理系统(CMS)+开发框架   基本框架 aspnetboi

轻松实现部分背景半透明的呈现效果

实现一个简单的呈现/解散动画效果,当呈现时,呈现的主要内容和背景要明显区分,背景呈现一个半透明遮罩效果,透过背景可以看到下层 View Controller 的内容: 呈现控制器(Presenting View Controller) 呈现控制器即要弹出另外一个控制器的 View Controller.本例中即 ViewController 类.当呈现时,使用如下代码: // 1 UIStoryboard* sb=[UIStoryboard storyboardWithName:@"Main&q