Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
104
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

938 C H A P T E R 2 7 C U S TO M S E R V E R C O N T R O L S

The Style class provides a collection of properties that you can set programmatically. Here’s an example of how you could set the background color of the header using the SuperSimpleReader.HeaderStyle property:

repeater.HeaderStyle.BackColor = Color.Red;

Even more usefully, you can configure all the style properties using the Properties window. Just look for the style property, and click the plus sign next to it. A full list of subproperties will appear, each of which you can configure in the same way you configure style information for an ordinary web control.

Of course, once you’ve added the properties for storing style information, you still need to adjust the control creation code to use these styles. The basic technique is the Control.ApplyStyle() method, which copies all the style information from a style object to a control. Here’s how you can use this technique to set the style attributes for the header:

if (headerStyle != null)

{

headerContainer.ApplyStyle(headerStyle);

}

The alternating item template is a special case. Usually, the alternating item will use the item style plus any styles that are redefined in the alternating item style. In this way, the user can just add a few style settings for the alternating item, rather than redefining all the style settings from the item style.

To accomplish this behavior, you need the help of the CopyFrom() and MergeWith() methods of the Style class. The CopyFrom() method copies the styles from one style object to the calling style object, overwriting current values if they exist. The MergeWith() method combines the two styles so that if a value exists for the style attribute in the first style, this value will not be overwritten by the style value from the second style object.

Table 27-4 demonstrates how this works. The first two columns show the values for several style properties on two instances of the style class. The third column shows the updated values for the first style after calling CopyFrom() and passing in the second style. The last column shows the same values in Style1 after calling MergeWith() and passing Style2 as a parameter. Note that Style2 is not changed by either of these operations.

Table 27-4. How Styles Are Copied and Merged

 

 

 

Style1 After

Style1 After

 

Style1 Before

Style2 Before

CopyFrom(Style2)

MergeWith(Style2)

BackColor

Black

White

White

Black

ForeColor

White

Black

Black

White

Height

25

[Not set]

25

25

Width

[Not set]

25

25

25

 

 

 

 

 

Here’s how you can use the CopyFrom() and MergeWith() methods to create a style for alternating items:

Style altStyle = new Style(); altStyle.MergeWith(itemStyle); altStyle.CopyFrom(alternatingItemStyle);

You can now apply that style when needed, just as with any other style:

container.ApplyStyle(altStyle);

C H A P T E R 2 7 C U S TO M S E R V E R C O N T R O L S

939

With this revised version of the control, you can add style tags to the repeater. Here’s an example of the style information that might be created after configuring the style properties in Visual Studio:

<apress:SimpleStyledRepeater id="sample" runat="server" repeatcount="10">

<AlternatingItemStyle Font-Bold="True" BorderStyle="Solid" BorderWidth="1px" ForeColor="White" BackColor="Red"></AlternatingItemStyle>

<HeaderStyle Font-Italic="True" BackColor="#FFFFC0"></HeaderStyle>

<AlternatingItemTemplate>

Item <%# Container.Index %> of <%# Container.Total%> </AlternatingItemTemplate>

<ItemTemplate>

<hr />Item <%# Container.Index %> of <%# Container.Total%><br /><hr /> </ItemTemplate>

<HeaderTemplate>

Now showing <%# Container.Total %> Items for your viewing pleasure. </HeaderTemplate>

</apress:SimpleStyledRepeater>

Notice that the templates in this example are pared down so that they no longer apply formatting directly through HTML tags and style attributes. Instead, all the formatting is set using the styles. Figure 27-14 shows the result when you bind the SimpleStyledRepeater and show the page.

Figure 27-14. Using styles with templates

You can accomplish quite a bit more with templated controls, and it would take a significant amount of code (and a major investment of time) to duplicate a control such as the GridView. However, these examples show what you need to get started. Using them, you can create templated controls that are fine-tuned for your own custom data.

940 C H A P T E R 2 7 C U S TO M S E R V E R C O N T R O L S

Summary

In this chapter, you learned how to use a variety of techniques to create custom controls. In the next chapter, you’ll continue your exploration by learning how to take control of the design-time representation of a control. In Chapter 29 and Chapter 30, you’ll see examples of custom controls that use JavaScript and GDI+ for advanced solutions.

