I think all of us would agree that Ext.Net provides too good (and a bit complex) ExtJs and ASP.NET integration. It exposes almost all ExtJs toolkit classes server-side in ASP.NET and many of the popular extensions too. In addition, it provides own custom components that are useful (e.g. MultiCombo, Linkbutton etc).

But as with all toolkits, a time would come when you would want to extend/enhance the functionality for your purpose. With Ext.Net, any enhancement you would like to perform to a component or any custom component you would like to create has 2 aspects: client-side and server-side.

Let’s say I want to create a specialized Panel with some additional config options or other features. The first thing I would do is to create the corresponding ExtJs component class in javascript, and you would find tonnes to examples of doing so on sencha.com as well as hundreds of blogs (including mine).

But the topic of this blog post is how to expose such a custom component server-side that would enable you to use the component in markup as you would do for a regular Ext.Net component and manipulate its properties in code-behind. This is something for which I haven’t found much information (almost no information) on web.

For the purpose of this blog post, we would be using the following custom Panel class and create a server-side Ext.Net component for it:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }Ext.ux.BlockPanel = Ext.extend(Ext.Panel, {
constructor: function (config) {

Ext.applyIf(config, {
addDefaultTools: true,
preventTitle: false
});

if (config.preventTitle) {
delete config.title;
}

if (config.addDefaultTools) {
config.tools = config.tools || [];
config.tools.splice(config.tools.length, 0,
{ id: ‘gear’, qtip: ‘Settings’, handler: this.toolSettingsClicked },
{ id: ‘close’, qtip: ‘Close’, handler: this.toolCloseClicked }
);
}

Ext.ux.BlockPanel.superclass.constructor.call(this, config);
},

toolSettingsClicked: function (event, toolEl, panel, tc) {
//Your code here
},

toolCloseClicked: function (event, toolEl, panel, tc) {
//Your code here
}
});

Ext.reg(‘ext.ux.blockpanel’, Ext.ux.BlockPanel);{/syntaxhighlighter}

I have tried to keep the example simple. In one of my projects, I needed a Panel with pre-defined tools and no title at lots of places, and decided to do it with a custom Panel derived component. The above example is a simplified version of the same Panel class from my application.

Now we would create a custom server-side Ext.Net component corresponding to this BlockPanel that would allow us using it in ASP.NET markup as well as in code-behind to set its additional config options.

Here’s the corresponding Ext.Net server component class:

 

{syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }namespace Rahul.UI.ExtNet
{
[Meta]
public class BlockPanel : Ext.Net.Panel
{
#region Public Properties
public override string XType
{
get
{
return “ext.ux.blockpanel”;
}
}

public override string InstanceOf
{
get
{
return “Ext.ux.BlockPanel”;
}
}

/// <summary>
/// True to add the default tools (settings, close etc.) to the Block Header, false otherwise. Defaults to true.
/// </summary>
[Meta]
[ConfigOption]
[Category(“6. Panel”)]
[DefaultValue(true)]
[NotifyParentProperty(true)]
[Description(“True to add the default tools (settings, close etc.) to the Block Header, flase otherwise. Defaults to true.”)]
public virtual bool AddDefaultTools
{
get
{
object obj = this.ViewState[“AddDefaultTools”];
return (obj == null) ? true : (bool) obj;
}
set
{
this.ViewState[“AddDefaultTools”] = value;
}
}

/// <summary>
/// True to prevent Block Title from rendering. If AddDefaultTools is also false, the Block Header would not render at all.
/// </summary>
[Meta]
[ConfigOption]
[Category(“6. Panel”)]
[DefaultValue(false)]
[NotifyParentProperty(true)]
public virtual bool PreventTitle
{
get
{
object obj = this.ViewState[“PreventTitle”];
return (obj == null) ? false : (bool) obj;
}
set
{
this.ViewState[“PreventTitle”] = value;
}
}

[Meta]
[ConfigOption]
[Category(“6. Panel”)]
[DefaultValue(true)]
[NotifyParentProperty(true)]
public int ColSpan
{
get
{
object obj = this.ViewState[“ColSpan”];
return (obj == null) ? 2 : (int) obj;
}
set
{
this.ViewState[“ColSpan”] = value;
}
}

[Meta]
[ConfigOption]
[Category(“6. Panel”)]
[DefaultValue(true)]
[NotifyParentProperty(true)]
public int RowSpan
{
get
{
object obj = this.ViewState[“RowSpan”];
return (obj == null) ? 1 : (int) obj;
}
set
{
this.ViewState[“RowSpan”] = value;
}
}

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[System.Xml.Serialization.XmlIgnore]
[Newtonsoft.Json.JsonIgnore]
public override ConfigOptionsCollection ConfigOptions
{
get
{
ConfigOptionsCollection list = base.ConfigOptions;

list.Add(“addDefaultTools”, new ConfigOption(“addDefaultTools”, null, true, this.AddDefaultTools));
list.Add(“preventTitle”, new ConfigOption(“preventTitle”, null, false, this.PreventTitle));
list.Add(“colspan”, new ConfigOption(“colspan”, null, true, this.ColSpan));
list.Add(“rowspan”, new ConfigOption(“rowspan”, null, true, this.RowSpan));

return (list);
}
}
#endregion
}
}{/syntaxhighlighter}

