4

场景是我有很多 FieldRenderer。这些应该从各个地方输出数据,一些来自项目 X,另一些来自项目 Y。并且应该从项目 Z 输出属性。

假设我有一个ItemX要从中输出属性的公共属性,以下任何一项都可以。但我没有得到任何输出:

<sc:FieldRenderer runat="server" FieldName="Logo" DataSource="<%# ItemX %>" />
<sc:FieldRenderer runat="server" FieldName="Logo" DataSource="<%= ItemX.Paths.FullPath %>" />
<sc:FieldRenderer runat="server" FieldName="Logo" Item="<%# ItemX %>" />
<sc:FieldRenderer runat="server" FieldName="Logo" Item-ID="<%# ItemX.ID %>" />
<sc:FieldRenderer runat="server" FieldName="Logo" Item-ID-Guid="<%# ItemX.ID.Guid %>" />

如果我添加一个 IDMyFieldRenderer并在下面做丑陋的部分,我会得到正确的输出:

MyFieldRenderer.Item = ItemX;

必须有更好的方法来做到这一点?我不确定这是特定于 Sitecore 的问题还是 WebForms 问题。

4

3 回答 3

7

如果不使用后面的代码,就不可能将 FieldRenderer 的数据源设置为服务器端对象。这是 WebForms 工作方式的结果。Microsoft 知识库文章中描述了该问题:

<%= ... %> 显示表达式等效于仅包含 Response.Write(...) 语句的嵌入式代码块。这是显示单个字符串、int 变量或常量等信息的最简单方法。[...]请记住,显示表达式不能在服务器控件的属性中使用。这是因为 .NET Framework 直接编译整个表达式,而不是将显示的内容作为属性的值。

换句话说,.NET 想要编译 sc:FieldRenderer,因此无法访问 <%= ItemX.Paths.FullPath %> 的运行时内容。您可以通过尝试显示以更简单的形式查看此问题:

<asp:TextBox runat="server" Text="<%= DateTime.Now.ToString() %>" />

在文本框中呈现<%= DateTime.Now.ToString() %> 。简而言之,除了服务器控件属性中的静态字符串之外,您无法获得任何其他内容。


这个问题有几种可能的解决方案:

  1. 在您的 Page_Load 方法中,按照您的描述设置 FieldRenderer 的 Item 字段。如果需要使用此逻辑的子布局数量有限,这是最好的方法。

  2. 您可以创建将 Item 绑定到 ItemX 的 ItemXFieldRenderer 子类:

     class ItemXFieldRenderer: FieldRenderer {
       public ItemXFieldRenderer() {
           Item = [code to retrieve ItemX];
       }
     }
    

    然后,您可以在解决方案中要从 ItemX 呈现字段的任何位置使用此控件。如果大量子布局需要使用此逻辑并且您可能需要绑定的项目数量非常有限,这是最好的方法。

  3. 您可以创建解析字符串属性并使用逻辑将字符串值映射到正确项目的 FieldRenderer 的子类。

  4. 如果 ItemX 的路径是常量,则可以在 Datasource 属性中设置完整路径,如下所示:

    <sc:FieldRenderer runat="server" FieldName="Logo"
    DataSource="/sitecore/context/home/some/item" />
    

    您也可以使用相对路径。例如,如果上下文项有一个名为“Sources”的子文件夹,而该子文件夹又具有一个子项“Default”,则可以使用以下语法在 FieldRenderer 中引用:

    <sc:FieldRenderer runat="server" FieldName="Logo"
    DataSource="sources/default" />
    

    根据我的测试,数据源的评估不区分大小写,并且像“../..”和“//*[@@name='value']”这样的 Sitecore 查询表达式不起作用。

  5. 您可以使用数据绑定来强制 ASCX 读取属性,如此论坛线程中所推荐的:

    <sc:FieldRenderer id="myRenderer" FieldName="Logo" 
    DataSource=<%# ItemX.Paths.FullPath %> />
    

    在代码隐藏中,添加

    myRenderer.DataBind(); 
    

    使用最后一种方法,您仍然在使用代码隐藏,但是决定哪个 FieldRenderer 使用哪个项目现在包含在标记中。正如 Christian Hagelid指出的那样,您可以在子布局上调用 this.DataBind() 以强制 DataBind 在页面上的所有控件上递归执行。

  6. 您可以使用 ASP.NET 的 ExpressionBuilder 语法来集中数据源路径的位置。有三种方法可以做到这一点:

    • 将您的路径放在 Web.config 中。将此添加到 Web.config 的 <AppSettings> 部分:

      <add key="ItemX" value="/sitecore/content/path/to/itemx" />
      然后将 DataSource 属性设置为:

      DataSource=<%$ AppConfig: ItemX %>

    • 将路径放在 App_GlobalSettings 中的 .resx 资源文件中。如果文件名为 Paths.resx,您可以使用以下语法访问其设置:

      DataSource=<%$ Resources: Paths,ItemX %>

    • 您可以构建一个ExpressionBuilder类来构建将字符串值转换为路径的逻辑。请注意,ExpressionBuilder 是在 Parse 时评估的,因此您将无权访问 Sitecore 上下文。这看起来并不简单:表达式构建器需要在 Web.config 中引用,并且代码需要驻留在 App_Code 中。

于 2012-11-16T23:25:38.033 回答
4

如果您有很多 FieldRenderer 控件并且您希望在标记中包含逻辑,那么您可以使用 <%# ... %> 表达式并调用 this.DataBind(); 在 Page.Load 的末尾。然后将绑定用户控件中的所有控件。

于 2012-11-17T02:21:24.397 回答
1

我了解您希望能够像 MVC 样式那样在前端而不是在代码隐藏中执行此操作,但这种情况似乎表明前端 FieldRenderer 控件可能不是正确的工具。我显然看到了您在答案中编写的代码,但为什么不这样做呢,全部在前端:

<%= FieldRenderer.Render(item, "field name") %>

例如

<%= FieldRenderer.Render(ItemX, "Logo") %>
于 2012-11-20T20:20:14.217 回答