Even after you’ve read all these chapters, you still will not have learned everything there is to know about ASP.NET custom control creation. If you want to continue your exploration into the tricks, techniques, and idiosyncrasies of custom control programming, you might be interested in a dedicated book about the topic. A good resource is Developing Microsoft ASP.NET Server Controls and Components (Microsoft Press, 2002).

C H A P T E R 2 8

■ ■ ■

Design-Time Support

Custom controls have two requirements. They need to interact with a web page (and your code) at runtime, and they need to interact with Visual Studio at design time. These two tasks are related, but they can be refined and customized separately. Some of ASP.NET’s most advanced controls (such as the Calendar and DataList) include an impressive degree of design-time smarts, including the ability to configure complex properties and apply themes with the click of a mouse.

You’ve probably already noticed that Visual Studio gives custom controls a high degree of design-time support automatically. For example, every custom control can be added to the Toolbox, dragged onto a form, and moved and repositioned. Additionally, you can configure the properties of the control in the Properties window, and depending on how you’ve implemented the rendering logic, you may even see a design-time representation of the control’s HTML. In this chapter, you’ll learn how to extend this level of design-time support.

Many of the techniques you’ll see are frills and niceties that make it easier to work with custom controls. For example, you might use design-time support to add descriptions that appear in the Properties window or render a more representative appearance for your control on the design surface. However, other times design-time support is required. For example, if you create a control that exposes complex objects as properties and you don’t take any extra steps to add design-time support, the control will work erratically in the design-time environment. You might find that properties you set using the Properties window are reset sporadically or cause nested child control tags to disappear. These quirks are a result of how ASP.NET serializes your control properties, and you’ll learn how to tackle these issues in this chapter.

DESIGN-TIME SUPPORT CHANGES IN .NET 2.0

ASP.NET 2.0 continues to increase the design-time features for .NET controls. Many of these features will appeal to hard-core control developers, and they’re out of the scope of this book. However, you will see the following enhancements:

New attributes: ASP.NET 2.0 introduces new features, such as themes and better localization, and you can use new attributes to tailor whether your control supports them.

Web resources: Using this feature, you can embed images, scripts, and other support files that your custom controls need directly in the compiled assembly. Best of all, you can still access these resources through a special URL format that uses the WebResource.axd handler.

Smart tags: Many ASP.NET controls offer convenient smart tags that group common tasks. With a custom designer, you can create your own smart tags that bring together static information, property-style edit boxes, and links.

941

942 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

Design-Time Attributes

The first level of design-time support consists of control attributes—declarative flags that are compiled into the metadata of your custom control assembly. Attributes give you a way to add information that’s related to a piece of code without forcing you to change the code or create a separate file in an entirely different format.

Attributes are always placed in square brackets before the code element they modify. For example, here’s how you can add an attribute that provides a description for the Text property of a control:

[Description("The text to be shown in the control")] public string Text

{ ... }

In this case, the Description attribute decorates the Text property.

Note All attributes are actually classes. By convention, the class name ends in Attribute. For example, the Description attribute is actually represented by the DescriptionAttribute class. Although you can use the full class name, the C# compiler allows you to use a handy shortcut and omit the final Attribute word.

In .NET, attributes are used for a range of tasks. The key detail to understand about attributes is that they can be read and interpreted by different agents. For example, you can add attributes that give information to the CLR, the compiler, or a custom tool. This chapter focuses primarily on attributes that provide information to Visual Studio and tell it how to work with a control at design time. Later in the “Control Serialization” section, you’ll also learn about some attributes that influence how the ASP.NET parser interprets control tags in the .aspx file.

Tip Like many of the classes for design-time support, most of the attributes you’ll learn about in this chapter are found in the System.ComponentModel namespace. Before applying these attributes, you should import that namespace into the code files for your custom controls.

The Properties Window

The simplest attributes influence how the properties of your control appear in the Properties window. For example, you’ve probably noticed that the core set of ASP.NET web controls group their properties into several categories. When you select a property, the Properties window shows a brief description. To add this information to your own control, you need to decorate each property with the Category and Description attribute, as shown here:

[Category("Appearance")]

[Description("The text to be shown in the control")] public string Text

