Putting Anything in a Cell? - Custom Editor Provider for WebDataGrid

Have you already considered the RowEditTemplate and Templated Column features but would rather use a custom editor provider?  Do you want to use a WebHtmlEditor to edit a cell?  How about a chart?  Well our WebDataGrid was built from the ground up to allow customer to do such customization without having to rewrite our control.  In this post, I am going to show you the basics in making the Microsoft DropDownList become an editor provider for the WebDataGrid.  If you aren’t interested in the details, here’s how you would use my DropDownProvider class in your WebDataGrid:

 

protected void Page_Load(object sender, EventArgs e)
{
    //reference to the custom provider
    DropDownProvider ddprov = null;

    if (!IsPostBack)
    {
        //creates a new provider and adds it to the grid
        ddprov = new DropDownProvider();
        ddprov.ID = "DropDownProvider1";
        this.WebDataGrid1.EditorProviders.Add(ddprov);
        //hooks up the provider to the column by key
        EditingColumnSetting colSet = new EditingColumnSetting();
        colSet.ColumnKey = "Country";
        colSet.EditorID = ddprov.ID;
        this.WebDataGrid1.Behaviors.EditingCore.Behaviors.CellEditing.ColumnSettings.Add(colSet);
    }
    else
    {
        //gets a reference to the provider when posting back
        foreach (EditorProviderBase epd in this.WebDataGrid1.EditorProviders)
        {
            if (epd.ID == "DropDownProvider1")
            {
                ddprov = (DropDownProvider)epd;
            }
        }
    }
    //gets the DropDownList and binds it
    DropDownList ddl = (DropDownList)ddprov.GetEditor();
    ddl.DataSource = getCountries();
    ddl.DataBind();
    ddl.ID = "editor1";
}

 

The above code is similar to this documentation on using the built in editor providers.  The difference being that there is no design time support.  If you want the sample, it is at the bottom of this post.  If  you want to know how this is done so that you could do one of your own, read on.

Where to Start

Getting started is one of the hardest things.  Without a guide on how to do something, it is a lot of trial and error.  When I started on this, I was thinking to myself “how am I supposed to do this?”  Then I realized, we have ten editor providers that shipped with the product!

Looking at the Source Code

As long as you have a subscription to our product, you have the source code for it.  If you want to follow along, go get it in your downloads section of our website

Looking at the source code is not easy.  You really have to invest the time to understand it.  All of our ASP.NET AJAX controls are in the Infragistics.Web assembly.  Once you open that solution, and go to the Infragistics35.Web.UI project,  navigate to the GridControls –> WebDataGrid –> EditorProviders folder.  In there you will find the code for how we did our editor providers.  I looked through a couple of them and noticed that the one for the WebSlider was fairly simple.  All it did was inherit from EditorProvider<Type> and overrode two items.  I did the same, hooked up my custom provider to the grid and ran the project.  I double clicked on a cell and there was my dropdown list being used as a provider.

public class DropDownProvider : EditorProvider<DropDownList>
{
    //the types that this editor provider can edit
    protected override bool CanEditType(Type t)
    {
        string name = t.FullName;
        if (t.Name == "String")
            return true;
        return false;
    }
    //class name that defines behavior on the client side
    protected override string ClientObjectClassName
    {
        get
        {
            return "Infragistics.Web.UI.DropDownProvider";
        }
    }
}

The Client Side

