Skip to content

Latest commit

 

History

History
613 lines (352 loc) · 51.7 KB

creating-a-wf-control-design-time-features.md

File metadata and controls

613 lines (352 loc) · 51.7 KB
title description ms.date dev_langs helpviewer_keywords ms.assetid
Create a Control That Takes Advantage of Visual Studio Design-Time Features
Learn how to create a custom designer for a custom control in Windows Forms that takes advantage of design-time features.
03/30/2017
csharp
vb
Windows Forms controls, creating
design-time functionality [Windows Forms], Windows Forms
DocumentDesigner class [Windows Forms]
walkthroughs [Windows Forms], controls
6f487c59-cb38-4afa-ad2e-95edacb1d626

Walkthrough: Create a control that takes advantage of design-time features

The design-time experience for a custom control can be enhanced by authoring an associated custom designer.

[!INCLUDE visual-studio-designer-net]

This article illustrates how to create a custom designer for a custom control. You'll implement a MarqueeControl type and an associated designer class called MarqueeControlRootDesigner.

The MarqueeControl type implements a display similar to a theater marquee with animated lights and flashing text.

The designer for this control interacts with the design environment to provide a custom design-time experience. With the custom designer, you can assemble a custom MarqueeControl implementation with animated lights and flashing text in many combinations. You can use the assembled control on a form like any other Windows Forms control.

When you're finished with this walkthrough, your custom control will look something like the following:

The app showing a marquee saying Text and a Start and Stop buttons.

For the complete code listing, see How to: Create a Windows Forms Control That Takes Advantage of Design-Time Features.

Prerequisites

In order to complete this walkthrough, you'll need Visual Studio.

Create the project

The first step is to create the application project. You will use this project to build the application that hosts the custom control.

In Visual Studio, create a new Windows Forms Application project, and name it MarqueeControlTest.

Create the control library project

  1. Add a Windows Forms Control Library project to the solution. Name the project MarqueeControlLibrary.

  2. Using Solution Explorer, delete the project's default control by deleting the source file named "UserControl1.cs" or "UserControl1.vb", depending on your language of choice.

  3. Add a new xref:System.Windows.Forms.UserControl item to the MarqueeControlLibrary project. Give the new source file a base name of MarqueeControl.

  4. Using Solution Explorer, create a new folder in the MarqueeControlLibrary project.

  5. Right-click the Design folder and add a new class. Name it MarqueeControlRootDesigner.

  6. You'll need to use types from the System.Design assembly, so add this reference to the MarqueeControlLibrary project.

Reference the Custom Control Project

You will use the MarqueeControlTest project to test the custom control. The test project will become aware of the custom control when you add a project reference to the MarqueeControlLibrary assembly.

In the MarqueeControlTest project, add a project reference to the MarqueeControlLibrary assembly. Be sure to use the Projects tab in the Add Reference dialog box instead of referencing the MarqueeControlLibrary assembly directly.

Define a Custom Control and Its Custom Designer

Your custom control will derive from the xref:System.Windows.Forms.UserControl class. This allows your control to contain other controls, and it gives your control a great deal of default functionality.

Your custom control will have an associated custom designer. This allows you to create a unique design experience tailored specifically for your custom control.

You associate the control with its designer by using the xref:System.ComponentModel.DesignerAttribute class. Because you are developing the entire design-time behavior of your custom control, the custom designer will implement the xref:System.ComponentModel.Design.IRootDesigner interface.

To define a custom control and its custom designer

  1. Open the MarqueeControl source file in the Code Editor. At the top of the file, import the following namespaces:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#220] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#220]

  2. Add the xref:System.ComponentModel.DesignerAttribute to the MarqueeControl class declaration. This associates the custom control with its designer.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#240] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#240]

  3. Open the MarqueeControlRootDesigner source file in the Code Editor. At the top of the file, import the following namespaces:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#520] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#520]

  4. Change the declaration of MarqueeControlRootDesigner to inherit from the xref:System.Windows.Forms.Design.DocumentDesigner class. Apply the xref:System.ComponentModel.ToolboxItemFilterAttribute to specify the designer interaction with the Toolbox.

    [!NOTE] The definition for the MarqueeControlRootDesigner class has been enclosed in a namespace called MarqueeControlLibrary.Design. This declaration places the designer in a special namespace reserved for design-related types.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#530] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#530]

  5. Define the constructor for the MarqueeControlRootDesigner class. Insert a xref:System.Diagnostics.Trace.WriteLine%2A statement in the constructor body. This will be useful for debugging.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#540] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#540]

