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

Forums / Developing with Sitefinity / GenericContentDesigner Inheritance

GenericContentDesigner Inheritance

8 posts, 0 answered
  1. Jeff
    Jeff avatar
    27 posts
    Registered:
    18 Jun 2008
    02 Mar 2009
    Link to this post
    Think I might have found a bug, but I wanted to check here before I reported it to make sure I wasn't doing something incorrectly.

    Scenario: We've got a GenericContent control subclassed within one of our base classes to allow shared content (think this was added when we first implemented Sitefinity) to be placed within the controls. We haven't had any problem with this up until this point, however with 3.6 the control editor no longer allows us to edit content on the control. Which is expected since we do not have a ControlDesigner implemented yet to accomplish this.

    Problem: Since the GenericContentDesigner was not sealed, I assumed I could inherit from this object and use the existing code to allow shared content manipulation. However, when doing so it looks like the embedded content that was done in 3.6 to speed up build times is looking for the resources in the current assembly, which in my case was App_Code.dll, for the resources rather than the assembly that called the code to pull the resources is in.

    Here's a sample of what I'm talking about:

    public class BaseGenericContentControl : UserControl
    {
        private GenericContent _genericContent1;

        public Guid ContentID
        {
            get { return this._genericContent1.ContentID; }
            set { this._genericContent1.ContentID = value; }
        }

        public string Content
        {
            get { return this._genericContent1.Content; }
            set { this._genericContent1.Content = value; }
        }

        public string ProviderName
        {
            get { return this._genericContent1.ProviderName; }
            set { this._genericContent1.ProviderName = value; }
        }   
    }

    Keep in mind the code I'm showing is just a sample of what we're doing. We've already been talking about changing our inheritance chain to start from WebControl rather than UserControl, but unfortunately too much code relies on this right now and would require a redesign of every control we've created thus far. The actual code of the above example contains some business rules and such we need access to on all of our controls. The control designer I haven't been able to finish yet because of the inheritance problem, this is just demonstrating what we're trying to do:

    public class GenericContentControlDesigner : GenericContentDesigner
    {
        public GenericContentControlDesigner()
        {
        }
    }

    I've thought about trying to subclass the GenericContentDesigner, but I'm fairly certain the resource problem will be there as well.

    Any help would be greatly appreciated. Thanks!
  2. Jeff
    Jeff avatar
    27 posts
    Registered:
    18 Jun 2008
    02 Mar 2009
    Link to this post
    Oops, forgot to add the control designer attribute to the control:

    [ControlDesigner(typeof(GenericContentControlDesigner))]
    public BaseGenericContentControl : UserControl

    When I go into Sitefinity admin and try to edit the control I get a "cannot find embedded resource Telerik.Cms.Engine.Resources.ControlTemplates.Backend.GenericContentDesigner.ascx" exception thrown in the Sitefinity UI. Which is expected if it was trying to load the resource from the App_Code.dll rather than the Telerik.Cms.Engine.dll it is defined in.
  3. Ivan
    Ivan avatar
    478 posts
    Registered:
    16 Jun 2015
    02 Mar 2009
    Link to this post
    Hi Jeff,

    the issue you are running into has to do with the embedded localization. In order to fix this, you should override the AssemblyInfo and LocalizationAssemblyInfo properties and set them to the type of GenericContentControlDesigner.

    Here is a blog post that explains something similar, though not exactly the same. However, you'll see the idea, code and find out more background on this:
    http://blogs.sitefinity.com/Ivan/Posts/09-02-25/Replacing_a_child_view_with_another_one.aspx

    I hope you'll find this information helpful. Let us know if we can assist you with anything else.

    Sincerely yours,
    Ivan
    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.
  4. Jeff
    Jeff avatar
    27 posts
    Registered:
    18 Jun 2008
    02 Mar 2009
    Link to this post
    Well, I finally got the problem solved for my subclassed generic content designer. However I'm not really sure why, but someone broke the rules of OO and shadowed the DesignedControl property on the GenericControlDesigner class unnecessarily. I'm a bit worried this may cause unintended consequences elsewhere in Sitefinity if the casting is not done correctly when using this class.

    With the DesignedControl property being shadowed, and both the accessibility and return type being modified, if you cast the instance of the GenericContentDesigner to that of the base type ControlDesigner, it will change how that property behaves entirely. I used this loophole to fix the problem I was running into with that property.

    Not sure if you'd want to take care of it or not, but here's what I did:

     

    using System;
    using System.Reflection;
    using System.Web.UI;
    using Telerik.Cms.Engine.WebControls;
    using Telerik.Cms.Engine.WebControls.Design;
    using Telerik.Framework.Web.Design;

    public
    sealed class GenericContentControlDesigner : GenericContentDesigner
    {
        public GenericContentControlDesigner()
        {
        }
        
        public override Type LocalizationAssemblyInfo
        {
            get { return typeof(GenericContentDesigner); }
            set { base.LocalizationAssemblyInfo = value; }
        }
        
        public
    override Type AssemblyInfo 
        {
            get { return typeof(GenericContentDesigner); } 
            set { base.AssemblyInfo = value; }
        }

        
    protected override void InitializeControls(Control viewContainer) 
        { 
            
    this.DesignedControl = ((BaseGenericContentControl)((ControlDesigner)this).DesignedControl).GenericContent1;

            base.InitializeControls(viewContainer);
        }
    }

     

  5. Ivan
    Ivan avatar
    478 posts
    Registered:
    16 Jun 2015
    03 Mar 2009
    Link to this post
    Hi Jeff,

    I am glad you have solved your problem. The DesignedControl property has been shadowed purposely, since GenericContentDesigner is a very specific designer (intended for designing GenericControl only). You can go higher up in the hierarchy and derive from ControlDesigner or ControlDesignerBase classes, if you look for more flexibility.

    I hope you'll find this information helpful. Let us know if there is anything else we can do for you.

    Regards,
    Ivan
    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.
  6. Jeff
    Jeff avatar
    27 posts
    Registered:
    18 Jun 2008
    03 Mar 2009
    Link to this post
    We're only using it to edit content on a GenericContent control, the only difference is we had to subclass it as shown from earlier 3.5 examples. With this new ControlDesigner system I can't see why we should need to rewrite the entire designer, or change our entire inheritance chain for our controls. I was thinking about this overnight, and I think I came up with a solution that'll work well and allow all of your designers to become much more flexible.

    1) Take the ControlDesignerBase class and add a virtual method on it:

    public abstract class ControlDesignerBase
    {
        public Control DesignedControl
        {
            get;
            set;
        }

        protected virtual Control GetDesignedControl()
        {
            return this.DesignedControl;
        }
    }

    Rather than pulling the control being designed from the DesignedControl property, use the virtual method that can be changed in derived types. Since the DesignedControl cannot change when the control is being saved, we really just need to override which is being edited by the designer in Sitefinity.

    2) Change the GenericContentDesigner class and remove the shadowed DesignedControl property. Change the methods that access the DesignedControl property to properly cast the value returned from the virtual method.

    public class GenericContentDesigner : ContentDesigner
    {
        protected override void InitializeControls(Control viewContainer)
        {
            GenericContent control = (GenericContent)this.GetDesignedControl();
            // Do your initialization here with the control object.
        }
    }

    Derived types can simply inherit from the GenericContentDesigner class and override the type of object being returned. That will allow those that are subclassing the object to add generic content editing capabilities to other controls and use the existing designer.

    public class GenericContentControlDesigner : GenericContentDesigner
    {
        public GenericContentControlDesigner()
        {
        }

        protected override Control GetDesignedControl()
        {
            BaseGenericContentControl control = this.DesignedControl as BaseGenericContentControl;
            if (control != null)
            {
                return control.GenericContent1;
            }

            return control;
        }
    }

    Where your GenericContentDesigner class uses the DesignedControl property just pull the control from the GetDesignedControl property and cast it to the appropriate type.

    The solution I posted earlier, actually didn't work completely. After posting it we ran into some problems when saving the data to the database.

    Any thoughts would be appreciated. I'm trying to come up with a solution that works long term without keeping us locked into Sitefinity 3.5 because of the designer change.
  7. Ivan
    Ivan avatar
    478 posts
    Registered:
    16 Jun 2015
    03 Mar 2009
    Link to this post
    Hello Jeff,

    I like your solution very much. The approach with virtual method seems as a solid and lasting one. At any rate, let us know if we can assist.

    Sincerely yours,
    Ivan
    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.
  8. Jeff
    Jeff avatar
    27 posts
    Registered:
    18 Jun 2008
    03 Mar 2009
    Link to this post
    *** THIS MAY RESULT IN BREAKING CHANGES AFTER UPGRADING SITEFINITY BEYOND 3.6 ***

    Here's the full solution I came up with using subclassed controls and the existing designers. Keep in mind the BaseGenericContentControl is our UserControl base class. That's the type you will need to change with whatever custom type you have created that you're using to subclass the GenericContent control. Hopefully it'll help someone else upgrading to 3.6. After I posted my earlier sample, we were having troubles getting the control saved to the database properly.

    using System;
    using System.Reflection;
    using System.Web.UI;
    using Telerik.Cms.Engine.WebControls;
    using Telerik.Cms.Engine.WebControls.Design;
    using Telerik.Framework.Web.Design;

    public sealed class GenericContentControlDesigner :
    GenericContentDesigner
    {
        private BaseGenericContentControl _contentControl;

        public GenericContentControlDesigner()
        {
        }

        public BaseGenericContentControl ContentControl
        {
            get { return this._contentControl; }
            set { this._contentControl = value; }
        }

        public
    override Type LocalizationAssemblyInfo
        {
            get { return typeof(GenericContentDesigner); }
            set { base.LocalizationAssemblyInfo = value; }
        }

        public
    override Type AssemblyInfo
        {
            get { return typeof(GenericContentDesigner); }
            set { base.AssemblyInfo = value; }
        }

        public override void OnSaving()
        {
            base.OnSaving();
            this.SetDesignedControlValue(this.ContentControl);
        }
       
        protected override void InitializeControls(Control viewContainer)
        {
            if (this.ContentControl == null)
            {
                BaseGenericContentControl contentControl = ((ControlDesigner)this).DesignedControl as BaseGenericContentControl;
                if (contentControl != null
                {
                    this.ContentControl = contentControl;
                    this.SetDesignedControlValue(contentControl.GenericContent1);
                }
            }
           
            base.InitializeControls(viewContainer);
        }

        private void SetDesignedControlValue(Control control)
        {
            FieldInfo field = typeof(ControlDesignerBase).GetField("designedControl", BindingFlags.NonPublic | BindingFlags.Instance);
            if (field != null)
            {
                field.SetValue(this, control);
            }
            else
            {
                throw new NullReferenceException("The designedControl field on the ControlDesignerBase class does not exist.");
            }
        }
    }

    Some information about why I chose to use Reflection to set the private field of the base type rather than using the property. Normally I would avoid doing this like the boubonic plague, but we simply had no choice in the matter other than being forced to rewrite all of our controls.

    When setting the DesignedControl property, it causes the control to reinitialize which in turn would cause the control to display an error indicating that the type of the control did not match what was originally being edited. The only way to avoid this was to set the field directly.

    As for the OnSaving method before it exits, the DesignedControl property MUST contain the type of control that was originally being edited by the class, even though the GenericContentDesigner expected a GenericContent object in the property. I had to leave the value alone while the base class was being manipulated because of the shadowed property on the object and change it before the control is saved.

    This is what I wanted to avoid with my solution I proposed earlier. Hopefully we will see that change made in the near future. :)

    Thanks for help you gave me earlier getting this to work Ivan. I appreciate it!

Register for webinar
8 posts, 0 answered