1-888-365-2779
+1-888-365-2779
Try Now
More in this section

Forums / Developing with Sitefinity / Adding User Controls to Page: Programmatic Doesn't Work, Sitefinity UI Does

Adding User Controls to Page: Programmatic Doesn't Work, Sitefinity UI Does

11 posts, 0 answered
  1. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    17 Apr 2009
    Link to this post
    I'm having trouble adding controls to a page through code. It works just fine when I add them through the Sitefinity user interface.

    Here's the funny thing: I only have a problem when the user control has a code behind file. If I place all the C# code in a server script tag within the ascx, I can add it just fine.

    Here's what I see with the code behind in place:
        * No errors adding the control in code
        * The control doesn't render in page preview.
        * When editing the page, the control simply shows "Index was outside the bounds of the array."
        *  The control's public properties show up if you edit it within the page editor.


    Here's the code I'm using to add the control to the page:
                CmsManager cmsManager = new CmsManager(); 
                Guid templateId = ((IPageTemplate)cmsManager.GetTemplates()[0]).ID; 
                ICmsPage NewsPage = cmsManager.CreatePage("TestPage"); 
                IStagedPage staged = NewsPage.Staged.CheckOut(); 
                staged.SetTemplate(templateId, null); 
     
                BuiltToBreak btb = new BuiltToBreak(); 
                staged.AddControl("Content", btb); 
     
                NewsPage = (ICmsPage)cmsManager.GetPage(NewsPage.ID, true); 
                staged.CheckIn(); 
     
                NewsPage.Publish(); 
     


    Here's the control with codebehind (.ascx):
    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="BuiltToBreak.ascx.cs" Inherits="BuiltToBreak" %> 
    <p>Not much to see here.</p> 
    <%= Anything %> 

    The codebehind (ascx.cs)
    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Collections; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
     
    public partial class BuiltToBreak : System.Web.UI.UserControl 
        protected void Page_Load(object sender, EventArgs e) 
        { 
     
        } 
     
        public string Anything 
        { 
            get 
            { 
                return "<p>Anything will do</p>"
            } 
        } 
     


    Get rid of the codebehind and everything works fine:
    <%@ Control ClassName="BuiltToBreak" Language="C#"  %> 
    <p>Not much to see here.</p> 
    <%= Anything %> 
    <script runat="server"
        public string Anything 
        { 
            get 
            { 
                return "<p>Anything will do</p>"; 
            } 
        } 
    </script> 
     


    Here's the web.config for registering the control. Adding using the page editor works, even with the code behind:
    <add name="Built to Break" section="Test" url="~/BuiltToBreak.ascx"  description="The UI adds this control no problem, code behind or not."/> 
     


    Very puzzling! What is Sitefinity doing that I'm not? Any ideas?

    Thanks,
    Jeff
  2. Dido
    Dido avatar
    149 posts
    Registered:
    24 Sep 2012
    17 Apr 2009
    Link to this post
    Hi Jeff,

    Here is a sample code that I use in an internal tool that creates a page and adds a Generic Content with shared IContent in it.
    ContentManager contentManger = new ContentManager(); 
    IContent topicContent = contentManger.CreateContent("text/html"); 
    topicContent.SetMetaData("Name", contentTitle); 
    topicContent.SetMetaData("Author"this.User.Identity.Name); 
    topicContent.SetMetaData("Description""..."); 
    topicContent.Content = "..."
    contentManger.SaveContent(topicContent); 
     
    // create a generic content control holding this topic content 
    GenericContent contentWrapper = new GenericContent(); 
    contentWrapper.ContentID = topicContent.ID; 
     
    // set page's properties (location) 
    // manager is an instance of CmsManager 
    // page name is just a string obtained through UI 
    // and siteMap is the control through which you selected pages in Pages section 
    // in admin. selectedPage is a ICmsPage, obtained with siteMap.SelectedPageID and 
    // CmsManager 
    ICmsPage topicPage = manager.CreatePage(pageName, this.siteMap.SelectedPageID); 
    topicPage.MenuName = this.topicName.Text; 
    topicPage.Title = "Sitefinity Documentation -> " + this.topicName.Text; 
    topicPage.CacheSettings = new CacheSettings(120, true); 
    topicPage.Navigable = true
    if (selectedPage != null
        topicPage.Ordinal = selectedPage.Pages.Count; 
     
    // if the user doesn't have permission to create pages, throw a security exception 
    PagePermission perm = new PagePermission(topicPage, CrudRights.Create); 
    perm.Demand(); 
     
    // when a page itself is changed (created in our case), it is saved 
    manager.SavePage(topicPage); 
     
    topicPage = manager.GetPage(topicPage.ID, trueas ICmsPage; 
     
    if (topicPage != null
        // set page's properties (look & feel) 
        // Default should always be present, but you can opt for something else  
        IPageTemplate template = manager.GetTemplate("MyCustomTemplateName"); 
        if (template == null
        { 
            template = manager.GetTemplate("Default"); 
            if (template == null
            { 
                // This simply utilizes a MessageControl's message and mode properties 
                DisplayMessage("Cannot find default template!", MessageMode.Error); 
                return
            } 
        } 
        // template is always set through this method, and not directly 
        topicPage.Staged.SetTemplate(template.ID, "Orange with left sidebar"); 
        topicPage.Staged.AddControl("Content", contentWrapper); 
     
        // when a page's content is changed, it is published 
        topicPage.Publish(); 
    // this redirects to the newly created page, so that we can see it 
    this.Page.Response.Redirect(String.Concat(topicPage.StaticUrl, "?cmspagemode=edit")); 

    The code is very straight-forward and should speak for itself. If you have more questions, feel free to ask.

    Sincerely yours,
    Dido
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  3. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    17 Apr 2009
    Link to this post
    Hi Dido,

    Thanks for providing the sample code. Unfortunately, I'm getting the exact same problem when I use it.

    I'm replaced the code where you create the generic content control with my own control.

    In place of this segment of code:
                IContent topicContent = contentManger.CreateContent("text/html"); 
                topicContent.SetMetaData("Name", contentTitle); 
                topicContent.SetMetaData("Author"this.User.Identity.Name); 
                topicContent.SetMetaData("Description""..."); 
                topicContent.Content = "..."
                contentManger.SaveContent(topicContent); 
     
                // create a generic content control holding this topic content  
                GenericContent contentWrapper = new GenericContent(); 
                contentWrapper.ContentID = topicContent.ID; 
     


    I use:
    // My simple control containing some HTML and one public string property (see first post) 
    BuiltToBreak contentWrapper = new BuiltToBreak(); 
     


    When I run your sample code, my control still fails to render and I get the "Index was outside the bounds of the array" in page editing mode.

    I'm still scratching my head on this one.
  4. Dido
    Dido avatar
    149 posts
    Registered:
    24 Sep 2012
    21 Apr 2009
    Link to this post
    Hello Jeff,

    The error you see is a regular .net exception. At design time all exceptions are caught, so if you see a strange error in design-time, you should debug your code. In your case, it is ArgumentOutOfRangeException.
    We will check the code once again, but we are using the same here.

    Kind regards,
    Dido
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  5. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    21 Apr 2009
    Link to this post
    Hi Dido,

    You have the entire markup and C# code for the user control in the first post. As you can see, the only code is a read only property that returns a hard-coded string. No chance of an "Index outside of bounds of array" exception there.

    In addition, when I go to Sitefinity's Page Editor with the debugger running, nothing is caught in my code.

    You mentioned "design-time." I just want to make sure we are talking about the same thing here. I'm seeing the error on the Sitefinity Page Editor that comes up when you click the "Edit this Page" button. I'm not talking about the Visual Studio Toolbox and this is not a custom control, it is a web user control.

    I know I must be simply missing a step. Like I say, when I drag the control onto the page in the Page Editor, it works fine. When I add it programmatically, I get the exception.

    Thanks for sticking with me on this problem.

    Jeff
  6. Dido
    Dido avatar
    149 posts
    Registered:
    24 Sep 2012
    22 Apr 2009
    Link to this post
    Hi Jeff,

    Would it be a problem if you gave me the source code for your control? I will test it locally, and return you a working example.

    Regards,
    Dido
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  7. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    22 Apr 2009
    Link to this post
    It's not a problem to give you the source code. In fact, it's been there all along in the first post of this thread!

    There are two versions of the source code. The first uses a code behind. This is the one that is giving the problem. The second has the C# code built in to the ascx. This one works fine.

    I appreciate you trying this out for yourself and getting to the bottom of it.

    Thanks,
    Jeff
  8. Dido
    Dido avatar
    149 posts
    Registered:
    24 Sep 2012
    23 Apr 2009
    Link to this post
    Hi Jeff,

    I have made a simple user control that demonstrates how to do this. I have attached it.

    Sincerely yours,
    Dido
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  9. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    24 Apr 2009
    Link to this post
    We're getting closer. The sample you provided does work but I simplified my "BuiltToBreak" example a bit too much: I'm also trying to save the control state when I add it to the page.

    Given that requirement, it's not enough to simply load .ascx file, I need to add the control after it has been instantiated.

    Allow me show you the BuiltToBreak control with state saving built in.

    BuiltToBreak.ascx
    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="BuiltToBreak.ascx.cs" Inherits="BuiltToBreak" %> 
    <p>Not much to see here.</p> 
    <%= IDPlease %> 

    BuiltToBreak.ascx.cs
    using System; 
    using System.Data; 
    using System.Configuration; 
    using System.Collections; 
    using System.Web; 
    using System.Web.Security; 
    using System.Web.UI; 
    using System.Web.UI.WebControls; 
    using System.Web.UI.WebControls.WebParts; 
    using System.Web.UI.HtmlControls; 
     
    public partial class BuiltToBreak : System.Web.UI.UserControl 
        private int m_nUniqueID; 
         
        public int UniqueID 
        { 
            get { return m_nUniqueID; } 
            set { m_nUniqueID = value; } 
        } 
     
        public string IDPlease 
        { 
            get 
            { 
                return "The saved ID is " + m_nUniqueID.ToString(); 
            } 
        } 
     
        protected override void OnInit(EventArgs e) 
        { 
            Page.RegisterRequiresControlState(this); 
            base.OnInit(e); 
        } 
     
        protected override void LoadControlState( 
            object savedState) 
        { 
     
            object[] rgState = (object[])savedState; 
            base.LoadControlState(rgState[0]); 
            m_nUniqueID = (int)rgState[1]; 
     
        } 
     
        protected override object SaveControlState() 
        { 
            object[] rgState = new object[2]; 
            rgState[0] = base.SaveControlState(); 
            rgState[1] = m_nUniqueID; 
            return rgState; 
        } 
     


    There are only two AddControl overloads that accept a Control as a parameter. Neither have the "string controlId" parameter.

    Can you think of a way to get this to work? Maybe let Sitefinity instantiate it and then modify the properties?

    Thanks again,
    Jeff
  10. Dido
    Dido avatar
    149 posts
    Registered:
    24 Sep 2012
    28 Apr 2009
    Link to this post
    Hello Jeff,

    User controls aren't meant to be initialized as variables, so this is going to be a kind of hack. If you want to set other properties than the ones inherited by Control or UserControl, you will have to use reflection (i.e. no medium trust).

    Custom controls, on the other hand, are much more flexible. You can implement SimpleControl to automate a few tasks.

    Anyway, here is how you load a user control:

    UserControl ctrl = new UserControl(); 
    Control myControl = ctrl.LoadControl("~/UserControls/Tickets/BuiltToBreak.ascx"); 

    Since user controls are partial classes, at design-time (in VS) you cannot access the second "part" of the control which is dynamically generated by ASP.NET. Still, you have the instance and can access all methods through reflection. but, as I said before, it is not a viable option.

    By the way, adding a user control to a Sitefinity page this way will not work well. Internally, ICmsControl has a type property, which in the case of user controls is set to the virtual path of the user control's ascx file. Whey you add it like a regular control (like I do in this example), it won't throw an exception, but a few glitches here and there will appear.

    A side note: Sitefinity automatically persists the state of public controls, so you do not have to load/save the state.

    As a conclusion - if you want a user control, use it as such and do not expect advanced behavior. On the other side, if you want full control, use a custom control.

    Kind regards,
    Dido
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  11. Jeff
    Jeff avatar
    124 posts
    Registered:
    05 Aug 2007
    29 Apr 2009
    Link to this post
    Hi Dido,

    It looks like I'm going to have to bite the bullet and convert most of my user controls to custom controls.

    Thanks again for the tips and sticking with me on this issue.

    Jeff
Register for webinar
11 posts, 0 answered