2012-11-16 60 views
4

這種情況是我有很多FieldRenderers。這些應該輸出來自各個地方的數據,一些來自項目X,另一些來自項目Y.並且應該輸出來自項目Z的屬性。什麼是設置FieldRenderer的Item或DataSource屬性的好方法?

假設我有一個公共屬性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 %>" /> 

如果我添加一個ID MyFieldRenderer給它做下面的難看的片片,我得到正確的輸出:

MyFieldRenderer.Item = ItemX; 

必須有一個更好的辦法去做這個?我不確定這是一個Sitecore特定還是WebForms問題。

回答

7

無法使用後臺代碼,無法將FieldRenderer的數據源設置爲服務器端對象。這是WebForms如何工作的結果。這個問題在Microsoft Knowledge Base article描述:

的<%= ...%>顯示錶達式是僅包含回覆於(...)語句嵌入代碼塊的等效。這是顯示單個字符串,int變量或常量等信息的最簡單方法。 [...] 請記住,顯示錶達式不能用於服務器控件的屬性。這是因爲.NET Framework直接編譯整個表達式而不是顯示內容作爲屬性的值。

換句話說,.NET希望編譯SC:FieldRenderer,所以不具有訪問<%= ItemX.Paths.FullPath%>的運行時的內容。您可以通過嘗試顯示看到簡單的形式這個問題:

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

這使得<%= DateTime.Now.ToString()%>的文本框內。簡而言之,除了服務器控制屬性中的靜態字符串以外,您無法獲取任何內容。


有這個問題的幾個可能的解決方案:

  1. 在你的Page_Load方法,設置FieldRenderer的項目領域,你描述。如果需要使用該邏輯的子層數量有限,這是最好的方法。

  2. 您可以創建一個子類ItemXFieldRenderer結合項目ItemX:

    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" /> 
    

    您也可以使用相對路徑。例如,如果上下文項目有一個名爲「源」子文件夾,這反過來又一個子項「默認」,你可以在你的FieldRenderer這個語法參考:

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

    按我的測試,評價的數據源不區分大小寫,Sitecore查詢表達式如「../ ..」和「// * [@@ name ='value']」不起作用。

  5. 您可以使用數據綁定來強制ASCX讀取屬性,如推薦的這款Forums thread

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

    而在代碼隱藏,添加

    myRenderer.DataBind(); 
    

    隨着你仍然在使用這個最後的方法代碼隱藏,但決定哪個FieldRenderer使用哪個項目現在包含在標記中。作爲Christian Hagelid points out,您可以在子佈局上調用this.DataBind()以強制DataBind遞歸執行頁面上的所有控件。

  6. 您可以使用ASP.NET的ExpressionBuilder語法來集中數據源路徑的位置。有三種方法可以做到這一點:

    • 將您的路徑放在Web.config中。這種添加到Web.config文件的<的AppSettings >部分:在App_GlobalSettings一個的.resx資源文件

      DataSource=<%$ AppConfig: ItemX %>

    • 地點的路徑:

      <add key="ItemX" value="/sitecore/content/path/to/itemx" />
      然後將DataSource屬性設置爲。如果文件被命名爲Paths.resx,你可以用這個語法訪問其設置:

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

    • 你可以建立一個ExpressionBuilder類構建邏輯翻譯一個字符串值的路徑。請注意,ExpressionBuilder會在Parse時間進行評估,因此您無法訪問Sitecore上下文。這看起來並不簡單:需要在Web.config中引用表達式構建器,並且代碼需要駐留在App_Code中。

+0

您的建議1,2和5沒有真正解決問題的方法,因爲它們會導致等量的每個數據源項複製代碼作爲我最初的問題(你的建議#1)。爲了記錄,我認爲建議#2是可怕的:)#4不是可擴展的,並且在所有地方重複路徑。 #3需要交換所有現有的FieldRenderers(這是一個大型項目中風險較大的「全部替換」操作),但實際上是一個有效的建議。 – Simeon

+0

哎唷!我有點喜歡#2;)但我同意,如果項目數量超過一個或兩個,它不可擴展。 (我正在尋找一些能夠讓你在ASCX中保留所有更改的東西。)我已經在ExpressionBuilders上添加了一個部分,這將允許您保留路徑合併。你提出的解決方案看起來很合理,但我會傾向於#3,並引用一個StringToPath引用類。 –

+0

這很有創意,我會給你的!但在我最近的項目中,我們有40多個可推薦的「關鍵項目」結構。感謝最後的解決方案#6,這很有趣,因爲在控件上調用'.DataBind()'使得它們輸出ViewState數據。構建一個ExpressionBuilder類,您可以直接從標記中引用項目ID,避免額外的數據。它唯一的限制是頁面需要被編譯(使用'CodeBehind'而不是'CodeFile'),但我想大多數項目都會編譯它們的頁面/控件。我會根據這個建議寫一個完整的答案! – Simeon

4

如果你有很多FieldRenderer控制措施,並要包含標記內部的邏輯,那麼你可以使用<%#...%>表達並調用this.DataBind() ;在Page.Load的結尾。這將綁定您的用戶控件中的所有控件。

+0

+1簡化DataBind過程的不錯方法。 –

+0

這對性能沒有影響嗎?想象一下,每個頁面上有10個以上的子佈局,每個子佈局都有數據綁定的30個控件(有些是不必要的)。我認爲這應該是一個評論,因爲它本身不是一個答案,而只是一個提示,順便說一句,但謝謝你的建議 – Simeon

+0

如果你有很多其他控件在你的用戶控件內,那麼是的這可能不是你最好的解決方案。這取決於您的控件的結構。基於你的問題中的信息,我認爲這會很好地解決你的問題 –

1

我知道你希望能夠像MVC風格那樣在前端做到這一點,而不是在代碼隱藏方面,但是這種情況似乎表明前端FieldRenderer控件可能不是正確的工具。我明顯看到了你在答案中寫的代碼,但爲什麼不這樣做呢,全部在前端:

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

例如,

<%= FieldRenderer.Render(ItemX, "Logo") %> 
+0

這不是「全部在前端」,因爲它需要在代碼隱藏中設置「item」(或「ItemX」)。但儘管Dan Solovay真正的「前端」解決方案超越了這個問題,但對於最初的問題 – Simeon

+0

這是一個很好的建議,我想這是基於你的陳述「假設我有一個公共財產ItemX」 - 所以我展示瞭如何做到這一點,沒有額外的C#。您將始終需要某種C#才能訪問您的項目。 –

相關問題