Create an instance of your custom control

  1. Add a new xref:System.Windows.Forms.UserControl item to the MarqueeControlTest project. Give the new source file a base name of DemoMarqueeControl.

  2. Open the DemoMarqueeControl file in the Code Editor. At the top of the file, import the MarqueeControlLibrary namespace:

    Imports MarqueeControlLibrary
    using MarqueeControlLibrary;
  3. Change the declaration of DemoMarqueeControl to inherit from the MarqueeControl class.

  4. Build the project.

  5. Open Form1 in the Windows Forms Designer.

  6. Find the MarqueeControlTest Components tab in the Toolbox and open it. Drag a DemoMarqueeControl from the Toolbox onto your form.

  7. Build the project.

Set Up the Project for Design-Time Debugging

When you're developing a custom design-time experience, it will be necessary to debug your controls and components. There is a simple way to set up your project to allow debugging at design time. For more information, see Walkthrough: Debugging Custom Windows Forms Controls at Design Time.

  1. Right-click the MarqueeControlLibrary project and select Properties.

  2. In the MarqueeControlLibrary Property Pages dialog box, select the Debug page.

  3. In the Start Action section, select Start External Program. You will be debugging a separate instance of Visual Studio, so click the ellipsis (The Ellipsis button (...) in the Properties window of Visual Studio) button to browse for the Visual Studio IDE. The name of the executable file is devenv.exe, and if you installed to the default location, its path is %ProgramFiles(x86)%\Microsoft Visual Studio\2019\<edition>\Common7\IDE\devenv.exe.

  4. Select OK to close the dialog box.

  5. Right-click the MarqueeControlLibrary project and select Set as StartUp Project to enable this debugging configuration.

Checkpoint

You are now ready to debug the design-time behavior of your custom control. Once you've determined that the debugging environment is set up correctly, you'll test the association between the custom control and the custom designer.

To test the debugging environment and the designer association

  1. Open the MarqueeControlRootDesigner source file in the Code Editor and place a breakpoint on the xref:System.Diagnostics.Trace.WriteLine%2A statement.

  2. Press F5 to start the debugging session.

    A new instance of Visual Studio is created.

  3. In the new instance of Visual Studio, open the MarqueeControlTest solution. You can easily find the solution by selecting Recent Projects from the File menu. The MarqueeControlTest.sln solution file will be listed as the most recently used file.

  4. Open the DemoMarqueeControl in the designer.

    The debugging instance of Visual Studio obtains focus and execution stops at your breakpoint. Press F5 to continue the debugging session.

At this point, everything is in place for you to develop and debug your custom control and its associated custom designer. The remainder of this article concentrates on the details of implementing features of the control and the designer.

Implement the Custom Control

The MarqueeControl is a xref:System.Windows.Forms.UserControl with a little bit of customization. It exposes two methods: Start, which starts the marquee animation, and Stop, which stops the animation. Because the MarqueeControl contains child controls that implement the IMarqueeWidget interface, Start and Stop enumerate each child control and call the StartMarquee and StopMarquee methods, respectively, on each child control that implements IMarqueeWidget.

The appearance of the MarqueeBorder and MarqueeText controls is dependent on the layout, so MarqueeControl overrides the xref:System.Windows.Forms.Control.OnLayout%2A method and calls xref:System.Windows.Forms.Control.PerformLayout%2A on child controls of this type.

This is the extent of the MarqueeControl customizations. The run-time features are implemented by the MarqueeBorder and MarqueeText controls, and the design-time features are implemented by the MarqueeBorderDesigner and MarqueeControlRootDesigner classes.

