Walkthrough: Implementing a UI Type Editor

You can provide a custom design-time experience for complex property types by implementing a user interface (UI) type editor.

This walkthrough explains how to author your own UI type editor for a custom type and display the editing interface by using a PropertyGrid.

Tasks explained in this walkthrough include:

  • Defining a custom type.

  • Defining a view control for your UI type editor.

  • Defining a class that derives from UITypeEditor.

  • Overriding the GetEditStyle method to inform the PropertyGrid of the type of editor style that the editor will use.

  • Overriding the EditValue method to handle the user interface, user input processing, and value assignment.

To copy the code in this walkthrough as a single listing, see How to: Create a Windows Forms Control That Takes Advantage of Design-Time Features.

In order to complete this walkthrough, you will need:

  • Sufficient permissions to be able to create and run Windows Forms application projects on the computer where the .NET Framework is installed.

Your custom UI type editor will display a custom type. This type could be complex or simple. For this walkthrough, you will define a simple type with custom design-time editing behavior. This type is called MarqueeLightShape, and it is an enum with two values, Square and Circle.

To define a custom enumeration type

  • In the body of your Windows Forms control's definition, define the MarqueeLightShape type.

    // This defines the possible values for the MarqueeBorder 
    // control's LightShape property. 
    public enum MarqueeLightShape
    {
        Square,
        Circle
    }
    

Your custom UI type editor displays the editing interface using a Windows Forms control. This control is named LightShapeSelectionControl, and it derives from UserControl. Its constructor takes the current property value and a reference to the IWindowsFormsEditorService. The view control uses the CloseDropDown method on IWindowsFormsEditorService to close the drop-down window when the user clicks on a selection.

