七、制作主题(四) Accessing and rendering shapes

shape是一个动态数据模型。shape的目的使用动态shape能在运行时使用一个model更新并替换ASP.NET MVC的静态view model,你可以认为shape是在模板中显示一大块数据。

本文介绍了shape的概念及解释如何使用。

 

Introducing Shapes

shape是一个动态数据模型,使用shape 模板为用户制造可见数据。shape模板是为呈现shapes的标记的片段。例如shapes包含menu.menu items,content items,documents,messages.

一个shape是从Orchard.DisplayManagement.Shapes.Shape类继承的数据模型对象。shape类不能实例化。shape是在运行时通过shape factory创建的,默认的shape factory是Orchard.DisplayManagement.Implementation.DefaultShapeFactory,shape factory创建的shape是动态的对象。

备注:动态对象是.net 4的新内容,一个动态对象在运行时而不是编译时暴露它的成员,对比一下,一个ASP.NET MVC model object是一个在编译时定义的静态对象。

关于shape的信息包含在这个shape的ShapeMetadata属性中,这个信息中包含了the shape's type, display type, position, prefix, wrappers, alternates, child content, and a WasExecuted Boolean value.

You can access the shape's metadata as shown in the following example:

var shapeType = shapeName.Metadata.Type;

shape对象创建后,使用shape模板呈现这个shape,shape是一段html标记,另一种方法是使用一个shape特性(Orchard.DisplayManagement.ShapeAttribute),使你写代码创建和显示该shape,不使用模板。

 

Creating Shapes

对于模块开发者,最常见的需求是从driver传输数据到一个要呈现的模板。drvier从Orchard.ContentManagement.Drivers.ContentPartDriver类继承,通常重写Display 和 Editor方法。Display 和 Editor方法返回一个ContentShapeResult对象,与ASP.NET MVC中的action返回的ActionResult对象相似。ContentShape方法帮助你创建shape并在ContentShapeResult对象中返回。

尽管ContentShape方法是重载方法,最典型的使用是传给它两个参数-shape类开和一个定义该shape的动态函数表达式。shape类型命名该shape并绑定该shape到会呈现它的模板。

函数表达式最好用一个示例描述。下面的示例展示了driver’s Display方法返回一个shape result,用来显示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和它的特性。表达式添加一个Longitude属性到shape并设置它等于该part’s Longitude属性。表达式也为shape添加了一个Latitudent属性并设置它等于part’s Latitude属性,ContentShape方法创建Display方法返回的results对象。

下面示例展示entire driver类,发送一个shape result给一个模板也在Map part中显示或编辑,Display方法用来显示该map,Editor方法为用户输入 使用”GET”方式在编辑模式显示shape result,Editor方法使用”POST”模式用用户输入的值重新显示该 editor view,这些方法使用不同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);
        }
    }
}

 

Editor方法通过“GET”使用ContentShape方法为editor模板创建一个shape,这种情况下,type name是Parts_Map_Edit和shapeHelper对象创建一个EditorTemplate shape,这几个shape有TemplateName和Model属性,TemplateName属性带部分路径到模板,这种情况下,"Parts/Map" 因此Orchard会在你的模块的下面路径下寻找:Views/EditorTemplates/Parts/Map.cshtml

Model属性带Part’s model文件名,不用带文件扩展名。

 

Naming Shapes and Templates

如前所述,shape type的名称用来绑定shape到模板,并用来渲染shape,例如:假设你创建了一个名叫Map 的part,为指定的longitude and latitude显示map,shape type的名称可能是 Parts_map。根据这个规则,所有的part shapes以Parts_开始,绩效考核part的名称。这个名称(Parts_Map),Orchard在你模块的下面位置寻找:views/parts/Map.cshtml,下表描述命名shape types和templates的规则:

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

 

