Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Architecture
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
Bing
BizTalk
Book Excerpts
Build and Deploy
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Design Patterns
Development Process
Display Technologies
Distributed Computing
DotNetNuke
DSL
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
Internet Explorer 8.0
Interviews
iPhone
Iron Ruby
Java
Java Script
jQuery
LINQ
Linux
Mac OS X
MDX
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
Network
NHibernate
Object Oriented Development
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
Podcasts
Post Mortem
PowerPoint
Print/Output
Product News
Product Reviews
Project Management
Python
Q&A
Rails
Rake
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Search
Security
Services
SharePoint
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server CE/AnyWhere/Mobile/Compact
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
Tips
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WF
Whitepapers
Windows 7
Windows Azure
Windows Live
Windows Server
Windows Vista
WinForms
Workflow
WPF
XAML
XML
XNA
XSLT



Hacker Halted


 


DevReach

Reader rating:
Click here to read 17 comments about this article.
Article source: CoDe (2005 - Nov/Dec)


Article Pages:  1  2 3 4 5 6 7 8 9 - Next >


Custom Web Controls Demystified, Part 2

Last issue I gave you a 'hit-the-ground-running' introduction to custom Web control development and showed you how to build a renered control and an inherited control. In this issue you'll complete your inherited control by adding styling, sizing capabilities, as well as instruct it how to raise events. Afterward you will jump into building the last control of the series, the EmailContact control, bringing together the previous two controls with some business functionality into a powrful composite Web control.

In the first part of this article (CoDe Magazine, September/October 2005) you learned how to create an inherited Web control, as well as a fairly functional rendered Web control. In part two of this article you’ll learn three professional touches for your custom Web control. First, you’ll learn how to make all parts of your custom control resize correctly. Next, you’ll learn how to capture an event when the button is clicked or when text in the textbox changes. Finally, you’ll learn how to add basic styling.

Control Sizing

When you drop any Web control on a form, you’re probably used to sizing it by dragging one of its sizing points in whatever direction you want. The problem here is that the Web control you’re building consists of three HTML elements, and you want to be able to size each one individually to give the FormField control maximum usefulness. The built-in Width and Height property that the control has comes from the Control class that you are ultimately inheriting from, and it corresponds to the control as a whole. If you try to resize the control using these properties as it currently stands, nothing happens. This is because you have not added sizing attributes to the contained elements, so they stay exactly as-is. I’m going to do something later with these existing properties, but for now I want you to add two properties called CaptionWidth and ButtonWidth. For the purposes of this article, I’m only going to show you how to deal with widths, but the downloadable code contains code to handle heights as well. I left out a width property for the textbox for a reason, as I’ll explain later.

"
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.
"

A property that handles height or width for an element is of type Unit. Here’s the code for the CaptionWidth property.

In VB.NET:

Public Property CaptionWidth() As Unit
   Get
      If CType( _
      ViewState("CaptionWidth"), Object) _
      Is Nothing Then
         Return Unit.Pixel(130)
      End If
      Return CType( _
      ViewState("CaptionWidth"), Unit)
   End Get
   Set(ByVal Value As Unit)
      ViewState("CaptionWidth") = Value
   End Set
End Property

In C#:

public Unit CaptionWidth 

   get
   {
      if (((object)
      ViewState["CaptionWidth"]) == null
         return Unit.Pixel(130); 
      return ((Unit)
      ViewState["CaptionWidth"]); 
   } 
   set 
   { 
      ViewState["CaptionWidth"] = value
   } 
}

As you can see, the same ViewState-oriented property technique is used here as described earlier in the article. The Unit object is serializable so it can be fully persisted in the ViewState variable. Now that you’ve added the new properties, you need to do something with them. Remember that earlier I taught you that you can use the AddAttribute method of the HtmlTextWriter object to add tag attributes to the upcoming RenderBeginTag call. That’s exactly how you’re going to set the Width attribute to the ‘span’ tag and ‘input’ tag for the button, with one minor difference I’ll explain in a minute.

output.AddStyleAttribute(
   HtmlTextWriterStyle.Width, 
   this.CaptionWidth.ToString());

And of course, you would have a similar line for the ButtonWidth property. Now you can set these properties individually to adjust the width of the caption and the button. As you realized, there’s one element of the Web control whose width I have not handled this way. In fact, there’s a reason I haven’t yet showed you how to handle this element. To make the control more programmer-friendly, I’m letting the textbox take the remaining width of the entire control; that is the total width of the Web control minus the width of the caption and the button (and don’t forget those two spaces you inserted between elements-calculated to be 10 pixels). The value that you’re going to use to set the width of the textbox will consist of the total width of the control (the Width property) minus the value of CaptionWidth, minus the value of ButtonWidth, and minus 10. The subtraction of the ButtonWidth value will depend on the setting of the ButtonVisible property and the number 10 accounts for the extra spaces rendered between the elements. I derived the number 10 by trial and error to see what looked best in the designer. You’ll use the calculated value to set the Width attribute of the textbox’s “input” tag.

In VB.NET:

Dim i_Width As Integer = _
CType(Me.Width.Value, Integer) - 
(CType(Me.CaptionWidth.Value, Integer) - 10)

