Creating a designer script baseClass - my head hurts
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 ??
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.
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?
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
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.
@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();