To implement your custom control

  1. Open the MarqueeControl source file in the Code Editor. Implement the Start and Stop methods.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#260] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#260]

  2. Override the xref:System.Windows.Forms.Control.OnLayout%2A method.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#270] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#270]

Create a Child Control for Your Custom Control

The MarqueeControl will host two kinds of child control: the MarqueeBorder control and the MarqueeText control.

  • MarqueeBorder: This control paints a border of "lights" around its edges. The lights flash in sequence, so they appear to be moving around the border. The speed at which the lights flash is controlled by a property called UpdatePeriod. Several other custom properties determine other aspects of the control's appearance. Two methods, called StartMarquee and StopMarquee, control when the animation starts and stops.

  • MarqueeText: This control paints a flashing string. Like the MarqueeBorder control, the speed at which the text flashes is controlled by the UpdatePeriod property. The MarqueeText control also has the StartMarquee and StopMarquee methods in common with the MarqueeBorder control.

At design time, the MarqueeControlRootDesigner allows these two control types to be added to a MarqueeControl in any combination.

Common features of the two controls are factored into an interface called IMarqueeWidget. This allows the MarqueeControl to discover any Marquee-related child controls and give them special treatment.

To implement the periodic animation feature, you will use xref:System.ComponentModel.BackgroundWorker objects from the xref:System.ComponentModel?displayProperty=nameWithType namespace. You could use xref:System.Windows.Forms.Timer objects, but when many IMarqueeWidget objects are present, the single UI thread may be unable to keep up with the animation.

To create a child control for your custom control

  1. Add a new class item to the MarqueeControlLibrary project. Give the new source file a base name of "IMarqueeWidget."

  2. Open the IMarqueeWidget source file in the Code Editor and change the declaration from class to interface:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#2] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#2]

  3. Add the following code to the IMarqueeWidget interface to expose two methods and a property that manipulate the marquee animation:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#3] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#3]

  4. Add a new Custom Control item to the MarqueeControlLibrary project. Give the new source file a base name of "MarqueeText."

  5. Drag a xref:System.ComponentModel.BackgroundWorker component from the Toolbox onto your MarqueeText control. This component will allow the MarqueeText control to update itself asynchronously.

  6. In the Properties window, set the xref:System.ComponentModel.BackgroundWorker component's WorkerReportsProgress and xref:System.ComponentModel.BackgroundWorker.WorkerSupportsCancellation%2A properties to true. These settings allow the xref:System.ComponentModel.BackgroundWorker component to periodically raise the xref:System.ComponentModel.BackgroundWorker.ProgressChanged event and to cancel asynchronous updates.

    For more information, see BackgroundWorker Component.

  7. Open the MarqueeText source file in the Code Editor. At the top of the file, import the following namespaces:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#120] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#120]

  8. Change the declaration of MarqueeText to inherit from xref:System.Windows.Forms.Label and to implement the IMarqueeWidget interface:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#130] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#130]

  9. Declare the instance variables that correspond to the exposed properties, and initialize them in the constructor. The isLit field determines if the text is to be painted in the color given by the LightColor property.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#140] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#140]

  10. Implement the IMarqueeWidget interface.

    The StartMarquee and StopMarquee methods invoke the xref:System.ComponentModel.BackgroundWorker component's xref:System.ComponentModel.BackgroundWorker.RunWorkerAsync%2A and xref:System.ComponentModel.BackgroundWorker.CancelAsync%2A methods to start and stop the animation.

    The xref:System.ComponentModel.CategoryAttribute.Category%2A and xref:System.ComponentModel.BrowsableAttribute.Browsable%2A attributes are applied to the UpdatePeriod property so it appears in a custom section of the Properties window called "Marquee."

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#150] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#150]

  11. Implement the property accessors. You'll expose two properties to clients: LightColor and DarkColor. The xref:System.ComponentModel.CategoryAttribute.Category%2A and xref:System.ComponentModel.BrowsableAttribute.Browsable%2A attributes are applied to these properties, so the properties appear in a custom section of the Properties window called "Marquee."

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#160] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#160]

  12. Implement the handlers for the xref:System.ComponentModel.BackgroundWorker component's xref:System.ComponentModel.BackgroundWorker.DoWork and xref:System.ComponentModel.BackgroundWorker.ProgressChanged events.

    The xref:System.ComponentModel.BackgroundWorker.DoWork event handler sleeps for the number of milliseconds specified by UpdatePeriod then raises the xref:System.ComponentModel.BackgroundWorker.ProgressChanged event, until your code stops the animation by calling xref:System.ComponentModel.BackgroundWorker.CancelAsync%2A.

    The xref:System.ComponentModel.BackgroundWorker.ProgressChanged event handler toggles the text between its light and dark state to give the appearance of flashing.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#180] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#180]

  13. Override the xref:System.Windows.Forms.Control.OnPaint%2A method to enable the animation.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#170] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#170]

  14. Press F6 to build the solution.