If Me.ButtonVisible Then
   i_Width -= CType( _
   Me.ButtonWidth.Value, Integer)
End If

If i_Width < 20 Then i_Width = 20

output.AddStyleAttribute( _
HtmlTextWriterStyle.Width, i_Width.ToString())

output.RenderBeginTag(HtmlTextWriterTag.Input)

In C#:

int i_Width = ((int)(this.Width.Value)) -
((int)(this.CaptionWidth.Value) - 10); 

if(this.ButtonVisible)
   i_Width -= ((int)(this.ButtonWidth.Value));

if (i_Width < 20)
   i_Width = 20; 

output.AddStyleAttribute(
HtmlTextWriterStyle.Width, i_Width.ToString());

output.RenderBeginTag(HtmlTextWriterTag.Input);

Notice that you’re also ensuring a minimum total width of 20 for the control. In the downloadable code for the finished FormField control (Figure 1), I also handle the Width property to account for percentages as well as pixel entry as its value. This will become necessary when I get to the composite control later, but for now I’m not going to worry about it. Another thing to note is that you’re using a different method from the AddAttribute you used before. The AddStyleAttribute takes care of adding the property, not as an attribute to the tag, but as an attribute within the HTML style attribute. Later when I address styling, I’ll touch on this some more.

Click for a larger version of this image.

Figure 1: FormField control in action.

If you try sizing this control on a Web Form now, you will see that the textbox stretches to the size of the entire control while leaving the caption and button the same width. Those elements will only be sized by setting their properties individually. Now that you can size the control properly, you’re going to add some actual functionality for handling postbacks and handling events.

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.

In VB.NET:

output.AddAttribute( _
HtmlTextWriterAttribute. _
Onclick, Page.GetPostBackEventReference( _
Me, "button"))

output.RenderBeginTag(HtmlTextWriterTag.Input)

In C#:

output.AddAttribute(
HtmlTextWriterAttribute.Onclick, 
Page.GetPostBackEventReference(
this, "button")) ;

output.RenderBeginTag(HtmlTextWriterTag.Input);

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 postbacked. 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.

In VB.NET:

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.

In VB.NET:

Public Sub RaisePostBackEvent( _
   ByVal eventArgument As StringImplements _
   IPostBackEventHandler.RaisePostBackEvent
   Select Case eventArgument.ToLower()
      Case "button"
         RaiseEvent ButtonClick( _
         MeNew EventArgs)
   End Select
End Sub

In C#:

public void RaisePostBackEvent(
   string eventArgument)
{
   switch (eventArgument.ToLower())
   {
      case "button" :
         if(this.ButtonClick != null)
            this.ButtonClick(
            thisnew EventArgs());
         break;
   }
}

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.

In VB.NET:

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.

In VB.NET:

Public Function LoadPostData( _
   ByVal postDataKey As String, _
   ByVal postCollection As NameValueCollection)
 As Boolean _
Implements IPostBackDataHandler.LoadPostData

   Dim s_CurrentValue As String = _
      Me.Text
   Dim s_PostedValue As String = _
      postCollection(Me.UniqueID & _
      ":Field")
   Dim s_Button As String = _
      postCollection(Me.UniqueID & _
      ":Button")
   Dim b_ButtonClicked As Boolean = _
      (Not s_Button Is Nothing AndAlso _
      s_Button.Length <0)

   If b_ButtonClicked Then
      Page.RegisterRequiresRaiseEvent(Me)
   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 = 
      postCollection[this.UniqueID];
   string s_Button = 
      postCollection[this.UniqueID + _
      ":Button"];
   bool b_ButtonClicked = 
      (s_Button != null) && 
      (s_Button.Length != 0);

   if(b_ButtonClicked)
      Page.RegisterRequiresRaiseEvent(this);

   if(!s_CurrentValue.Equals(s_PostedValue))
   {
      this.Text = s_PostedValue;
      return true;
   }

   return false;
}
&

By: Miguel Castro

Miguel is an architect with IDesign who specializes in architecture consulting and building .NET solutions. He is a Microsoft MVP and INETA speaker and has been a software developer for over 22 years. With a Microsoft background that goes all the way back to VB 1.0 (and QuickBasic in fact), Miguel jumped on .NET as soon as the first public Beta was released and has provided .NET solutions for clients around the country in a variety of industries. He considers himself to be a .NET Developer and Architect and has equal love for both VB and C#, and no tolerance for language bigotry. He’s spoken at numerous user groups around the country as well as developer conferences.

He’s the author of the CodeBreeze code-generator, which among things can be found on his Web site:

www.steelbluesolutions.com

Miguel currently lives in Lincoln Park, NJ with his wife Elena and his daughter Victoria.

subscriptions@infotekcg.com

Fast Facts

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



Article Pages:  1  2 3 4 5 6 7 8 9 - Next Page: 'Events' >>

Page 1: Custom Web Controld Demystified, Part 2
Page 2: Events
Page 3: Events (con't)
Page 4: Styling
Page 5: Styling (con't)
Page 6: The EmailContact Control
Page 7: Compsite Control Properties
Page 8: Event Handling
Page 9: Other Associated Technologies
Page 10: ASP.NET 2.0

How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
4.0 out of 5

53 people have rated this article.

      CODE TRAINING

 

CODE TRAINING