Adding Properties
In addition to managing object state, properties allow the developer to set the state of a control during design time. By default, all properties appear in the Object Inspector when a control on the design surface is selected. This section explains how to create properties that are usable at design time.
The implementation in Listing 1 showed how to build a composite custom control, but it's very basic and limited in practical use. More useful would be the ability to change which author bio is displayed. We can do this by adding a property to the control, as shown in Listing 2.
Listing 2 Composite Control Properties
[Bindable(true), Category("Appearance"), DefaultValue("")] public string AuthorName { get { return authorName; } set { authorName = value; EnsureChildControls(); } } // returns bio for current author protected string Bio { get { EnsureChildControls(); LoadAuthorData(); AuthorBioInfo info = (AuthorBioInfo)authorBios[AuthorName]; string bio = "No Bio Specified"; if (info != null) bio = info.AuthorBio; return bio; } } // returns site of current author protected string AuthorSite { get { EnsureChildControls(); LoadAuthorData(); AuthorBioInfo info = (AuthorBioInfo)authorBios[AuthorName]; string site = "No Site Specified"; if (info != null) site = info.AuthorSite; return site; } }
The AuthorName property in Listing 2 sets a private class variable named authorName, which is used in subsequent actions to set up the control. Each of the properties includes a call to EnsureChildControls, which ensures that the child controls have been initialized and added to the Controls collection of the control properly. If the controls haven't been set up yet, this will call the CreateChildControls method.
When you drag-and-drop the AuthorBio control onto the design surface, the AuthorName property will appear in the Object Inspector. Type Jane Doe into it to make the control render properly. The other properties, AuthorSite and Bio, don't appear in the Object Inspector because they're read-only.
TIP
If you have a property that's read/write and you don't want it to appear in the Object Inspector, add a [Browsable("false")] attribute to the property.
The AuthorName property has a few attributes that control how it appears in the Object Inspector:
The Bindable attribute enables Object Inspector support for data binding.
The Category attribute lets you define the section in the Object Inspector where this property will appear.
The Default attribute communicates to the C#Builder IDE what the default value of this property should be. When you change the value of a property in the Object Inspector to something other than its default value, it will appear in a bold font. When set to its default value, the font is normal. Notice also that when the property is set to its default value, no attribute is added to the HTML for the control. I chose to set the default as a blank string ("") to force the attribute to be added to the control, which helps make sure that the control is rendered properly in the designer.
Notice that I called EnsureChildControls after initializing authorName in the set accessor of the AuthorName property. There was no harm because I wasn't accessing controls. Other constituent controls are initialized based on the value of AuthorName, as shown for AuthorSite and Bio in Listing 2; CreateChildControls also uses AuthorName to initialize controls. Because AuthorName is the first control member called, I didn't want to make a call to EnsureChildControls() that would call CreateChildControls with a null authorName. Also, the reason I called EnsureChildControls in the first place was to make sure that the control rendered properly in the designer when setting the AuthorName in the Object Inspector. Listing 3 shows the revised version of CreateChildControls.
Listing 3 Updated Version of CreateChildControls
// adds constituent controls protected override void CreateChildControls() { // title Controls.Add(new LiteralControl( "<p><i>About the Author</i></p><p><b>")); // author name lblAuthorName.Text = AuthorName; Controls.Add(lblAuthorName); // line break Controls.Add(new LiteralControl("</b></p><p>")); // author bio lblAuthorBio.Text = Bio; Controls.Add(lblAuthorBio); // line break Controls.Add(new LiteralControl("</p><p>")); // author site lkbAuthorSite.Text = "Visit " + AuthorName + "'s Web Site"; lkbAuthorSite.Click += new EventHandler(AuthorSite_Click); Controls.Add(lkbAuthorSite); // line break Controls.Add(new LiteralControl("</p>")); }
The CreateChildControls method in Listing 3 uses properties from Listing 2 to initialize constituent controls. The rest of the control implementation is included in the source code for this article. It contains isolated methods that make it easier for you to replace data loading with custom logic. Toward the end of Listing 3 is the logic that assigns the AuthorSite_Click method as a callback to the lkbAuthorSite link button Click event.