To define a view control

  • In the body of your Windows Forms control's definition, define the LightShapeSelectionControl control.

        // This control provides the custom UI for the LightShape property 
        // of the MarqueeBorder. It is used by the LightShapeEditor. 
        public class LightShapeSelectionControl : System.Windows.Forms.UserControl
    	{
            private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
            private IWindowsFormsEditorService editorService = null;
            private System.Windows.Forms.Panel squarePanel;
    		private System.Windows.Forms.Panel circlePanel;
    		
    		// Required designer variable. 
    		private System.ComponentModel.Container components = null;
    
            // This constructor takes a MarqueeLightShape value from the 
            // design-time environment, which will be used to display 
            // the initial state. 
    		public LightShapeSelectionControl( 
                MarqueeLightShape lightShape,
                IWindowsFormsEditorService editorService )
    		{
    			// This call is required by the designer.
    			InitializeComponent();
    
                // Cache the light shape value provided by the  
                // design-time environment. 
    			this.lightShapeValue = lightShape;
    
                // Cache the reference to the editor service. 
                this.editorService = editorService;
    
                // Handle the Click event for the two panels.  
    			this.squarePanel.Click += new EventHandler(squarePanel_Click);
    			this.circlePanel.Click += new EventHandler(circlePanel_Click);
    		}
    
            protected override void Dispose( bool disposing )
    		{
    			if( disposing )
    			{
                    // Be sure to unhook event handlers 
                    // to prevent "lapsed listener" leaks.
    				this.squarePanel.Click -= 
                        new EventHandler(squarePanel_Click);
    				this.circlePanel.Click -= 
                        new EventHandler(circlePanel_Click);
    
    				if(components != null)
    				{
    					components.Dispose();
    				}
    			}
    			base.Dispose( disposing );
    		}
    
            // LightShape is the property for which this control provides 
            // a custom user interface in the Properties window. 
    		public MarqueeLightShape LightShape
    		{
    			get
    			{
    				return this.lightShapeValue;
    			}
    			
    			set
    			{
    				if( this.lightShapeValue != value )
    				{
    					this.lightShapeValue = value;
    				}
    			}
    		}
    
            protected override void OnPaint(PaintEventArgs e)
    		{
    			base.OnPaint (e);
    
    			using( 
    				Graphics gSquare = this.squarePanel.CreateGraphics(),
    				gCircle = this.circlePanel.CreateGraphics() )
    			{	
                    // Draw a filled square in the client area of 
                    // the squarePanel control.
    				gSquare.FillRectangle(
                        Brushes.Red, 
                        0,
                        0,
                        this.squarePanel.Width,
                        this.squarePanel.Height
                        );
    
                    // If the Square option has been selected, draw a  
                    // border inside the squarePanel. 
    				if( this.lightShapeValue == MarqueeLightShape.Square )
    				{
    					gSquare.DrawRectangle( 
    						Pens.Black,
    						0,
    						0,
    						this.squarePanel.Width-1,
    						this.squarePanel.Height-1);
    				}
    
                    // Draw a filled circle in the client area of 
                    // the circlePanel control.
    				gCircle.Clear( this.circlePanel.BackColor );
    				gCircle.FillEllipse( 
                        Brushes.Blue, 
                        0,
                        0,
                        this.circlePanel.Width, 
                        this.circlePanel.Height
                        );
    
                    // If the Circle option has been selected, draw a  
                    // border inside the circlePanel. 
    				if( this.lightShapeValue == MarqueeLightShape.Circle )
    				{
    					gCircle.DrawRectangle( 
    						Pens.Black,
    						0,
    						0,
    						this.circlePanel.Width-1,
    						this.circlePanel.Height-1);
    				}
    			}	
    		}
    
            private void squarePanel_Click(object sender, EventArgs e)
    		{
    			this.lightShapeValue = MarqueeLightShape.Square;
    			
    			this.Invalidate( false );
    
                this.editorService.CloseDropDown();
    		}
    
    		private void circlePanel_Click(object sender, EventArgs e)
    		{
                this.lightShapeValue = MarqueeLightShape.Circle;
    
                this.Invalidate( false );
    
                this.editorService.CloseDropDown();
    		}
    
    		#region Component Designer generated code
    		/// <summary>  
    		/// Required method for Designer support - do not modify  
    		/// the contents of this method with the code editor. 
    		/// </summary> 
    		private void InitializeComponent()
    		{
                this.squarePanel = new System.Windows.Forms.Panel();
                this.circlePanel = new System.Windows.Forms.Panel();
                this.SuspendLayout();
    //  
    // squarePanel 
    //  
                this.squarePanel.Location = new System.Drawing.Point(8, 10);
                this.squarePanel.Name = "squarePanel";
                this.squarePanel.Size = new System.Drawing.Size(60, 60);
                this.squarePanel.TabIndex = 2;
    //  
    // circlePanel 
    //  
                this.circlePanel.Location = new System.Drawing.Point(80, 10);
                this.circlePanel.Name = "circlePanel";
                this.circlePanel.Size = new System.Drawing.Size(60, 60);
                this.circlePanel.TabIndex = 3;
    //  
    // LightShapeSelectionControl 
    //  
                this.Controls.Add(this.squarePanel);
                this.Controls.Add(this.circlePanel);
                this.Name = "LightShapeSelectionControl";
                this.Size = new System.Drawing.Size(150, 80);
                this.ResumeLayout(false);
    
            }
    		#endregion
    
    		
    	}
    

To implement UI type editor behavior, derive from the UITypeEditor base class. This class is called LightShapeEditor.

