What's a good way to set the Item or DataSource attribute of a FieldRenderer?

前端 未结 3 2110
孤城傲影
孤城傲影 2021-02-10 14:50

The scenario is that I have a lot of FieldRenderers. These should output data from various places, some from item X and others from item Y. And should be outputting properties f

3条回答
  •  遥遥无期
    2021-02-10 15:40

    It is not possible to set the datasource of a FieldRenderer to a server side object without using code behind. This is a consequence of how WebForms works. The issue is described in a Microsoft Knowledge Base article:

    The <%= ... %> displaying expression is an equivalent of the embedded code block that contains only the Response.Write(…) statement. This is the simplest way to display information such as a single string, an int variable, or a constant. [...] Remember that the displaying expression cannot be used in the attributes of server controls. This is because the .NET Framework directly compiles the whole expression instead of the displaying content as the value to the attribute.

    In other words, .NET wants to compile the sc:FieldRenderer, and so does not have access to the run-time contents of <%= ItemX.Paths.FullPath %>. You can see this issue in simpler form by trying to display:

    
    

    which renders <%= DateTime.Now.ToString() %> inside the text box. In short, you cannot get anything other than a static string inside a server control attribute.


    There are several possible solutions to this issue:

    1. In your Page_Load method, set the Item field of the FieldRenderer, as you describe. This is the best approach if the number of sublayouts that need to use this logic is limited.

    2. You can create an ItemXFieldRenderer subclass that binds Item to ItemX:

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

      Then you can use this control anywhere in your solution where you want to render a field from ItemX. This is the best approach if a large number of sublayouts need to use this logic and the number of items you might need to bind to is very limited.

    3. You could create a subclass of FieldRenderer that parses a string property and uses logic to map the string value to the correct item.

    4. If the path to ItemX is constant, you can set the full path in the Datasource property like this:

      
      

      You can also use a relative path. For example, if the context item has a child folder called "Sources", which in turn has a child item "Default", you could reference in your FieldRenderer with this syntax:

      
      

      Per my testing, the evaluation of the datasource is case-insensitive, and Sitecore Query expressions like "../.." and "//*[@@name='value']" do not work.

    5. You can use databinding to force ASCX to read the property, as recommended in this Forums thread:

       />
      

      And in codebehind, add

      myRenderer.DataBind(); 
      

      With this last approach you are still using codebehind, but the decision of which FieldRenderer uses which item is now contained in the markup. And as Christian Hagelid points out, you can call this.DataBind() on the sublayout to force DataBind to execute recursively on all controls on the page.

    6. You can use ASP.NET's ExpressionBuilder syntax to centralize the location of your datasource paths. There are three ways of doing this:

      • Places your paths in Web.config. Add this to the section of Web.config:


        Then set the DataSource attribute to:

        DataSource=<%$ AppConfig: ItemX %>

      • Place the paths in a .resx resource file in App_GlobalSettings. If the file is named Paths.resx, you can access its settings with this syntax:

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

      • You can build an ExpressionBuilder class to build logic to translate a string value to a path. Note that the ExpressionBuilder is evaluated at Parse time, so you will not have access to the Sitecore context. This does not look straightforward: the expression builder needs to be referenced in Web.config and the code needs to reside in App_Code.

提交回复
热议问题