RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Custom Web Controls Demystified, Part 2 : Page 2

In Part 1 you learned how to build a custom rendered control. In Part 2 you'll learn how to build a composite control.

Triggering Events
What good is having a button on a form if it doesn't do anything? Since I've taken time to build a custom Web control with a button as part of its elements, I now want to give that button some functionality. In a rendered control, you accomplish this by implementing a couple of interfaces. The button does not need to do any data checking, instead, it simply needs to trigger a postback and raise an event in the Web Form's code-behind class. In order to do this, I start by extending the control's class to implement the IPostBackEventHandler interface. This interface defines only one method called RaisePostBackEvent which receives a string argument. This method will get fired when a postback is triggered by one of the elements in the control.

In order to trigger a postback from the Web control, you need to do a couple of things. First, you must tell the button to trigger a page postback when a user clicks it. The HTML tag that you used to render the button is an "input" tag with a "type" attribute of "button." Inherently, this HTML tag can only raise a client event in its "onclick" attribute; no problem, that's exactly what you're going to do. Once again you're going to add another attribute to one of the HTML tags. This time it will be the "input" tag that gets rendered for the button element. The attribute you need to add to this tag is the "onclick" attribute whose value should contain Jscript code to execute when the user clicks the button.

When the ASP.NET parser processes an ASPX Web Form to render to a browser, it also builds a Jscript function that handles the postback to the server. This function is normally called by any control that needs to trigger a postback. You don't really need to know the name of this Jscript function because .NET provides a method call that will generate it (though if you view the source of any rendered ASPX page, you will see this function which is called "__doPostBack"). This is good in case the function name changes in future versions of .NET. The method that generates the Jscript call is called GetPostBackEventReference and it sits off the Page object, which incidentally is accessible from the control's class. The two arguments you need to send to this method are the calling class (the control's class) and an identifier that identifies the button element. This identifier is what gets sent into the RaisePostBackEvent method that was defined by the IPostBackEventHandler interface. As before, you add the new attribute to the button's "input" tag before the "input" tag is rendered.


   output.AddAttribute( _
      HtmlTextWriterAttribute. _
      Onclick, Page.GetPostBackEventReference( _
      Me, "button"))
In C#:

      this, "button")) ;
As you can see, the word "button" is chosen for the identifier of the button element. This will get passed into the RaisePostBackEvent method when the page is posted back. Your control is now ready to handle postbacks. Clicking the button will now call the RaisePostBackEvent method, sending the word "button" into its argument. The only problem is that you haven't told this method to do anything yet, so let's wire in an event to raise to the page.

You can create custom composite controls that contain other custom composite controls, thus creating a control tree. Remember however, that the deeper you get the more performance-heavy your control will get.
When you click on a regular button control on a Web Form, you trigger a Click event on the page's code-behind class. You're going to create a ButtonClick event that will get raised on the page's code-behind class when the button on the FormField control gets pressed. Let's start by declaring a ButtonClick event using the standard EventHandler delegate.


   Public Event ButtonClick As EventHandler
In C#:

   public event EventHandler ButtonClick;
This is the event that will be raised in the RaisePostBackEvent method. To make sure that this event gets raised only when the button is pressed, you'll need a condition-check against the value that was used when the button element was rendered.


   Public Sub RaisePostBackEvent( _
      ByVal eventArgument As String) Implements _
      Select Case eventArgument.ToLower()
      Case "button"
         RaiseEvent ButtonClick( _
            Me, New EventArgs)
      End Select
   End Sub
In C#:

   public void RaisePostBackEvent(
      string eventArgument)
      switch (eventArgument.ToLower())
      case "button" :
         if(this.ButtonClick != null)
               this, new EventArgs());
It's a good idea to use a Case statement (switch in C#) as opposed to an If statement. This sets you up for any future enhancements to your control.
It's a good idea to use a Case statement (switch in C#) as opposed to an If statement. This sets you up for any future enhancement to your control. Now that you have an event wired up to the button element, let's make it the default event for the control. This will allow programmers that use your control to double-click on it while in design mode on a Web Form, and have the code-behind come up with the ButtonClick all coded up and ready to go. To do this you need to decorate the class declaration with the DefaultEvent attribute and send into its constructor, the string "ButtonClick" (code not shown).

You're not done with events yet. You need to create a TextChanged event to capture changes in the textbox, much like the one that comes with the regular Textbox Web control. This event is a bit different because upon the page postback, you're going to need to check if the value in the textbox has changed before you raise it. You should know that you have to declare the event so go ahead and do that at the top of the class.


   Public Event TextChanged As EventHandler
In C#:

   public event EventHandler TextChanged;
As you can see, this code will use the default EventHandler delegate as well. For neither of these two events do you need to create a new delegate and event argument object, so you're fine with using the default one. In the case where you needed to send information to the event, you would use a custom event argument object and delegate as you would in any case where you're using events. Now that the event is declared, you need to raise it somewhere. You need to implement an interface that will allow you to check posted values for elements in your control; it's called IPostBackDataHandler and it implements two methods: LoadPostData and RaisePostDataChangedEvent.

The LoadPostData method gets called during a postback and receives data from the elements on the Web Form. This data can be checked against properties in your control to check for changes or anything else that may be required. This is the essence behind the ability to check for text changes in the textbox. The code in the LoadPostData event will look at the data that was posted from the textbox and compare it against the value of the Text property, which may be different from that of the actual textbox on the form.


   Public Function LoadPostData( _
      ByVal postDataKey As String, _
      ByVal postCollection As NameValueCollection)
      As Boolean _
      Implements IPostBackDataHandler.LoadPostData
      Dim s_CurrentValue As String = _
      Dim s_PostedValue As String = _
         postCollection(Me.UniqueID & _
      Dim s_Button As String = _
         postCollection(Me.UniqueID & _
      Dim b_ButtonClicked As Boolean = _
         (Not s_Button Is Nothing AndAlso _
         s_Button.Length <> 0)
      If b_ButtonClicked Then
      End If
      If (Not s_CurrentValue.Equals( _
          s_PostedValue)) Then
         Me.Text = s_PostedValue
         Return True
      End If
      Return False
   End Function
In C#:

   public bool LoadPostData(
   string postDataKey, 
      NameValueCollection postCollection)
      string s_CurrentValue = this.Text;
      string s_PostedValue = 
      string s_Button = 
         postCollection[this.UniqueID + _
      bool b_ButtonClicked = 
         (s_Button != null) && 
         (s_Button.Length != 0);
         this.Text = s_PostedValue;
         return true;
      return false;

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date