To define a UI type Editor Class

  1. Enable access to .NET Framework design-time support by referencing the System.Design assembly and importing the System.Drawing.Design and System.Windows.Forms.Design namespaces. For more information, see How to: Access Design-Time Support in Windows Forms.

  2. In the body of your Window Forms control's definition, define the LightShapeEditor class.

    // This class demonstrates the use of a custom UITypeEditor.  
    // It allows the MarqueeBorder control's LightShape property 
    // to be changed at design time using a customized UI element 
    // that is invoked by the Properties window. The UI is provided 
    // by the LightShapeSelectionControl class. 
    internal class LightShapeEditor : UITypeEditor
    {
    

The GetEditStyle method indicates to the design environment which kind of user interface your UI type editor implements. The possible values are defined in the UITypeEditorEditStyle type. The LightShapeEditor implements a DropDown UI type editor.

To override the GetEditStyle method

  • In the body of the LightShapeEditor definition, override the GetEditStyle method to return DropDown.

    public override UITypeEditorEditStyle GetEditStyle(
    System.ComponentModel.ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown;
    }
    

The EditValue method establishes the interaction between the design environment and the user interface for editing your custom type. The EditValue method creates an instance of the view control or the modal dialog box with which the user edits the value. When the user is finished editing, the EditValue method returns the value to the design environment.

In the case of a view control like LightShapeSelectionControl, the EditValue method may pass a reference to the IWindowsFormsEditorService to the view control. The view control can use this reference to close itself when the user selects a value. This is not necessary for a modal dialog box, because a form can close itself.

To override the EditValue method

  • In the body of the LightShapeEditor definition, override the EditValue method.

    public override object EditValue(
        ITypeDescriptorContext context,
        IServiceProvider provider,
        object value)
    {
        if (provider != null)
        {
            editorService =
                provider.GetService(
                typeof(IWindowsFormsEditorService))
                as IWindowsFormsEditorService;
        }
    
        if (editorService != null)
        {
            LightShapeSelectionControl selectionControl =
                new LightShapeSelectionControl(
                (MarqueeLightShape)value,
                editorService);
    
            editorService.DropDownControl(selectionControl);
    
            value = selectionControl.LightShape;
        }
    
        return value;
    }
    

You can provide a graphical representation of your property's value by overriding the PaintValue method.

To override the PaintValue method

  • In the body of the LightShapeEditor definition, override the PaintValue method. Also override the GetPaintValueSupported method to return true.

    // This method indicates to the design environment that 
    // the type editor will paint additional content in the 
    // LightShape entry in the PropertyGrid. 
    public override bool GetPaintValueSupported(
        ITypeDescriptorContext context)
    {  
        return true;
    }
    
    // This method paints a graphical representation of the  
    // selected value of the LightShpae property. 
    public override void PaintValue(PaintValueEventArgs e)
    {   
        MarqueeLightShape shape = (MarqueeLightShape)e.Value;
        using (Pen p = Pens.Black)
        {
            if (shape == MarqueeLightShape.Square)
            {
                e.Graphics.DrawRectangle(p, e.Bounds);
            }
            else
            {
                e.Graphics.DrawEllipse(p, e.Bounds);
            }
        }   
    }
    

When your UI type editor is ready for use in your custom control, attach the LightShapeEditor to a property, implement the property based on the MarqueeLightShape type, and apply the EditorAttribute to the property.

To attach your UI type editor to a property

  • In the body of your control's definition, declare a MarqueeLightShape property named LightShape. Also declare an instance field named lightShapeValue of type MarqueeLightShape to back the property. Apply the EditorAttribute to the property.

private MarqueeLightShape lightShapeValue;

[Category("Marquee")]
[Browsable(true)]
[EditorAttribute(typeof(LightShapeEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public MarqueeLightShape LightShape
{
    get
    {
        return this.lightShapeValue;
    }

    set
    {
        this.lightShapeValue = value;
    }
}

You can test your UI type editor by creating an instance of your custom control and attaching it to a PropertyGrid control using the SelectedObject property.

If you are using Visual Studio, you can create a new Windows Application project, reference your control's assembly, and add an instance of your control to the form. There is extensive support for this task in Visual Studio. Walkthrough: Automatically Populating the Toolbox with Custom Components
Walkthrough: Automatically Populating the Toolbox with Custom Components
Walkthrough: Automatically Populating the Toolbox with Custom Components
Walkthrough: Automatically Populating the Toolbox with Custom Components
Walkthrough: Automatically Populating the Toolbox with Custom Components

When the properties for your control are displayed at design time, you can select the LightShape property. When it is selected, a drop-down arrow (Properties Window Down Arrow) appears. When you click on the arrow, your view control appears beneath the property entry. Click on the circle or square to select the value. After you click, the view control dismisses itself, and the value you selected appears in the PropertyGrid.

Note Note

When you develop your custom UITypeEditor, it is recommended that you set the build number to increment with each build. This prevents older, cached versions of your UITypeEditor from being created in the design environment.

Once you have authored your own UI type editor, explore other ways to interact with a PropertyGrid and the design environment:

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft