Creating a designer script baseClass - my head hurts

Posted by Community Admin on 04-Aug-2018 03:33

Creating a designer script baseClass - my head hurts

All Replies

Posted by Community Admin on 27-Sep-2012 00:00

My head is hurting from banging it on the desk, so I thought I'd solicit some feedback from others smarter than me.

I have lots of CustomControls in a class library.

Each control has a designer, based on Telerik.Sitefinity.Web.UI.ControlDesign.ControlDesignerBase

The designers all use a standard tabbed (RadTabstrip) interface, all tend to present properties in similar ways, request similar responses, and process the input in similar ways.

FWIW, I chose ControlDesignerBase + TabStrip over ContentViewDesignerBase, because I found it more flexible, but that's beside the point.


Anyway... the 'normal' way to register a designer script is:

MyNamespace.MyDesigner <- Telerik.Sitefinity.Web.UI.ControlDesign.ControlDesignerBase

The property editor, any custom UI components, etc etc, are all defined and managed in MyDesigner, and referenced by overriding and adding to the base ScriptDescriptors, in the CustomControl DesignerClass.

This is all simple enough, and it does work, but results in a really cluttered and messy script, when you have a complex designer, that makes creating and debugging a new control script a total PITA.

To reduce some of the clutter and code duplication, I'd moved all of the UI manipulation and data logic into a separate 'utility' class, and reference it from MyDesigner by instantiating the utility class... and it certainly works, but it just doesn't 'feel' right, and still requires the use of 'boiler-plate' objects and methods be set up each time.

I was trying to re-jig a 'cleaner' solution as follows:

MyNamespace.MyDesigner <- MyNamespace.MyDesignerBase

MyNamespace.MyDesignerBase <- Telerik.Sitefinity.Web.UI.ControlDesign.ControlDesignerBase

The idea was that I would then load all of the common objects, methods and events into the base class, and set up the structure using initializeBase() and then callBaseMethod() as required.

I have a basic understanding of how asp.net ajax inheritance works, and at first blush, that seems pretty straight-forward.

However, where my brain starts to fry, is in rendering multiple scriptdescriptors, to instantiate both base and derived classes, and what the ramifications will be.

I assume that I would need to add a second scriptdescriptor doing something like: new ScriptControlDescriptor("MyNamespace.MyDesignerBase") so that a separate $create() is rendered for the base class.

But then I'm struggling with which descriptor should be loaded with the property editor, which should manage the common tabstrip, which should get the unique UI components, etc etc, and what problems I might be creating for myself (assuming I can get that far).

Anyone with any experience in creating this sort of inheritance structure ??

Posted by Community Admin on 27-Sep-2012 00:00

You are just a bit ahead of me on this topic. I haven't gone as far as you in solving the complex boilerplate, but I have been pondering the issue as I rework some of my widgets. I appreciate you posting this.

Posted by Community Admin on 27-Sep-2012 00:00

Is your intention for myDesignerBase to be for the js file, or the ascx file or both? I read it that you are trying to make a base js file to inherit to abstract away some of the tedious boilerplate. Am I understanding correctly?

Posted by Community Admin on 27-Sep-2012 00:00

Yeah, that's right - splitting the standard single client script into a designerbase for the boilerplate stuff, and then loading the control-specific stuff into the derived class.

The mechanics of it are simple enough... you specify your base class at the bottom of the script (normally that is telerik's designerbase) and then initialise the base in the constructor. You can then access any property/method in the base via callBaseMethod(this, 'method', [parameters])

This is a better explanation:
http://www.codeproject.com/Articles/55414/From-Simple-JavaScript-Classes-to-ASP-NET-AJAX-Con

However, to implement this, I assume you would need to split the script descriptors into 'base' and 'derived' $create() functions, so that you can initialise the base with a reference to a valid instance.

The Telerik designer server-control (that you derive from) is already setting up a ScriptControlDescriptor in base.GetScriptDescriptors() on the assumption that you are going to have a single client script... and will just add to it's descriptors for all your extras 'stuff'.

I assume that I would need to split that into a ScriptComponentDescriptor for the designerbase and a newed ScriptControlDescriptor for the derived class,  and this is where I'm getting a bit lost - the old "who's on first" problem. i.e. What should be loaded where, and what issues this might create.

Maybe it will all come to me in the shower tomorrow morning... LOL

Posted by Community Admin on 27-Sep-2012 00:00

It just occurred to me that this line of thinking may affect page load speed since it requires a blocking script, rather than a compiled class like we are used to in C#. One more script would need to be sent to the client and processed in order before the derived class. Since it only occurs on the backend and because it may improve your developer efficiency, it may be worth it. But this is not exactly speedy js to start with, so I would be sure to check that.
Another option would to be to make a snippet rather than your base class. You would still need to look at a mess of code, but you wouldn't have to retype a lot of boilerplate. This is sort of the Thunder appproach I suppose.

That said, it is still a curiosity for me and I will let you know if I solve the original question.

Posted by Community Admin on 29-Sep-2012 00:00

@Dan

When you are in page-edit mode, if you right-click on a designer's dialog and ask to view the source for the frame... scroll to the bottom to see all the $create statements... I don't think that spitting the designer into base and derived will make any difference to the load times... especially when it's all coming from a common webresource.

However... that's all by-the-way I think, as I suspect it's a no-go anyway.

I did some more work on this, and I actually managed to get separate 'base' & 'derived' script classes instantiated, but I don’t think it’s a valid structure and/or solution.

Basically, I fetched descriptors from base.GetScriptDescriptors() and then modified the descriptor type (so that it would render a $create referencing my base class) and added a few more descriptors to it for standard UI components (e.g. my main tabstrip) and common properties.

I then added a 2nd ScriptControlDescriptor for the derived class, and loaded all of the descriptors unique to that designer (e.g. all of the unique RadControls I might be using) which rendered the 2nd $create() referencing the derived designer class.

Both base and derived script classes load as expected, but then the problems start...

First, there is the issue of initializing the base from somewhere other than the derived class – which is clearly not logical – and means you need to cancel the base initializer in the derived class to stop it killing all the pre-loaded properties.

Second, there is the issue of where and when the refreshUI() and applyChanges() methods run... base, derived, both ?

The thing ends up in a confused mess and clearly, this is the wrong (probably even invalid) approach, and bottom line, I don’t think it will work.

So, I’ve returned to my current approach of moving all common logic and UI handling code into a stand-alone Component script, instantiate it from the designer, and then call the utility functions as I need them. This allows me to at least reduce the clutter to the minimum possible.

FWIW: I'm simply creating a utility script this way:

Type.registerNamespace("MyNamespace");
  
MyNamespace.MyClass = function ()
    if (typeof (Sys) !== 'undefined') Sys.Application.registerDisposableObject(this);
  
    // Constructor stuff goes here
  
MyNamespace.MyClass.prototype =
    initialize: function ()
    
        // Init stuff goes here
    ,
  
    dispose: function ()
    
        // Dispose stuff goes here
    ,
  
    otherMethodsEtc: function ()
    
    
  
,
  
MyNamespace.MyClass.registerClass('MyNamespace.MyClass', null, Sys.IDisposable);
  
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

So long as there is a scriptReference loaded for the utility class, I can just instantiate it as:

var designerUtils = new MyNamespace.MyClass();

or even stuff it into the window object, to make it available for the page duration.

This thread is closed