Windows Forms 2.0 Programming (Microsoft .NET Development Series)

Design-time functionality is available to controls in one of two ways: programmatically or declaratively. Checking the DesignMode property is an example of the programmatic approach. One side effect of using a programmatic approach is that your implementation takes on some of the design-time responsibility, resulting in a blend of design-time and run-time code within the component implementation.

On the other hand, the declarative approach relies on attributes to request design-time functionality provided by the designer host.

Changing the Toolbox Icon

For example, consider the default Toolbox icon for AlarmClockControl, shown in Figure 11.5 residing in the AlarmClockControlLibrary's Toolbox tab. Recall from Chapter 9 that this tab is automatically created for you after a project build and is filled with all the public components in your solution.

Figure 11.5. Default Toolbox Icon

Typically, a custom component prefers a more appropriate icon. To substitute your own icon, you start by adding a 16 x 16, 16-color icon or bitmap to your project. Next, you set its Build Action to Embedded Resource (embedded resources are discussed in Chapter 13: Resources).[3] Finally, you add the ToolboxBitmap attribute to associate the icon with your component:

[3] The Toolbox supports transparent colors, too. The transparent color for icons and bitmaps is determined by the color of their bottom-left pixel.

// AlarmClockControl.cs [ToolboxBitmap( typeof(AlarmClockControl), "AlarmClockControl.ico")] partial class AlarmClockControl : ScrollableControl {...}

Whenever you add or change the ToolboxBitmap attribute, the icon for your component is automatically updated in both the Toolbox and the nonvisual design surface after a project rebuild, resulting in something like Figure 11.6.

Figure 11.6. New and Improved Toolbox Icon

You can achieve the same result without using the ToolboxBitmap attribute: Simply place a 16 x 16, 16-color bitmap in the same project folder as the component, give it the same name as the component, select the file in Solution Explorer, and set its Build Action to Embedded Resource from the Properties window. This is a special shortcut for the ToolboxBitmap attribute only; don't expect to find similar shortcuts for other design-time attributes.

It is possible that not all of the public component implementations in your assembly specifically target the design time. In these cases, you probably don't want them to be available from the Toolbox at all. To hide them, you adorn them with the DesignTimeVisible attribute, passing false to its constructor:[4]

[4] You can also use the ToolboxItem and ToolboxItemFilter attributes, although a discussion of these is beyond the scope of this book.

// IDontWantToAppearInTheToolboxComponent.cs [ToolboxBitmap(false)] partial class DontAppearInTheToolboxComponent : Component {...}

Passing true to the constructor is the same as not using the DesignTimeVisible attribute at all.

Properties Window Integration

Just as we use an attribute to influence which icon the design time displays in the Toolbox, we can use attributes to influence how properties and events appear. This is worth considering because the defaults can be quite uninformative, as shown in Figure 11.7.

Figure 11.7. Default Appearance of Public Properties and Events in the Properties Window

For a more informative experience, you can use special design-time attributes to enhance the look and feel of your component in the Properties window, describing and categorizing your properties and events. The System.ComponentModel namespace provides a comprehensive set of attributes, as shown in Table 11.1, that you can use to influence your custom component's behavior within the Properties window as well as its interactions with other design-time features like the Toolbox and the Windows Forms Designer.

Table 11.1. Design-Time Properties Window-Influencing Attributes

Attribute

Description

AmbientValue

Specifies the value for this property that causes it to acquire its value from another source, usually its container (see the section "Ambient Properties" in Chapter 10: Controls).

Bindable

Most properties can be data-bound. The Bindable attribute allows you to control whether this is true for your custom properties and, if so, whether a property supports one-way or two-way binding. Additionally, properties adorned with the Bindable attribute appear in the Properties window under Properties Window | Data | (DataBindings) Property; here, you can highlight them as recommended for data binding, which is usually reserved for key properties. See Chapter 17: Applied Data Binding for further information.

Browsable

Determines whether the property is visible in the Properties window. Covered in this chapter.

Category

Tells the Properties window which group to include this property in. Covered in this chapter.

DefaultEvent

When a component is double-clicked, this attribute determines which event the Windows Forms Designer automatically registers with and creates an event handler for. Covered in this chapter.

DefaultProperty

When a component is selected, the property that is selected by default is specified by this attribute. Covered in this chapter.

DefaultValue

Stipulates the default property value. Covered in this chapter.

Description

Provides text for the Properties window to display in its description bar. Covered in this chapter.

DesignerCategory

Can be used to force Code view to be displayed for forms, components, controls, and user controls, which would normally open to Designer view.*[*] Note that you must use the full name (e.g., System.ComponentModel.DesignerCategoryAttribute) for this to work.

DesignerSerializationVisibility

Influences how property values are serialized to InitializeComponent, if at all.Covered in this chapter.

DesignOnly

Specifies that the design-time value of this property is serialized to the form's resource file. This attribute is typically used on properties that do not exist at run time.

DesignTimeVisible

You apply this to a component to specify whether it can be visible in a visual designer, such as the Windows Forms Designer. Covered in this chapter.

DisplayName

By default, the name used by the Properties window is the actual property's name. Using the DisplayName attribute allows you to specify a more human-readable property name to be displayed by the Properties window. This is particularly useful for extender provider properties, which are covered in this chapter.

Editor

Assigns a custom UI-type editor to a property to provide rich editing support. Covered in this chapter.

EditorBrowsable

