Custom Control, Postback and Events
Sometime back I was required to write a Custom Control, with some custom Events and some advanced functionality. Since I was not too familiar with the "Art of Writing Controls", I was searching through Google, to get some useful material. After spending sometime, I realized that there are a lot of blogs, articles are out there on custom controls, but none of them are complex and realistic, most of them are "Hello World" kind of an example. Moreover, I found that different people are using some different ways of achieving the same thing. So in this blog I will try to explain what I have learned from that experience, but I may add up to another different approach. Sorry for that. :D
In this post I will try to explain few concepts about Custom controls, mostly related to Postback and Raising Events.
I will use example of a Custom Control, that will not do much of the activities, but it will contain 2 buttons, and it will expose 2 events, one each for each of the button.
Event Handlers
Now to start with the very basic of Event handling in a Custom Control, we need to define a List of type EventHandlerList . EventHandler List will help us to store the Event Handler's associated with each of the Events.
protected EventHandlerList _lstEventHandlers = new EventHandlerList(); |
Since our control exposes two Events, we will define two key values, so that we can use them as Key values.
static readonly object _prevEventKey = new object(); static readonly object _nextEventKey = new object(); |
So far we have defined the EventHandlerList and two key values, to use to store the EventHandlers in the List. Now we will define the two Events, those will be Exposed to the Page, that uses this Control.
Remember, how we add EventHandler to an Event? Offcourse using the +=. If we want to expose those two events as Public Properties, then how they should look like? Hmmmm... surely we won't be using get;set;. Rather than that we will be using add; and remove; Sounds a bit confusing ??? Let's look into the code snippet.
public event EventHandler onPreviousButtonClick { add { _lstEventHandlers.AddHandler(_prevEventKey, value); } remove { _lstEventHandlers.RemoveHandler(_prevEventKey, value); } } |
Similarly for the Next Button Click, we will define another Event say "onNextButtonClick"
public event EventHandler onNextButtonClick { add { _lstEventHandlers.AddHandler(_nextEventKey, value); } remove { _lstEventHandlers.RemoveHandler(_nextEventKey, value); } } |
Now we will look into most important section, how to wire up PostBack and how to Raise Events.
Since I have two buttons, in the CreateChildControls() function, will add two LinkButtons, and give them ID's.
LinkButton _lnkNext = new LinkButton(); _lnkNext.ID = "NextButton"; LinkButton _lnkPrev = new LinkButton(); _lnkPrev.ID = "PrevButton"; |
Why we require ID's? hmmm, we will require these ID's sometime later. One quick point here, whenever you are creating any Custom Control, better to Implement "INamingContainer" interface. So far we have defined the events, and created the controls. But we haven't added any code to write the famous "__doPostback" function. So in the CreateChildControl method we will add the following code.
_lnkNext.Attributes.Add("href", Page.GetPostBackClientHyperlink(_lnkNext, this.ClientID)); _lnkNext.Attributes.Add("name", _lnkNext.ClientID); _lnkPrev.Attributes.Add("href", Page.GetPostBackClientHyperlink(_lnkPrev, this.ClientID)); _lnkPrev.Attributes.Add("name", _lnkPrev.ClientID); |
Postback
The above code snippet will add the Hyperlink and it will make sure that it will do a Postback. Now we will Use "IPostBackDataHandler" interface so that we can notify ASP.NET engine that this Control Participates in PostBack, and this control will have an opportunity to the values and decide whether to Raise any Event or Not.
This interface defines two methods,
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection); public void RaisePostDataChangedEvent(); |
The first method will fire just after Page_Init, but before Page_Load. If the PostBack is caused by our control, then we will return true, else we will return false. So in LoadPostData, we will write the below code snippet.
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection) { string _target = postCollection["__EVENTTARGET"]; string _arg = postCollection["__EVENTARGUMENT"]; if (_arg == this.ClientID) { if (_target == "NextButton") { //Property, to track which button was clicked. this.NextButtonClicked = true; } Page.RegisterRequiresRaiseEvent(this); return (true); } else { return (false); }
} |
In the above code snippet "__EVENTTARGET", and "" are two values, those were set from the __doPostBack javascript function. And the "Page.RegisterRequiresRaiseEvent(this);" line will notify the page that, it needs to fire the "RaisePostDataChangedEvent()" method.
In the "RaisePostDataChangedEvent" method, we will simply raise the Events, for the corresponding buttons.
public void RaisePostDataChangedEvent() { EventHandler prevHandler = (EventHandler)_lstEventHandlers[_prevEventKey]; if (prevHandler != null) { prevHandler(this, EventArgs.Empty); } EventHandler nextHandler = (EventHandler)_lstEventHandlers[_nextEventKey]; if (nextHandler != null) { nextHandler(this, EventArgs.Empty); } } |
Hmmm, we are done..., a Control with multiple Events, is Done. Hope this post will help you.
Question: Why we are firing the Event in "RaisePostDataChangedEvent()", what will happen if we raise the Event in "LoadPostData"?