Now let’s discuss each portion of the above class as well as my approach towards creating it that should provide enough pointers to enable you to create such (and more complex) Ext.Net components:

  1. I used Ext.Net’s TabPanel and TabPanelBase as a reference. I haven’t found any documentation for creating custom Ext.Net components and therefore resorted to analyzing the toolkit code itself to figure out how to do it.
  2. In Ext.Net, you would always find 2 classes for an ExtJs component, one a abstract base class and second the concrete component class (e.g. TabPanelBase and TabPanel classes for ExtJs’ TabPanel).
    This relates to some ASP.NET parsing constraints that I had earlier discussed with the Ext.Net team here, you might find that thread useful.
  3. At a very minimum, you need to atleast override 2 properties for your custom Ext.Net component: InstanceOf and XType. The first is the fully-qualified javascript class name for yoour ExtJs component, and the second is its xtype registered with Extjs. InstanceOf is used during direct instantization of a component by Ext.Net, whereas XType is used for lazy instantization usually in layouts.
  4. My first approach was to just put in the other config options in the server-side class decorated with proper attributes. I had hoped this would automatically put the property and its value in the config options for the constructor in javascript when an instance is created for the component.

    {syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }[Meta]
    [ConfigOption]
    [Category(“6. Panel”)]
    [DefaultValue(false)]
    [NotifyParentProperty(true)]
    public virtual bool PreventTitle
    {
    get
    {
    object obj = this.ViewState[“PreventTitle”];
    return (obj == null) ? false : (bool) obj;
    }
    set
    {
    this.ViewState[“PreventTitle”] = value;
    }
    }{/syntaxhighlighter}
    But it did not work out. When I set the value of this property either in markup or in code-behind, that value was not sent to the client in javascript.

  5. So I turned exploring other classes in the Ext.Net framework, and stumbled upon the Factory folder in Ext.Net project, which further contained Builder, Config and ConfigOptions folders.
    Each of these further had a associated code file for TabPanelBase and TabPanel classes. And interestingly enough, the corresponding code files in various folders contain the same partial class distributed across multiple files.

    So, in Ext.Net’s project structure, the following files:
    Ext.Net\Ext\TabPanel.cs
    Ext.Net\Factory\Builder\TabPanelBuilder.cs
    Ext.Net\Factory\Config\TabPanelConfig.cs
    Ext.Net\Factory\ConfigOptions\TabPanelConfigOptions.cs

    all contained the same partial class split across multiple files. Also, there were separate DirectEvent and Listeners classes for TabPanel in:
    Ext.Net\Factory\ConfigOptions\TabPanelDirectEventsConfigOptions.cs
    Ext.Net\Factory\ConfigOptions\TabPanelListenersConfigOptions.cs

  6. You might want to explore each of these files atleast once, and also corresponding classes for the TabPanelBase class. This would give you a pretty good idea of the framework and foundation on which Ext.Net works.
    My own personal opinion is that I see lots of code duplicacy across multiple files. The same config option is repeated many times over across files. Ext.Net team would know their design better, but a reduction in redundancy should certainly help maintenance as well as understanding of the toolkit code.
  7. Anyways, in this code file:
    Ext.Net\Factory\ConfigOptions\TabPanelBaseConfigOptions.cs

    I noticed that Ext.Net registers all additional config options for a Component in a public Property called ConfigOptions, as you can see in the following code:

    {syntaxhighlighter brush: as3;fontsize: 100; first-line: 1; }public override ConfigOptionsCollection ConfigOptions
    {
    get
    {
    ConfigOptionsCollection list = base.ConfigOptions;

    list.Add(“visibleIndex”, new ConfigOption(“visibleIndex”, new SerializationOptions(“activeTab”), -1, this.VisibleIndex ));
    list.Add(“animScroll”, new ConfigOption(“animScroll”, null, true, this.AnimScroll ));

    //More code lines
    return list;
    }
    }{/syntaxhighlighter}You would notice that this property collects the ConfigOptions from the base class in a list, and then adds its own options to this list.

  8. So I added the following property to my BlockPanel class:

    {syntaxhighlighter brush: csharp;fontsize: 100; first-line: 1; }[Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [System.Xml.Serialization.XmlIgnore]
    [Newtonsoft.Json.JsonIgnore]
    public override ConfigOptionsCollection ConfigOptions
    {
    get
    {
    ConfigOptionsCollection list = base.ConfigOptions;

    list.Add(“addDefaultTools”, new ConfigOption(“addDefaultTools”, null, true, this.AddDefaultTools));
    list.Add(“preventTitle”, new ConfigOption(“preventTitle”, null, false, this.PreventTitle));
    list.Add(“colspan”, new ConfigOption(“colspan”, null, true, this.ColSpan));
    list.Add(“rowspan”, new ConfigOption(“rowspan”, null, true, this.RowSpan));

    return (list);
    }
    }{/syntaxhighlighter}The first argment to the list.add method is the javascript property name with which your server property would be serialized, then a ConfigOption object with the property details (including its SerializationMode).

    And as soon as I did that and re-complied, I could see the config options set in markup and in code-behind being serialized to the client succesfully.

You would now register this component on the page using the ASP.NET’s <% Register %> directive, give the namespace or assembly a TagPrefix (like you would do to Ext.Net) and you can then use it in markup. Here’s an example:

 

 

<%@ Register Assembly="MyAssembly" Namespace="Rahul.UI.ExtNet" TagPrefix="rahul" %>


<rahul:BlockPanel runat="server" ID="pnlBlock" AutoWidth="225" Collapsible="true" AddDefaultTools="false" PreventTitle="true" />

 

 

In a nut-shell, to create custom ExtJs components and expose them as Ext.Net server components, you create your ExtJs Component class, the corresponding server component class derived from a suitable Ext.Net component class, add properties corresponding to your additional config options (and applying them with the attributes as in the above BlockPanel class), and register all those properties by overriding the ConfigOptions property. While overriding ConfigOptions, a very important thing to remeber is to add your options to base class options (and not replace them). So, they should be added to the list returned by a call to base.ConfigOptions.

Please note that I have covered creating custom components very much from the surface. There are many in-depth things I have left like invoking custom methods on your new component (which can be done in various ways like XControl.AddScript, XControl.Call and various other script registration methods available), adding custom DirectEvents or Listeners (for which this forum thread should provide a good understanding on how to do it and the gotchas involved), setting Properties in DirectEvents/DirectMethods etc.

A blog post covering all these aspects would stretch too long, and you might probably be better having a look at the above refeenced code files to see how Ext.Net does these things itself (you might need to create custom ConfigBuilders, nested Config classes and other such things if you want to exploit everything available out there in Ext.Net using your custom component).

I only tried to cover the basic features of adding Config Options to custom components which is what I do most of the time myself (very rarely do I need to provide a wrapper for invoking a custom javascript method on the component from server). And DirectEvent And DirectMethod related functionality is something I almost never incorporate in custom components because as a rule of thumb, I almost always avoid DirectEvents and DirectMethods in favor of javascript listeners and manual server interaction via web-service methods or .ashx handlers.