Posts Tagged ‘custom control’

Create Custom Event Handlers & Arguments For Controls

July 28th, 2009

I was hoping to get this post up earlier, but got distracted by a couple of things. First, I jacked up my hand trying to separate my dogs from fighting to the point I couldn’t even move my ring finger (long story about the dogs, but I love ‘em). Second, I started a new job at Groove Commerce on Monday. So far, so good. I feel it’ll keep me on my game and give me some nice challenges ahead.

A few entries ago we talked about custom server controls and mentioned how to structure a composite control to allow the ViewState to “pick up” on it’s post back content so that data isn’t re-binded with every call. It was a very basic composite control, but it showed how to add native WebControl objects to the Controls tree to create a basic layout without using a .ASCX design file. This type of control can be added to your app_code directory or manually compiled as an assembly and placed into your bin directory. The advantage of the latter is to feasibly add it to your Visual Studio toolbar by loading the assembly. This time, we’ll add a custom event to our control using a delegate for an event, as well as a custom event argument class to pass along with it.

We’ll take the code from last time, but add several things which are highlighted in green.

  • A line to declare our event delegate variable and the delegate itself.
  • A class which will be used for our arguments.
  • A binded event for the button Click.
  • A line in the button Click handler to invoke the event delegate.
using System;
using System.Web;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace RyanControls
{
    public class MyCompositeControl : CompositeControl
    {
        public MyCompositeControl() { }

        // Declare the Updating event of delegate type CustomControlUpdateHandler
        public event CustomControlUpdateHandler Updating;

        protected override void CreateChildControls()
        {
            TextBox txtMyData = new TextBox();
            DropDownList drpMyList = new DropDownList();
            Button btnUpdate = new Button() { Text = "Update!" };

            Controls.Add(drpMyList);
            Controls.Add(txtMyData);
            Controls.Add(btnUpdate);

            if (!Page.IsPostBack)
            {
                drpMyList.Items.Add(new ListItem("item 1"));
                drpMyList.Items.Add(new ListItem("item 2"));
                drpMyList.Items.Add(new ListItem("item 3"));
            }

            // Add a handler for the button click to launch the Updating event
            btnUpdate.Click += new EventHandler(FireEvent);
        }

        // The method that handles the Click event from the button
        // and triggers the Updating event by calling the delegate
        protected void FireEvent(Object s, EventArgs e)
        {
            Updating(s, new CustomControlUpdateEventArgs() { SomeData = "The control was updated!" });
        }
    }

    // Our custom event class that inherits EventArgs. You may use
    // EventArgs as the class if you don't have additional data to pass
    public class CustomControlUpdateEventArgs : EventArgs
    {
        public CustomControlUpdateEventArgs() { }
        public String SomeData;
    }

    // The delegate declaration (or template, as I call it) for the handler
    public delegate void CustomControlUpdateHandler(Object s, CustomControlUpdateEventArgs e);
}

There you have it. If you place this code into a .CS file inside your app_code directory, you’ll be able to include the control on your pages. If you compiled it into an assembly, toss it into your bin directory and feel free to add it to your toolbar in VS or VWD.

<asp:MyCompositeControl runat="server" ID="cntMyControl" OnUpdating="DisplayUpdate" />

By assigning an event handler for the Updating event (the page automatically prepends the “On” for “OnUpdating”) to call a method to handle the update, you can use data contained in the CustomControlUpdateEventArgs object. In this case, there’s only a public string called SomeData.

protected void DisplayUpdate(Object s, RyanControls.CustomControlUpdateEventArgs e)
{
    Response.Write(String.Format("<div>From the event handler: {0}</div>", e.SomeData));
}

When you run the code and click the button, the method will be invoked and the message displayed. Similarly, you can bind the handler programmatically by adding cntMyControl.Updating += new RyanControls.CustomControlUpdateHandler(DisplayUpdate) in your code-behind.

custom_event

So again, while the code isn’t really useful, it shows how to create your own event handlers and custom argument class(es) for a composite server control. The point is to be able to create one or more event handlers, as well as combine those with one or more types of event argument classes. The fields of that particular argument class should be specific to that type of event and it is best not to use one custom event args class to satisfy many types of event argsuments, unless the data being passed into your hander functions generic enough to do so.

Happy coding!

Extending A Native Web Control

July 9th, 2009

Some may not know the difference but user controls are different from server controls. User controls inherit from the UserControl class while server controls can inherit from, say, the Button, DropDownList, GridView, or, well, any Control or WebControl object. Using a server control is faster than using a user control since there is no designer UI to be compiled.

The main drawback from creating a server control versus a user control is the lack of building a rich UI in most respects. Because there isn’t a .ascx page to load existing controls onto, you don’t have this ability as easily as you do with a server control (unless you programmatically add controls to a placeholder object in a composite control).

The main difference is that your control must inherit from an existing Control or WebControl object, or one that inherits from these, such as a Button.

Here’s a quick example of a DropDownList that is pre-populated with data essential to determining sex – “Male” and “Female”. You can save this as a .CS file into your app_code directory. In this example, we are essentially extending the existing DropDownList control to add additional functionality.

using System;
using System.Web;
using System.Web.UI.WebControls;
using System.Data;

namespace CustomControls
{
    public class GenderList : DropDownList
    {
        public GenderList()
        {
            System.Collections.Generic.Dictionary<String, String> dctGender = new System.Collections.Generic.Dictionary<String, String>();

            dctGender.Add("MALE", "Male");
            dctGender.Add("FEMALE", "Female");
            dctGender.Add("NA", "No response");

            base.DataSource = dctGender;
            base.DataTextField = "value";
            base.DataValueField = "key";
            base.DataBind();
        }

        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            base.Render(writer);
        }
    }
}

Now, to use it on your page, you’ll need to either Register the control using the <%@ Register %> declaration or add a reference in the <pages><controls> … </controls></pages> section of your web.config.

<%@ Register TagPrefix="asp" Namespace="CustomControls" %>

…or in your web.config…

<pages>
    <controls>
        ....
        <add tagPrefix="asp" namespace="CustomControls"/>
    </controls>
</pages>

Notice we did not include the assembly. This is because it was not compiled into a .dll. If it is compiled into a .dll, you must include the Assembly name.

genderlist

After that, if you don’t feel like waiting for your intellisense to update, you can simply insert the control onto your page. The only difference is, you do not need to include the src attribute, since the control will be compiled (just the TagPrefix, and Namespace).

The class above is very simple. It inherits from DropDownList, so it has all the functionality of the native DropDownList class, but binds some data in the constructor. Pretty simple, but will save a lot of time when working with forms. If this seem a bit boring, another idea could be to have a StateList control that is populated with states from an XML file. Or, perhaps you find yourself always setting default attributes in your GridViews, such as GridLines=”none” or AutoGenerateColumns=”false”. Make your own and have these defaults set for you.