Create the MarqueeBorder Child Control

The MarqueeBorder control is slightly more sophisticated than the MarqueeText control. It has more properties and the animation in the xref:System.Windows.Forms.Control.OnPaint%2A method is more involved. In principle, it is quite similar to the MarqueeText control.

Because the MarqueeBorder control can have child controls, it needs to be aware of xref:System.Windows.Forms.Control.Layout events.

To create the MarqueeBorder control

  1. Add a new Custom Control item to the MarqueeControlLibrary project. Give the new source file a base name of "MarqueeBorder."

  2. Drag a xref:System.ComponentModel.BackgroundWorker component from the Toolbox onto your MarqueeBorder control. This component will allow the MarqueeBorder control to update itself asynchronously.

  3. In the Properties window, set the xref:System.ComponentModel.BackgroundWorker component's WorkerReportsProgress and xref:System.ComponentModel.BackgroundWorker.WorkerSupportsCancellation%2A properties to true. These settings allow the xref:System.ComponentModel.BackgroundWorker component to periodically raise the xref:System.ComponentModel.BackgroundWorker.ProgressChanged event and to cancel asynchronous updates. For more information, see BackgroundWorker Component.

  4. In the Properties window, select the Events button. Attach handlers for the xref:System.ComponentModel.BackgroundWorker.DoWork and xref:System.ComponentModel.BackgroundWorker.ProgressChanged events.

  5. Open the MarqueeBorder source file in the Code Editor. At the top of the file, import the following namespaces:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#20] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#20]

  6. Change the declaration of MarqueeBorder to inherit from xref:System.Windows.Forms.Panel and to implement the IMarqueeWidget interface.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#30] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#30]

  7. Declare two enumerations for managing the MarqueeBorder control's state: MarqueeSpinDirection, which determines the direction in which the lights "spin" around the border, and MarqueeLightShape, which determines the shape of the lights (square or circular). Place these declarations before the MarqueeBorder class declaration.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#97] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#97]

  8. Declare the instance variables that correspond to the exposed properties, and initialize them in the constructor.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#40] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#40]

  9. Implement the IMarqueeWidget interface.

    The StartMarquee and StopMarquee methods invoke the xref:System.ComponentModel.BackgroundWorker component's xref:System.ComponentModel.BackgroundWorker.RunWorkerAsync%2A and xref:System.ComponentModel.BackgroundWorker.CancelAsync%2A methods to start and stop the animation.

    Because the MarqueeBorder control can contain child controls, the StartMarquee method enumerates all child controls and calls StartMarquee on those that implement IMarqueeWidget. The StopMarquee method has a similar implementation.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#50] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#50]

  10. Implement the property accessors. The MarqueeBorder control has several properties for controlling its appearance.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#60] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#60]

  11. Implement the handlers for the xref:System.ComponentModel.BackgroundWorker component's xref:System.ComponentModel.BackgroundWorker.DoWork and xref:System.ComponentModel.BackgroundWorker.ProgressChanged events.

    The xref:System.ComponentModel.BackgroundWorker.DoWork event handler sleeps for the number of milliseconds specified by UpdatePeriod then raises the xref:System.ComponentModel.BackgroundWorker.ProgressChanged event, until your code stops the animation by calling xref:System.ComponentModel.BackgroundWorker.CancelAsync%2A.

    The xref:System.ComponentModel.BackgroundWorker.ProgressChanged event handler increments the position of the "base" light, from which the light/dark state of the other lights is determined, and calls the xref:System.Windows.Forms.Control.Refresh%2A method to cause the control to repaint itself.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#90] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#90]

  12. Implement the helper methods, IsLit and DrawLight.

    The IsLit method determines the color of a light at a given position. Lights that are "lit" are drawn in the color given by the LightColor property, and those that are "dark" are drawn in the color given by the DarkColor property.

    The DrawLight method draws a light using the appropriate color, shape, and position.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#80] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#80]

  13. Override the xref:System.Windows.Forms.Control.OnLayout%2A and xref:System.Windows.Forms.Control.OnPaint%2A methods.

    The xref:System.Windows.Forms.Control.OnPaint%2A method draws the lights along the edges of the MarqueeBorder control.

    Because the xref:System.Windows.Forms.Control.OnPaint%2A method depends on the dimensions of the MarqueeBorder control, you need to call it whenever the layout changes. To achieve this, override xref:System.Windows.Forms.Control.OnLayout%2A and call xref:System.Windows.Forms.Control.Refresh%2A.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#70] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#70]