I was ecstatic that such little code was needed to get my control in the cell.  My enthusiasm soon died when I realized that I have to write the JavaScript to send data to and from my DropDownList and the grid.  I went back to the source code and found the relevant code in igWebDataGridEditorProviders.js under the GridControls –> WebDataGrid –> js folder.  In there, I saw that certain methods were overridden depending on the editor provider.  I copied some of the code and changed the class names and ran it.  Not so lucky this time.  I ran into some JavaScript errors.  Apparently, my JavaScript needed to be executed after the initialization of our code because of namespaces.  I moved the link to my JavaScript file to the end of the body section.  After I got past that error, I started debugging the scripts and wrote the code specific to my DropDownList.  I made the most changes to get_value, set_value, and _show methods.  The get_value and set_value methods are called when passing the values to and from the grid to the editor provider.  I changed this to work with the selectedindex property.  The _show method, called when showing the editor, was modified to find the grid’s table element.  This is necessary so that the control will be hidden when clicking in the grid region, ending edit mode.  After these mild changes, the values were getting passed correctly to and from the editor and my editor provider was done or so I thought…

    $IG.DropDownProvider.prototype =
{
    get_value: function() {
        /// <summary>
        /// Overrides the EditorProvider object's property.
        /// Gets/sets the value to/from the underlying DropDownList. 
        /// </summary>
        /// <returns type="Object">Current value in editor.</returns>
        var val = this._editor ? this._editor[this._editor.selectedIndex].text : this._old;
        return this._areEqual(val, this._old) ? this._old0 : val;
    },
    set_value: function(val) {
        /// <summary>Sets initial value in editor.</summary>
        /// <param name="val">Value for editor.</param>
        this._old0 = this._old = val;
        var editor = this._editor;
        if (!editor)
            return;
        for (var x = 0; x < editor.length; x++) {
            if (editor[x].text == val) {
                editor.selectedIndex = x;
            }
        }
        this._old = editor[editor.selectedIndex].text;
    }

More Changes

This sample really started because of a support case.  After a few days, my solution came back with a problem.  It would give an exception when posting back.  Looking through the stack trace, I traced it back to a GetType method call on the full name of my custom class.  Since the GetType method will only look in the current assembly (Infragistics.Web), that method was returning null and made the application crash.  After some additional digging, the origin of the class name was traced back to a member of ICollectionObject called GetObjectType.  I implemented that interface and overwrote that method to return the full name in addition to the assembly.  Done! Right? Nope.  The assembly name seemed to contain the ‘~’ character.  Since, we use that character internally as a separator, I moved my class to a separate class library.  It compiled, ran and everything was fine. 

string ICollectionObject.GetObjectType()
{
    return (this.GetType().FullName + ", " + this.GetType().Assembly.FullName).Replace('.', '~');
}

Cleaning it up

Since it was already being separated into a separate class library, it didn’t make much sense to have to link JavaScript files in my ASP.NET website.  So I moved that file and started the process of registering it as a resource in my code behind of my aspx.  I never had to embed resources before so I had to do some digging to find out how to do it.  It is fairly straight forward, you need to set an WebResource attribute in your class and you have to set the BuildAction property on the file to be an Embedded Resource.  I used the ScriptReference object and added it to my ScriptManager.  I ran it and got JavaScript errors due to timing.  I decided to wrap my JavaScript definitions in a method and used RegisterStartupScript to execute my method to initialize my editor provider on the client side.  Happiness returned momentarily as the application runs without issues.

[assembly: System.Web.UI.WebResource("CustomProviders.js.CustomProviders.js", "text/javascript")]

Never really done

I realized that I may have moved the link to the script out of the aspx but it was still in the application.  I looked for a way to remove all references to my script from my application and came across the InitializeEditor method.  After realizing I needed a reference to the Page and ScriptManager, I made a constructor that received those two items as parameters.  You probably know what going to happen next.  You guessed it.  It works until I posted back.

The error message read something like “The object does not have a parameterless constructor”.  Sigh.  If I can’t pass in my references to the page and scriptmanager, how am i going to register my script?  The only link I have to my parent (the grid) was internal and I did not have access in my class.  I had a reference to my editor but it did not yet belong to the page.  After some tinkering, I decided to leverage the DropDownList and handle its PreRender event.  At that time, it was late enough that everything was added to the page.  I used the static GetCurrent method off the ScriptManager class to obtain the current ScriptManager and I was finally done.

protected override void InitializeEditor()
{
    ((DropDownList)this.EditorControl).PreRender += new EventHandler(DropDownProvider_PreRender);
    base.InitializeEditor();
}

void DropDownProvider_PreRender(object sender, EventArgs e)
{
    Page page = this.EditorControl.Page;
    ScriptManager sm = ScriptManager.GetCurrent(page);

    ScriptReference sr = new ScriptReference("CustomProviders.js.CustomProviders.js", this.GetType().Assembly.FullName);

    sm.Scripts.Add(sr);
    ScriptManager.RegisterStartupScript(page, typeof(Page), "initDDP", @"<script type='text/javascript'>initDropDownProvider();</script>", false);
}

Now you know

Not only do you know the basics to get started in making one of your own editor providers but you also saw the process that goes into a support case that is on the complicated side.

Possible Improvements

There are probably a few things that can be done to improve this.  Leave comments below with your thoughts.  The purpose of this post was to get you started if you needed to use an editor provider over a Templated Column or the RowEditTemplate feature.  With this outline, you really should be able to use any control as an editor provider.  You may run into issues but at least it won’t be the ones I ran into.

Sample uses NetAdvantage 8.3.20083.1009 CLR 3.5 and Visual Studio 2008

Full Sample (contains website and code library project, both must reference the same version of NetAdvantage)

Just my CustomProviders dll that contains my DropDownProvider (references 8.3.20083.1009 CLR 3.5)


Comments  (2 )

urs_ck
on Thu, Dec 3 2009 4:31 AM

i tried this sample in v9.2 version of WebDataGrid im getting a javascript error. Can you please confirm whether this works in the new version of WebDataGrid?

Thanks

[Infragistics] Sung Kim
on Mon, Dec 7 2009 3:50 PM

@urs_ck

You are probably gettin a js error because the namespace for my javascript, "dropdownprovider", is the same for the one we released in 9.1. If you are using this article to get a dropdownprovider, this article is no longer necessary since we released one.  If you are using this for making your own editor provider, then I would suggest using a different javascript namespace.

Add a Comment

Please Login or Register to add a comment.