Shows or hides a property or method in IntelliSense. Useful for discouraging developers from writing code against either type of member, or from viewing them with IntelliSense. You need to pass to the EditorBrowsable attribute one of the EditorBrowsableState enumeration values: Never (to always hide it), Always (to always show it), and Advanced (to allow developers to configure their IDEs to hide or show members, if supported). The EditorBrowsable attribute should be used in conjunction with the Browsable attribute.

MergableProperty

Allows this property to be combined with properties from other objects when two or more are selected for editing.

NotifyParentProperty

When this attribute is applied to a child property (such as Size) that is a child property of the Font property, a change to the child property sends a notification to the parent property to update itself. Parent-child properties are covered later. Note that, for this to work, you must use the full name (e.g., System.ComponentModel.NotifyParentPropertyAttribute). Covered in this chapter.

ParenthesizePropertyName

Specifies whether this property should be surrounded by parentheses in the Properties window.

PasswordPropertyText

Replaces the string representation of a property value with password replacement characters, which are either dots or asterisks.

ProvideProperty

Configures a component to provide an extended property to other components hosted on either the visual or the nonvisual design surface. Covered in this chapter.

ReadOnly

Specifies that this property cannot be edited in the Properties window.

RefreshProperties

When a property is refreshed, you can use this attribute to instruct the Properties window to refresh itself in one of several ways specified by the RefreshProperties enumeration value: All (to refresh all properties), Repaint (to refresh only the visible properties), and None (to refresh no properties). Covered in this chapter.

SettingsBindable

All properties can be bound to application and user settings, but by applying the SettingsBindable attribute to a component's property, you force it to appear as a "recommended" property for settings purposes. A property with this attribute appears under Properties Window | Data | (Application Settings), which alleviates the need to open the Property Binding UI type editor. See Chapter 15: Settings for more information on application and user settings.

TypeConverter

Assigns a type converter to a component property to facilitate conversion between that property type and the string that's displayed in the Properties window. Covered in this chapter.

[*] Ian Griffiths has a great explanation at http://www.interact-sw.co.uk/iangblog/2004/06/10/codeviewinvs (http://tinysells.com/31).

By default, public read-only and read-write propertiessuch as the Alarm property highlighted in Figure 11.7are displayed in the Properties window under the Misc category. If a property is intended for run time only, as Alarm is, you can prevent it from appearing in the Properties window by adorning the property with the Browsable attribute:

[Category("Behavior")] public DateTime Alarm { get {...} set {...} }

You can use the Description attribute to provide a description for the Properties window to display, thereby improving the situation:

[Category("Behavior")] [Description("Alarm for late risers")] public DateTime Alarm { get {...} set {...} }

If you really want to, you can even change the property label text in the Properties window; the value of the property label text is the name of the underlying component's property implementation. You can do this by using the DisplayName attribute, which also allows you to use spaces and punctuation characters:

[Category("Behavior")] [Description("Alarm for late risers.")] [DisplayName("Alarm!!!")] public DateTime Alarm { get {...} set {...} }

After you add these attributes and rebuild, the Alarm property is categorized appropriately and described nicely in the Properties window, as shown in Figure 11.8.

Figure 11.8. Alarm Property Augmented with Category, Description, and DisplayName Attributes

Note that you can use the Category attribute to create new categories, but you should do so only if the existing categories don't suitably describe a property's purpose. Otherwise, you'll confuse developers who look for your properties in the most intuitive category.

In Figure 11.8, some property values are shown in boldface and others are not. Boldface values are those that differ from the property's default value. For example, if we had a property, ShowDigitalTime that allowed us to hide or show the digital time value on the clock face, and if the default value was true, we could use the DefaultValue attribute to specify that it be bolded when false:

bool showDigitalTime = true; ... [Category("Appearance")] [Description("Whether digital time is shown")] [DefaultValue(true)] public bool ShowDigitalTime { get {...} set {...} }

Using the DefaultValue attribute also allows you to reset a property to its default value using the Properties window, which is available from the property's context menu, as shown in Figure 11.9.

Figure 11.9. Resetting a Property to Its Default Value

This option is disabled if the current property is already the default value. Default values represent the most common value for a property. Some properties, such as Alarm or Text, simply don't have a default that's possible to define, whereas others, such as Enabled and ControlBox, do.

Just like properties, a class can have defaults. You can specify a default event by adorning a class with the DefaultEvent attribute:

// AlarmClockControl.cs [DefaultEvent("AlarmSounded")] partial class AlarmClockControl : ScrollableControl {...}

Double-clicking the component causes the Windows Forms Designer to automatically hook up the default event; it does this by serializing code to register with the specified event in InitializeComponent and providing a handler for it:

// AlarmClockControlHostForm.cs partial class AlarmClockControlHostForm : Form { ... void alarmClockControl_AlarmSounded( object sender, AlarmClockControlLibrary.AlarmSoundedEventArgs e) {...} ... } // AlarmClockControlHostForm.Designer.cs partial class AlarmClockControlHostForm { ... void InitializeComponent() { ... this.alarmClockControl.AlarmSounded += this.alarmClockControl_AlarmSounded; ... } }

You can also adorn your component with the DefaultProperty attribute:

// AlarmClockControl.cs [DefaultProperty("ShowDigitalTime")] partial class AlarmClockControl : ScrollableControl {...}

This attribute causes the Windows Forms Designer to highlight the default property when the component's property is first edited. Default properties aren't terribly useful, but properly setting the correct default event can save a developer's time when using your component.

Категории