Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]
.pdf
C H A P T E R 2 8 ■ D E S I G N - T I M E S U P P O RT |
969 |
Figure 28-12. A custom smart tag
The Action List
Smart tags allow a number of options. To keep it all well organized, it’s a good idea to separate your code by creating a custom class that encapsulates your action list. This custom class should derive from DesignerActionList (in the System.ComponentModel.Design namespace).
Here’s an example that creates an action list that’s intended for use with the TitledTextBox:
public class TitledTextBoxActionList : DesignerActionList { ... }
You should add a single constructor to the action list that requires the matching control type. You can then store the reference to the control in a member variable. This isn’t required, because the base ActionList class does have a Component property that provides access to your control. However, by using this approach, you gain the convenience of strongly typed access to your control.
private TitledTextBox linkedControl;
public TitledTextBoxActionList(TitledTextBox ctrl) : base(ctrl)
{
linkedControl = ctrl;
}
Before you can build the smart tag, you need to equip your action list class with the required members. For every link you want to add to the tag (via a DesignerActionMethodItem), you need to create a method. For every property you want to add (via the DesignerActionPropertyItem), you need to create a property procedure.
The smart tag in Figure 28-4 includes eight custom items: two category headers, three properties, one action link, and one piece of static text (at the bottom of the tag).
The first step is to add the properties. The get property procedure needs to retrieve the value of the property from the linked control. The set property procedure needs to apply the new value to the linked control. However, this has a catch—you can’t set the new value directly. If you do, other parts of the designer infrastructure won’t be notified about the change. Instead, you need to work through the PropertyDescriptor.SetValue() method. To make this easier, you can define a private helper method in your action list class that retrieves the PropertyDescriptor for a given property by name:
970 C H A P T E R 2 8 ■ D E S I G N - T I M E S U P P O RT
private PropertyDescriptor GetPropertyByName(string propName)
{
PropertyDescriptor prop;
prop = TypeDescriptor.GetProperties(linkedControl)[propName];
if (null == prop)
{
throw new ArgumentException("Matching property not found.", propName);
}
else
{
return prop;
}
}
Now you can create the three properties that wrap the properties in the TitledTextBox control:
public string Text
{
get { return linkedControl.Text; }
set { GetPropertyByName("Text").SetValue(linkedControl, value); }
}
public string Title
{
get { return linkedControl.Title; }
set { GetPropertyByName("Title").SetValue(linkedControl, value); }
}
public Color BackColor
{
get { return linkedControl.BackColor; }
set { GetPropertyByName("BackColor").SetValue(linkedControl, value);
}
■Note Not all properties can be edited natively in a smart tag—it all depends on the data type. If the data type has an associated UITypeEditor (for graphically editing the property) or a TypeConverter (for converting the data type to and from a string representation), editing will work. Most common data types have these ingredients, but your custom objects won’t (and as a result, all you’ll see is a read-only string generated by calling ToString() on the object). For more information, refer to the next chapter, which looks at type conversion in detail.
The next step is to build the functionality for the See Website Information link. To do this, create a method in the action list class. Here’s the code, which uses the Process class to launch the default browser:
public void LaunchSite()
{
try
{
System.Diagnostics.Process.Start("http://www.prosetech.com");
}
catch { }
}
C H A P T E R 2 8 ■ D E S I G N - T I M E S U P P O RT |
971 |
The DesignerActionItem Collection
The individual items in a smart tag are represented by the DesignerActionItem class. The .NET Framework provides four basic classes that derive from DesignerActionItem, as described in Table 28-3.
Table 28-3. Classes Derived from DesignerActionItem
Method |
Description |
DesignerActionMethodItem |
This item is rendered as a link. When you click it, it triggers an |
|
action by calling a method in your DesignerActionList class. |
DesignerActionPropertyItem |
This item is rendered as an edit control and uses logic that’s |
|
similar to the Properties window. Strings are given edit boxes, |
|
enumerated values become drop-down lists, and Boolean values |
|
are turned into check boxes. When you change the value, the |
|
underlying property is modified. |
DesignerActionTextItem |
This item is rendered as a static piece of text. Usually, it provides |
|
additional information about the control. It’s not clickable. |
DesignerActionHeaderItem |
This item derives from DesignerActionTextItem. It’s a static piece |
|
of text that’s styled as a heading. Using one or more header |
|
items, you can divide the smart tag into separate categories and |
|
group your other properties accordingly. It’s not clickable. |
|
|
To create your smart tag, you need to build a DesignerActionItemCollection that combines your group of DesignerActionItem objects. Order is important in this collection, because Visual Studio will add the DesignerActionItem objects to the smart tag from top to bottom in the order they appear.
To build your action list, you override the DesignerActionList.GetSortedActionItems() method, create the DesignerActionItemCollection, add each DesignerActionItem to it, and then return the collection. Depending on the complexity of your smart tag, this may take several steps.
The first step is to create the headers that divide the smart tag into separate regions. You can then add other items to these categories. This example uses two headers:
public override DesignerActionItemCollection GetSortedActionItems()
{
// Create 8 items.
DesignerActionItemCollection items = new DesignerActionItemCollection();
// Begin by creating the headers.
items.Add(new DesignerActionHeaderItem("Appearance")); items.Add(new DesignerActionHeaderItem("Information"));
...
Next, you can add the properties. You specify the name of the property the class, followed by the name that should appear in the smart tag. The last two items include the category where the item should be placed (corresponding to one of the DesignerActionHeaderItems you just created) and a description (which appears as a tooltip when you hover over that item).
...
// Add items that wrap the properties. items.Add(new DesignerActionPropertyItem("Title",
"TextBox Title", "Appearance", "The heading for this control."));
972 C H A P T E R 2 8 ■ D E S I G N - T I M E S U P P O RT
items.Add(new DesignerActionPropertyItem("Text", "TextBox Text", "Appearance",
"The content in the TextBox."));
items.Add(new DesignerActionPropertyItem("BackColor", "Background Color", "Appearance",
"The color shown behind the control as a background."));
...
Visual Studio connects the action item to the property in the action item class by using reflection with the property name you supply. If you add more than one property to the same category, they’re ordered based on the order in which you add them. If you add more than one category header, the categories are also ordered according to their position.
The next step is to create a DesignerActionMethodItem(), which binds a smart tag item to a method. In this case, you specify the object where the callback method is implemented, the name of the method, the name that should appear in the smart tag display, the category where it will appear, and the tooltip description. The last parameter is a Boolean value. If true, the item will be added to the context menu for the control as well as to the smart tag.
...
items.Add(new DesignerActionMethodItem(this, "LaunchSite", "See website information", "Information",
"Opens a web browser with the company site.", true));
...
Finally, you can create new DesignerActionTextItem objects with the static text you want to show and return the complete collection of items, like so:
...
items.Add(new DesignerActionTextItem( "ID: " + linkedControl.ID,
"ID"));
return items;
}
The Control Designer
Once you’ve perfected your smart tag action list, you still need to connect it to your control. You do this by creating a custom designer and overriding the ActionLists property so that it returns an instance of your custom action list class. The following control designer demonstrates this. Notice that the action list isn’t created each time ActionList is called—instead, it’s cached in a private member variable to optimize performance.
public class TitledTextBoxDesigner : ControlDesigner
{
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists
{
get
{
if (actionLists == null)
{
actionLists = new DesignerActionListCollection();
C H A P T E R 2 8 ■ D E S I G N - T I M E S U P P O RT |
973 |
actionLists.Add(
new TitledTextBoxActionList((TitledTextBox)Control));
}
return actionLists;
}
}
}
Summary
In this chapter, you took a tour through some of the simple and complex aspects of the .NET design-time architecture. You learned how to configure the way control properties are displayed in the Properties window and how to take charge of control serialization and parsing. For many more advanced topics, such as custom control designers, you can consult the MSDN documentation.
C H A P T E R 2 9
■ ■ ■
JavaScript
ASP.NET provides a rich server-based programming model. The postback architecture allows you to perform all your work with object-oriented programming languages on the server, which ensures that your code is secure and compatible with all browsers. However, the postback architecture has its weaknesses. Because posting back the page always includes some small but noticeable overhead, it’s impossible to react efficiently to mouse movements and key presses. Additionally, certain tasks— such as showing pop-up windows, providing a real-time status message, and communicating between frames—need browser interaction and just aren’t possible with server-side programming.
To compensate for these problems, experienced ASP.NET developers sometimes use clientside programming to supplement their server-side web-page code. This client-side script allows you to make more-responsive pages and accomplish some feats that wouldn’t otherwise be possible. Often, these considerations occur when creating custom controls that render rich user interfaces (such as pop-up menus or rollover buttons). For the greatest browser compatibility, the client-side script language of choice is JavaScript.
■Note In this chapter, you’ll learn some tried-and-true techniques for integrating JavaScript with ASP.NET. You’ll also learn about a JavaScript-based feature that’s new in ASP.NET 2.0—client callbacks allow you to refresh a portion of data in a web page without triggering a full postback.
JavaScript Essentials
JavaScript is an embedded language. This means that JavaScript code is inserted directly into another document—typically, an HTML web page. The code is downloaded to the client computer and executed by the browser.
You have two ways to embed JavaScript code in a web page:
•You can embed the code directly in an event attribute for an HTML element. This is the most straightforward approach for small amounts of code.
•You can add a <script> tag that contains the JavaScript code. You can choose to run this code automatically when the page loads, or you can create a JavaScript function that will be called in response to a client-side event.
In many cases, you’ll use both of these techniques at the same time. For example, you might |
|
define a function in a <script> block and then wire this function up to a client-side event using |
|
an event attribute. ASP.NET follows this pattern when it performs automatic postbacks. The |
|
__doPostBack() function defines the code needed to set the appropriate event information for |
|
every control, and it’s rendered inside a <script> block. The __doPostBack() function is then |
|
connected to different controls using JavaScript event attributes such as onClick. |
975 |
C H A P T E R 2 9 ■ J AVA S C R I P T |
977 |
The combination of JavaScript and the HTML document model is called DHTML (Dynamic HTML). As is common in the world of the Web, not all browsers support the same level of DHTML. As a result, the events you can use, the elements you can manipulate, and the way events work vary from browser to browser. However, Table 29-1 lists the events that are usually safe to use in any browser that supports JavaScript. As usual, if you are creating a web application for a large number of users, you must perform extensive testing.
■Tip You can find event compatibility tables on the Internet (see, for example, www.quirksmode.org/js/ events_compinfo.html). For a comprehensive introduction to DHTML, you can refer to the MSDN website at http://msdn.microsoft.com/workshop/author/dhtml/dhtml.asp. You can find a full JavaScript reference at http://devedge.netscape.com/library/manuals.
You can insert JavaScript code for any of the attributes listed in Table 29-1. For example, the following web page adds the onMouseOver attribute to two TextBox controls:
protected void Page_Load(object sender, System.EventArgs e)
{
TextBox1.Attributes.Add("onMouseOver", "alert('Your mouse is hovering on TextBox1.');");
TextBox2.Attributes.Add("onMouseOver", "alert('Your mouse is hovering on TextBox2.');");
}
When the user moves the mouse over the appropriate text box, the event occurs and the JavaScript alert() function is called, which shows a message box (as shown in Figure 29-1).
Figure 29-1. Responding to a JavaScript event
■Note Keep in mind that ASP.NET already uses the onChange event to support the automatic postback feature. If you add the onChange attribute and set the AutoPostBack property to true, ASP.NET is intelligent enough to add both your JavaScript and the __doPostBack() function call to the attribute. Your client-side JavaScript code will be executed first, followed by the __doPostBack() function.