你应该以下面的规则放置你项目中的模板:

  • Content item shape templates are in the views/itemsfolder.
  • Parts_ shape templates are in the views/partsfolder.
  • Fields_ shape templates are in the views/fieldsfolder.
  • The EditorTemplate shape templates are in the views/EditorTemplates/template name folder.
    For example, an EditorTemplate with a template name of Parts/Routable.RoutePart has its template at views/EditorTemplates/Parts/Routable.RoutePart.cshtml.
  • All other shape templates are in the views folder.

备注:模板扩展名能是激活的视图引擎支持的任何扩展名,比如:.cshtml,.vbhtml , .ascx

 

From Template File Name to Shape Name

通常地,从一个模板文件名映射到对应的shape名称的规则如下:

  • 点(.) 和反斜线(\) 变为下划线(_)。 备注:这不意味着Views的子目录myviews 下的_example.cshtml 文件与Views下的myviewsexample.chtml_文件一样, shape模板仍旧在预期的目录(见上文)。
  • 连接符 (-) 变为双下划线(__).
  • 例如:Views/Hello.World.cshtml会用于呈现一个名称为Hello_Worldr的shape,Views/Hello.World-85.cshtml会用于呈现名称为Hello_World__85的shape

     

     

    Alternate Shape Rendering

    如前文所述,AsideSecond zone中的一个HTML widget应该被widget.cshtml模板、widget-htmlwidget.cshtml模板或widget-asidesecond.cshtml呈现(如果存在的话),当一些呈现相同内容的可能性存在,这些引用到候选shape,并且他们丰富了模板。一组候选对应相同的shape,它们之间的不同仅是双下划线后缀。例如:Hello_World, Hello_World__85, and Hello_World__DarkBlue是一组Hello_World shape的候选,Hello_World_Summary,相反地,不属于这组,与它相对应的是Hello_World_Shape shape,不是 Hello_World   shape。(注意__和_的不同)

     

    Which Alternate Will Be Rendered?

    即便有候选,shape也总是以基本的名称创建,就像Hello_World。候选给了主题开发者额外的模板名称选项超出了默认的(比如hello.world.cshtml)。系统会选择从他们之中选择最匹配的模板,因此 hello.world-orange.cshtml会指向hello.world.cshtml(如果存在)。

     

    Built-In Content Item Alternates

    上面的表为content items展示了可能的模板名称,现在应该很明确从内容和显示的类型创建shape名称。(例如:Content_Summary)。系统也自动添加内容类型和内容ID 给 候选(例如:Content_Summary__page 和 Content_Summary__42)。

     

    Rendering Shapes Using Templates

    shape模板是用来呈现shape的一段标记,Orchard的默认视图引擎是Razor,因此,shape模板使用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属性包含了一个URL和传递查询字符串值的一组参数,这个查询字符串中,@Model代表被传递到模板的shape,因此,@Model.Latitude是shape的Latitude属性,@Model.Longitude是shape的Longitude属性。

    下面示例展示了editor的模板,这个模板使用户输入latitude and 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表达式使用shape属性名称创建labels,@Html.TextBoxFor表达式创建关于用户输入shape值的文本框,@Html.ValidationMessageFor表达式显示消息(当用户输入错误时)。

     

    Wrappers

    Wrappers让在shape周围添加标记来定制shape的呈现,例如:Document.cshtmlLayout shaper wrapper,因为它在Layout shape周围给了特殊的标记代码。

    通常,在主题的Views文件夹中添加Wrapper文件,例如:要为widget添加wrapper,就在主题的Views文件夹下添加Widget.Wrapper.cshtml文件。如果你启用了Shape Tracing功能,你将会看到一个shape可用的wrapper名称,也能在placement.info中指定一个wrapper。

     

    Creating a Shape Method

    创建和呈现shape的另一种方法是创建一个方法定义和呈现shape,方法必须用Shape特性标记(Orchard.DisplayManagement.ShapeAttribute类),方法返回IHtmlString对象来使用模板,返回对象包含呈现shape的标签。

    下面示例展示DateTimeRelative shape,这个shape传递一个 DateTime值,返回相对应的字符串。

    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");
        }
    }
posted @ 2012-03-23 14:43  commanderss  阅读(576)  评论(0编辑  收藏  举报