Create a Custom Designer to Shadow and Filter Properties

The MarqueeControlRootDesigner class provides the implementation for the root designer. In addition to this designer, which operates on the MarqueeControl, you'll need a custom designer that is specifically associated with the MarqueeBorder control. This designer provides custom behavior that is appropriate in the context of the custom root designer.

Specifically, the MarqueeBorderDesigner will "shadow" and filter certain properties on the MarqueeBorder control, changing their interaction with the design environment.

Intercepting calls to a component's property accessor is known as "shadowing." It allows a designer to track the value set by the user and optionally pass that value to the component being designed.

For this example, the xref:System.Windows.Forms.Control.Visible%2A and xref:System.Windows.Forms.Control.Enabled%2A properties will be shadowed by the MarqueeBorderDesigner, which prevents the user from making the MarqueeBorder control invisible or disabled during design time.

Designers can also add and remove properties. For this example, the xref:System.Windows.Forms.Control.Padding%2A property will be removed at design time, because the MarqueeBorder control programmatically sets the padding based on the size of the lights specified by the LightSize property.

The base class for MarqueeBorderDesigner is xref:System.ComponentModel.Design.ComponentDesigner, which has methods that can change the attributes, properties, and events exposed by a control at design time:

  • xref:System.ComponentModel.Design.ComponentDesigner.PreFilterProperties%2A

  • xref:System.ComponentModel.Design.ComponentDesigner.PostFilterProperties%2A

  • xref:System.ComponentModel.Design.ComponentDesigner.PreFilterAttributes%2A

  • xref:System.ComponentModel.Design.ComponentDesigner.PostFilterAttributes%2A

  • xref:System.ComponentModel.Design.ComponentDesigner.PreFilterEvents%2A

  • xref:System.ComponentModel.Design.ComponentDesigner.PostFilterEvents%2A

When changing the public interface of a component using these methods, follow these rules:

  • Add or remove items in the PreFilter methods only

  • Modify existing items in the PostFilter methods only

  • Always call the base implementation first in the PreFilter methods

  • Always call the base implementation last in the PostFilter methods

Adhering to these rules ensures that all designers in the design-time environment have a consistent view of all components being designed.

The xref:System.ComponentModel.Design.ComponentDesigner class provides a dictionary for managing the values of shadowed properties, which relieves you of the need to create specific instance variables.