{

get {return (string)ViewState["Text"]; set {ViewState["Text"] = value;}

}

As you can see, both the Category and Description attribute accept a single string as an argument. Figure 28-1 shows the resulting display if you select the Text property in the Properties window.

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

943

Figure 28-1. A property with a description

Table 28-1 lists the key attributes that influence the way a property is displayed in the Properties window.

Table 28-1. Attributes for Control Properties

Attribute

Description

Browsable(true|false)

If false, this property will not appear in the Properties window

 

(although the programmer can still modify it in code or by

 

manually adding the control tag attribute, as long as you

 

include a Set property procedure). One reason you might use

 

this attribute is to hide calculated or runtime properties that

 

can’t be changed at design time.

Category(string)

A string that indicates the category under which the property

 

will appear in the Properties window.

Description(string)

A string that indicates the description the property will have

 

when selected in the Properties window.

DefaultValue()

Sets the default value that will be displayed for the property in

 

the Properties window. The default value is typically the initial

 

value, in which case you don’t need to use the DefaultValue

 

attribute. However, using this attribute can sometimes allow

 

the code generator to optimize the tags it generates by leaving

 

out information if it matches the default.

Themeable(true|false)

All custom controls automatically support theming (see

 

Chapter 15). However, if you don’t want a specific property

 

to be configurable as part of a skin, apply the Themeable

 

attribute with a value of false.

Localizable(true|false)

Localization is enabled for all controls and objects. If a

 

property is localizable, Visual Studio will allow its values to be

 

persisted in a satellite assembly. If you don’t want this to be

 

possible, or you have a property that shouldn’t vary based on

 

local, set this to false.

ReadOnly(bool)

When true, this property is read-only in the Properties

 

window at design time.

Continued

944 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

Table 28-1. Continued

Attribute

Description

DesignOnly(bool)

When set to true, this property is available only at design time.

 

This is typically used with special properties that configure

 

how a control behaves at design time and don’t correspond to

 

a “real” piece of information about the control.

ImmutableObject(bool)

When set to true on an object property, this attribute ensures

 

that the subproperties of this object are displayed as read-

 

only. For example, if you apply this to a property that uses a

 

Point object, the X and Y subproperty will be read-only.

MergableProperty(bool)

Configures how the Properties window behaves when more

 

than one instance of this control is selected at once. If false,

 

the property is not shown. If true (the default), the property

 

can be set for all selected controls at once.

ParenthesizePropertyName(bool)

If true, Visual Studio will display parentheses around this

 

property in the Properties window (as it does with the ID

 

property).

Bindable(bool)

If true, Visual Studio will display this property in the

 

DataBindings dialog box and allow it to be bound to a

 

field in a data source.

RefreshProperties()

You use this attribute with a value from the RefreshProperties

 

enumeration. It specifies whether the rest of the Properties

 

window must be updated when this property is changed (for

 

example, if one property procedure could change another

 

property).

 

 

You can apply two attributes, DefaultEvent and DefaultProperty, to your custom control class declaration, rather than a specific property. Additionally, the TagPrefix attribute is used at the assembly level and isn’t attached to any code construct. Table 28-2 describes these attributes.

Table 28-2. Attributes for Control Classes and Assemblies

Attribute

Description

DefaultEvent(string)

Indicates the name of the default event. When you double-click the

 

control in the design environment, Visual Studio automatically adds

 

an event handler for the default event.

DefaultProperty(string)

Indicates the name of the default property. The DefaultProperty is the

 

property that is highlighted in the Properties window by default, the

 

first time the control is selected.

TagPrefix(string, string)

Associates a namespace with a prefix, which will be used when adding

 

control tags to an .aspx page.

 

 

As you learned in Chapter 27, every custom control has a prefix that’s registered with the Register directive in the .aspx page. Visual Studio adds this directive automatically when you insert the control. If you want to customize the prefix, you can use the TagPrefix attribute, which accepts two string parameters. The first string is the namespace your controls are in, and the second string is the tag prefix you want to use.

Here’s an example that specifies that controls in the CustomServerControlsLibrary should use the apress tag prefix:

[assembly: System.Web.UI.TagPrefix("CustomServerControlsLibrary", "apress")]

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

945

Now if you add a control with the class name CustomTextBox from the CustomServerControlsLibrary namespace, this is the tag Visual Studio uses:

<apress:CustomTextBox ... />

If you have controls in multiple namespaces, you need to use TagPrefix multiple times, once for each namespace. You can use the same prefix or different prefixes. Often, the TagPrefix attribute is placed in the AssemblyInfo.cs file.

Here’s the simple CustomTextBox control from the previous chapter, with a full complement of attributes. The code has been left out.

[assembly: System.Web.UI.TagPrefix("CustomServerControlsLibrary", "apress")] namespace CustomServerControlsLibrary

{

[DefaultProperty("Text")]

[DefaultEvent("TextChanged")]

public class CustomTextBox : WebControl, IPostBackDataHandler

{

public CustomTextBox() : base(HtmlTextWriterTag.Input) { ... }

[Category("Appearance")]

[Description("The text to be shown in the control")] [DefaultValue("")]

[MergableProperty(true)] public string Text

{ ... }

protected override void AddAttributesToRender(HtmlTextWriter output) { ... }

public bool LoadPostData(string postDataKey, NameValueCollection postData)

{ ... }

public void RaisePostDataChangedEvent() { ... }

public event EventHandler TextChanged;

protected virtual void OnTextChanged(EventArgs e) { ... }

}

}

Attributes and Inheritance

When you derive a control from a base class that has design-time attributes, the control inherits the design-time functionality of its parent, just like it inherits the methods and properties. If the parent class’s implementation of the design-time attributes is sufficient for your control, you do not need to reapply them.

However, in some cases you might want to change the design-time behavior of an existing property. In this case, you must first override the property and then reapply the changed attributes or add the new ones.

Most of the properties in the base classes WebControl and Control are marked as virtual, which allows you to change their behavior. For example, if you wanted to hide the Height property of a custom control that derives from WebControl (maybe because it is calculated from the content

946C H A P T E R 2 8 D E S I G N - T I M E S U P P O RT

rather than set by the developer), you could override the Height property and apply the Browsable attribute, as shown here:

[Browsable(false)]

public override Unit Height

{

get {return base.Height;} set {base.Height = value;}

}

The Toolbox Icon

Adding a Toolbox icon is refreshingly easy. All you need to do is add a bitmap to your project and ensure that it has the same filename as your custom control class. This bitmap must meet a few basic criteria:

It must be 16 16 pixels. Otherwise, it will be mangled when Visual Studio attempts to scale it.

It must use only 16 colors.

Once you add the file, use the Properties window to set the Build Action for it to Embedded Resource. Then recompile the control project. Figure 28-2 shows the required image for the CustomTextBox control.

Figure 28-2. Adding a Toolbox bitmap

When you add the control to a client project, the embedded bitmap appears in the Toolbox. Figure 28-3 shows an example with two custom controls. One has the generic gear icon, while the CustomTextBox uses the bitmap added to the control project.

If you’re creating a simple control, all you may need to do is add a set of descriptive properties and a Toolbox icon. However, more complex controls often require other considerations. These range from code serialization issues (how the control tag is created when you use the Properties window) to control designers (advanced tools for customizing the design-time HTML your control renders). In the rest of the chapter, you’ll take a look at these topics.

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

947

Figure 28-3. A custom Toolbox bitmap

Web Resources

Often, custom controls will have other, associated, noncode resources. For example, you might have script files, stylesheets, and images that you need to use with the control. This introduces an additional deployment headache, because you now need to copy these resource files to every web application that uses your control. Fortunately, ASP.NET 2.0 introduces an innovative solution

with a new web resources model.

Here’s how it works: when you create the custom control assembly, you add the resource files directly to your project. Using the Solution Explorer, you change them so that the Build Action is set to Embedded Resource, not Content (see Figure 28-4). This way, the file will be embedded inside your compiled assembly.

The next step is to make that resource URL-accessible. This works by using a new assemblylevel WebResource attribute.

For example, imagine you want to access the button picture used for the CustomImageButton control. Once you’ve changed the Build Action setting, here’s the attribute you need to add to your code:

[assembly: WebResource("CustomServerControls.button1.jpg", "image/jpeg")]

The WebResource attribute takes two parameters. The first is the full name of your embedded resource, which must be prefixed with the default namespace of your project. (This detail, which you can see by right-clicking your project in the Solution Explorer and choosing Properties, is automatically added to the beginning of the resource name). The second parameter is the content type.

Once you’ve taken these steps, the last bit of work you need to do is get the URL for your embedded resource. ASP.NET supports this through a new WebResource.axd handler. This handler accepts URL requests, extracts the corresponding resource from the appropriate assembly, and then returns the content. In other words, you don’t need a clutter of images and scripts, because the WebResource.axd file can serve them as needed, right from your custom control assembly.