To create a custom designer to shadow and filter properties

  1. Right-click the Design folder and add a new class. Give the source file a base name of MarqueeBorderDesigner.

  2. Open the MarqueeBorderDesigner source file in the Code Editor. At the top of the file, import the following namespaces:

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#420] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#420]

  3. Change the declaration of MarqueeBorderDesigner to inherit from xref:System.Windows.Forms.Design.ParentControlDesigner.

    Because the MarqueeBorder control can contain child controls, MarqueeBorderDesigner inherits from xref:System.Windows.Forms.Design.ParentControlDesigner, which handles the parent-child interaction.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#430] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#430]

  4. Override the base implementation of xref:System.ComponentModel.Design.ComponentDesigner.PreFilterProperties%2A.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#450] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#450]

  5. Implement the xref:System.Windows.Forms.Control.Enabled%2A and xref:System.Windows.Forms.Control.Visible%2A properties. These implementations shadow the control's properties.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#440] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#440]

Handle Component Changes

The MarqueeControlRootDesigner class provides the custom design-time experience for your MarqueeControl instances. Most of the design-time functionality is inherited from the xref:System.Windows.Forms.Design.DocumentDesigner class. Your code will implement two specific customizations: handling component changes, and adding designer verbs.

As users design their MarqueeControl instances, your root designer will track changes to the MarqueeControl and its child controls. The design-time environment offers a convenient service, xref:System.ComponentModel.Design.IComponentChangeService, for tracking changes to component state.

You acquire a reference to this service by querying the environment with the xref:System.ComponentModel.Design.ComponentDesigner.GetService%2A method. If the query is successful, your designer can attach a handler for the xref:System.ComponentModel.Design.IComponentChangeService.ComponentChanged event and perform whatever tasks are required to maintain a consistent state at design time.

In the case of the MarqueeControlRootDesigner class, you will call the xref:System.Windows.Forms.Control.Refresh%2A method on each IMarqueeWidget object contained by the MarqueeControl. This will cause the IMarqueeWidget object to repaint itself appropriately when properties like its parent's xref:System.Windows.Forms.Control.Size%2A are changed.

To handle component changes

  1. Open the MarqueeControlRootDesigner source file in the Code Editor and override the xref:System.Windows.Forms.Design.DocumentDesigner.Initialize%2A method. Call the base implementation of xref:System.Windows.Forms.Design.DocumentDesigner.Initialize%2A and query for the xref:System.ComponentModel.Design.IComponentChangeService.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#580] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#580]

  2. Implement the xref:System.ComponentModel.Design.IComponentChangeService.OnComponentChanged%2A event handler. Test the sending component's type, and if it is an IMarqueeWidget, call its xref:System.Windows.Forms.Control.Refresh%2A method.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#560] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#560]

Add Designer Verbs to your Custom Designer

A designer verb is a menu command linked to an event handler. Designer verbs are added to a component's shortcut menu at design time. For more information, see xref:System.ComponentModel.Design.DesignerVerb.

You will add two designer verbs to your designers: Run Test and Stop Test. These verbs will allow you to view the run-time behavior of the MarqueeControl at design time. These verbs will be added to MarqueeControlRootDesigner.

When Run Test is invoked, the verb event handler will call the StartMarquee method on the MarqueeControl. When Stop Test is invoked, the verb event handler will call the StopMarquee method on the MarqueeControl. The implementation of the StartMarquee and StopMarquee methods call these methods on contained controls that implement IMarqueeWidget, so any contained IMarqueeWidget controls will also participate in the test.

To add designer verbs to your custom designers

  1. In the MarqueeControlRootDesigner class, add event handlers named OnVerbRunTest and OnVerbStopTest.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#570] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#570]

  2. Connect these event handlers to their corresponding designer verbs. MarqueeControlRootDesigner inherits a xref:System.ComponentModel.Design.DesignerVerbCollection from its base class. You will create two new xref:System.ComponentModel.Design.DesignerVerb objects and add them to this collection in the xref:System.Windows.Forms.Design.DocumentDesigner.Initialize%2A method.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#590] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#590]

Create a Custom UITypeEditor

When you create a custom design-time experience for users, it is often desirable to create a custom interaction with the Properties window. You can accomplish this by creating a xref:System.Drawing.Design.UITypeEditor.

The MarqueeBorder control exposes several properties in the Properties window. Two of these properties, MarqueeSpinDirection and MarqueeLightShape are represented by enumerations. To illustrate the use of a UI type editor, the MarqueeLightShape property will have an associated xref:System.Drawing.Design.UITypeEditor class.

To create a custom UI type editor

  1. Open the MarqueeBorder source file in the Code Editor.

  2. In the definition of the MarqueeBorder class, declare a class called LightShapeEditor that derives from xref:System.Drawing.Design.UITypeEditor.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#96] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#96]

  3. Declare an xref:System.Windows.Forms.Design.IWindowsFormsEditorService instance variable called editorService.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#92] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#92]

  4. Override the xref:System.Drawing.Design.UITypeEditor.GetEditStyle%2A method. This implementation returns xref:System.Drawing.Design.UITypeEditorEditStyle.DropDown, which tells the design environment how to display the LightShapeEditor.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#93] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#93]

  5. Override the xref:System.Drawing.Design.UITypeEditor.EditValue%2A method. This implementation queries the design environment for an xref:System.Windows.Forms.Design.IWindowsFormsEditorService object. If successful, it creates a LightShapeSelectionControl. The xref:System.Windows.Forms.Design.IWindowsFormsEditorService.DropDownControl%2A method is invoked to start the LightShapeEditor. The return value from this invocation is returned to the design environment.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#94] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#94]

Create a View Control for your Custom UITypeEditor

The MarqueeLightShape property supports two types of light shapes: Square and Circle. You will create a custom control used solely for the purpose of graphically displaying these values in the Properties window. This custom control will be used by your xref:System.Drawing.Design.UITypeEditor to interact with the Properties window.

To create a view control for your custom UI type editor

  1. Add a new xref:System.Windows.Forms.UserControl item to the MarqueeControlLibrary project. Give the new source file a base name of LightShapeSelectionControl.

  2. Drag two xref:System.Windows.Forms.Panel controls from the Toolbox onto the LightShapeSelectionControl. Name them squarePanel and circlePanel. Arrange them side by side. Set the xref:System.Windows.Forms.Control.Size%2A property of both xref:System.Windows.Forms.Panel controls to (60, 60). Set the xref:System.Windows.Forms.Control.Location%2A property of the squarePanel control to (8, 10). Set the xref:System.Windows.Forms.Control.Location%2A property of the circlePanel control to (80, 10). Finally, set the xref:System.Windows.Forms.Control.Size%2A property of the LightShapeSelectionControl to (150, 80).

  3. Open the LightShapeSelectionControl source file in the Code Editor. At the top of the file, import the xref:System.Windows.Forms.Design?displayProperty=nameWithType namespace:

    Imports System.Windows.Forms.Design
    using System.Windows.Forms.Design;
  4. Implement xref:System.Windows.Forms.Control.Click event handlers for the squarePanel and circlePanel controls. These methods invoke xref:System.Windows.Forms.Design.IWindowsFormsEditorService.CloseDropDown%2A to end the custom xref:System.Drawing.Design.UITypeEditor editing session.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#390] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#390]

  5. Declare an xref:System.Windows.Forms.Design.IWindowsFormsEditorService instance variable called editorService.

    Private editorService As IWindowsFormsEditorService
    private IWindowsFormsEditorService editorService;
  6. Declare a MarqueeLightShape instance variable called lightShapeValue.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#330] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#330]

  7. In the LightShapeSelectionControl constructor, attach the xref:System.Windows.Forms.Control.Click event handlers to the squarePanel and circlePanel controls' xref:System.Windows.Forms.Control.Click events. Also, define a constructor overload that assigns the MarqueeLightShape value from the design environment to the lightShapeValue field.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#340] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#340]

  8. In the xref:System.ComponentModel.Component.Dispose%2A method, detach the xref:System.Windows.Forms.Control.Click event handlers.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#350] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#350]

  9. In Solution Explorer, click the Show All Files button. Open the LightShapeSelectionControl.Designer.cs or LightShapeSelectionControl.Designer.vb file, and remove the default definition of the xref:System.ComponentModel.Component.Dispose%2A method.

  10. Implement the LightShape property.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#360] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#360]

  11. Override the xref:System.Windows.Forms.Control.OnPaint%2A method. This implementation will draw a filled square and circle. It will also highlight the selected value by drawing a border around one shape or the other.

    [!code-csharpSystem.Windows.Forms.Design.DocumentDesigner#380] [!code-vbSystem.Windows.Forms.Design.DocumentDesigner#380]

Test your Custom Control in the Designer

At this point, you can build the MarqueeControlLibrary project. Test your implementation by creating a control that inherits from the MarqueeControl class and using it on a form.

To create a custom MarqueeControl implementation

  1. Open DemoMarqueeControl in the Windows Forms Designer. This creates an instance of the DemoMarqueeControl type and displays it in an instance of the MarqueeControlRootDesigner type.

  2. In the Toolbox, open the MarqueeControlLibrary Components tab. You will see the MarqueeBorder and MarqueeText controls available for selection.

  3. Drag an instance of the MarqueeBorder control onto the DemoMarqueeControl design surface. Dock this MarqueeBorder control to the parent control.

  4. Drag an instance of the MarqueeText control onto the DemoMarqueeControl design surface.

  5. Build the solution.

  6. Right-click the DemoMarqueeControl and from the shortcut menu select the Run Test option to start the animation. Click Stop Test to stop the animation.

  7. Open Form1 in Design view.

  8. Place two xref:System.Windows.Forms.Button controls on the form. Name them startButton and stopButton, and change the xref:System.Windows.Forms.Control.Text%2A property values to Start and Stop, respectively.

  9. Implement xref:System.Windows.Forms.Control.Click event handlers for both xref:System.Windows.Forms.Button controls.

  10. In the Toolbox, open the MarqueeControlTest Components tab. You will see the DemoMarqueeControl available for selection.

  11. Drag an instance of DemoMarqueeControl onto the Form1 design surface.

  12. In the xref:System.Windows.Forms.Control.Click event handlers, invoke the Start and Stop methods on the DemoMarqueeControl.

    Private Sub startButton_Click(sender As Object, e As System.EventArgs)
        Me.demoMarqueeControl1.Start()
    End Sub 'startButton_Click
    
    Private Sub stopButton_Click(sender As Object, e As System.EventArgs)
    Me.demoMarqueeControl1.Stop()
    End Sub 'stopButton_Click
    private void startButton_Click(object sender, System.EventArgs e)
    {
        this.demoMarqueeControl1.Start();
    }
    
    private void stopButton_Click(object sender, System.EventArgs e)
    {
        this.demoMarqueeControl1.Stop();
    }
  13. Set the MarqueeControlTest project as the startup project and run it. You will see the form displaying your DemoMarqueeControl. Select the Start button to start the animation. You should see the text flashing and the lights moving around the border.

Next steps

The MarqueeControlLibrary demonstrates a simple implementation of custom controls and associated designers. You can make this sample more sophisticated in several ways:

  • Change the property values for the DemoMarqueeControl in the designer. Add more MarqueBorder controls and dock them within their parent instances to create a nested effect. Experiment with different settings for the UpdatePeriod and the light-related properties.

  • Author your own implementations of IMarqueeWidget. You could, for example, create a flashing "neon sign" or an animated sign with multiple images.

  • Further customize the design-time experience. You could try shadowing more properties than xref:System.Windows.Forms.Control.Enabled%2A and xref:System.Windows.Forms.Control.Visible%2A, and you could add new properties. Add new designer verbs to simplify common tasks like docking child controls.

  • License the MarqueeControl.

  • Control how your controls are serialized and how code is generated for them. For more information, see Dynamic Source Code Generation and Compilation.

See also

  • xref:System.Windows.Forms.UserControl
  • xref:System.Windows.Forms.Design.ParentControlDesigner
  • xref:System.Windows.Forms.Design.DocumentDesigner
  • xref:System.ComponentModel.Design.IRootDesigner
  • xref:System.ComponentModel.Design.DesignerVerb
  • xref:System.Drawing.Design.UITypeEditor
  • xref:System.ComponentModel.BackgroundWorker