.NET Framework Class Library  

UITypeEditor Class

Provides a base class that can be used to design value editors that can provide a user interface for representing and editing the values of objects of the supported data type(s).

For a list of all members of this type, see UITypeEditor Members.

System.Object
   System.Drawing.Design.UITypeEditor
      Derived classes

[Visual Basic]
Public Class UITypeEditor
[C#]
public class UITypeEditor
[C++]
public __gc class UITypeEditor
[JScript]
public class UITypeEditor

Thread Safety

Any public static (Shared in Visual Basic) members of this type are safe for multithreaded operations. Any instance members are not guaranteed to be thread safe.

Remarks

UITypeEditor provides a base class that you can derive from and extend to implement a custom type editor for the design-time environment. Custom type editors are useful in situations where a text-box value editor is insufficient to effectively select the values of certain types.

To implement a custom design-time UI type editor, you must at least:

You can add additional support for painting a value's representation in the Properties window by implementing the following:

For more information about enhancing design-time support, see Enhancing Design-Time Support. For more information on implementing a UITypeEditor, see Implementing a UI Type Editor.

Example

[Visual Basic, C#, C++] The following example UITypeEditor can be associated with an integer, double, or float property using an EditorAttribute to provide an angle selection interface from the properties window in design mode. This example uses the System.Windows.Forms.Design.IWindowsFormsEditorService interface to display a drop-down control in the properties window in design mode. Compile the code and add the assembly to the toolbox using the Customize Toolbox command on the Tools menu of Visual Studio .NET. Create an instance of the AngleTestControl and click the drop-down button next to the Angle property in the properties window to configure the property using the editor.

[Visual Basic] 
Option Strict Off
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Design
Imports System.Reflection
Imports System.Windows.Forms
Imports System.Windows.Forms.Design

' This UITypeEditor can be associated with Int32, Double and Single
' properties to provide a design-mode angle selection interface.
Public Class AngleEditor
   Inherits System.Drawing.Design.UITypeEditor
   
   Public Sub New()
    End Sub

    ' Indicates whether the UITypeEditor provides a form-based (modal) dialog, 
    ' drop down dialog, or no UI outside of the properties window.
    <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
    Public Overloads Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        Return UITypeEditorEditStyle.DropDown
    End Function

    ' Displays the UI for value selection.
    <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
    Public Overloads Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
        ' Return the value if the value is not of type Int32, Double and Single.
        If Not value.GetType() Is GetType(Double) Or value.GetType() Is GetType(Single) Or value.GetType() Is GetType(Integer) Then
            Return value
        End If
        ' Uses the IWindowsFormsEditorService to display a 
        ' drop-down UI in the Properties window.
        Dim edSvc As IWindowsFormsEditorService = CType(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService)
        If Not (edSvc Is Nothing) Then
            ' Display an angle selection control and retrieve the value.
            Dim angleControl As New AngleControl(System.Convert.ToDouble(value))
            edSvc.DropDownControl(angleControl)

            ' Return the value in the appropraite data format.
            If value Is GetType(Double) Then
                Return angleControl.angle
            ElseIf value Is GetType(Single) Then
                Return System.Convert.ToSingle(angleControl.angle)
            ElseIf value Is GetType(Integer) Then
                Return System.Convert.ToInt32(angleControl.angle)
            End If
        End If
        Return value
    End Function

    ' Draws a representation of the property's value.
    <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
    Public Overloads Overrides Sub PaintValue(ByVal e As System.Drawing.Design.PaintValueEventArgs)
        Dim normalX As Integer = e.Bounds.Width / 2
        Dim normalY As Integer = e.Bounds.Height / 2

        ' Fill background and ellipse and center point.
        e.Graphics.FillRectangle(New SolidBrush(Color.DarkBlue), e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)
        e.Graphics.FillEllipse(New SolidBrush(Color.White), e.Bounds.X + 1, e.Bounds.Y + 1, e.Bounds.Width - 3, e.Bounds.Height - 3)
        e.Graphics.FillEllipse(New SolidBrush(Color.SlateGray), normalX + e.Bounds.X - 1, normalY + e.Bounds.Y - 1, 3, 3)

        ' Draw line along the current angle.
        Dim radians As Double = System.Convert.ToDouble(e.Value) * Math.PI / System.Convert.ToDouble(180)
        e.Graphics.DrawLine(New Pen(New SolidBrush(Color.Red), 1), normalX + e.Bounds.X, normalY + e.Bounds.Y, e.Bounds.X + (normalX + System.Convert.ToInt32(System.Convert.ToDouble(normalX) * Math.Cos(radians))), e.Bounds.Y + (normalY + System.Convert.ToInt32(System.Convert.ToDouble(normalY) * Math.Sin(radians))))
    End Sub

    ' Indicates whether the UITypeEditor supports painting a 
    ' representation of a property's value.
    <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
    Public Overloads Overrides Function GetPaintValueSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function
End Class

' Provides a user interface for adjusting an angle value.
Friend Class AngleControl
    Inherits System.Windows.Forms.UserControl

    ' Stores the angle.
    Public angle As Double
    ' Stores the rotation offset.
    Private rotation As Integer = 0
    ' Control state tracking variables.
    Private dbx As Integer = -10
    Private dby As Integer = -10
    Private overButton As Integer = -1

    Public Sub New(ByVal initial_angle As Double)
        Me.angle = initial_angle
        Me.SetStyle(ControlStyles.DoubleBuffer Or ControlStyles.AllPaintingInWmPaint, True)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        ' Set angle origin point at center of control.
        Dim originX As Integer = Me.Width / 2
        Dim originY As Integer = Me.Height / 2

        ' Fill background and ellipse and center point.
        e.Graphics.FillRectangle(New SolidBrush(Color.DarkBlue), 0, 0, Me.Width, Me.Height)
        e.Graphics.FillEllipse(New SolidBrush(Color.White), 1, 1, Me.Width - 3, Me.Height - 3)
        e.Graphics.FillEllipse(New SolidBrush(Color.SlateGray), originX - 1, originY - 1, 3, 3)

        ' Draw angle markers.
        Dim startangle As Integer = (270 - rotation) Mod 360
        e.Graphics.DrawString(startangle.ToString(), New Font("Arial", 8), New SolidBrush(Color.DarkGray), Me.Width / 2 - 10, 10)
        startangle = (startangle + 90) Mod 360
        e.Graphics.DrawString(startangle.ToString(), New Font("Arial", 8), New SolidBrush(Color.DarkGray), Me.Width - 18, Me.Height / 2 - 6)
        startangle = (startangle + 90) Mod 360
        e.Graphics.DrawString(startangle.ToString(), New Font("Arial", 8), New SolidBrush(Color.DarkGray), Me.Width / 2 - 6, Me.Height - 18)
        startangle = (startangle + 90) Mod 360
        e.Graphics.DrawString(startangle.ToString(), New Font("Arial", 8), New SolidBrush(Color.DarkGray), 10, Me.Height / 2 - 6)

        ' Draw line along the current angle.   
        Dim radians As Double = ((angle + rotation + 360) Mod 360) * Math.PI / System.Convert.ToDouble(180)
        e.Graphics.DrawLine(New Pen(New SolidBrush(Color.Red), 1), originX, originY, originX + System.Convert.ToInt32(System.Convert.ToDouble(originX) * Math.Cos(radians)), originY + System.Convert.ToInt32(System.Convert.ToDouble(originY) * Math.Sin(radians)))

        ' Output angle information.
        e.Graphics.FillRectangle(New SolidBrush(Color.Gray), Me.Width - 84, 3, 82, 13)
        e.Graphics.DrawString("Angle: " + angle.ToString("F4"), New Font("Arial", 8), New SolidBrush(Color.Yellow), Me.Width - 84, 2)
        ' Draw square at mouse position of last angle adjustment.
        e.Graphics.DrawRectangle(New Pen(New SolidBrush(Color.Black), 1), dbx - 2, dby - 2, 4, 4)
        ' Draw rotation adjustment buttons.
        If overButton = 1 Then
            e.Graphics.FillRectangle(New SolidBrush(Color.Green), Me.Width - 28, Me.Height - 14, 12, 12)
            e.Graphics.FillRectangle(New SolidBrush(Color.Gray), 2, Me.Height - 13, 110, 12)
            e.Graphics.DrawString("Rotate 90 degrees left", New Font("Arial", 8), New SolidBrush(Color.White), 2, Me.Height - 14)
        Else
            e.Graphics.FillRectangle(New SolidBrush(Color.DarkGreen), Me.Width - 28, Me.Height - 14, 12, 12)
        End If
        If overButton = 2 Then
            e.Graphics.FillRectangle(New SolidBrush(Color.Green), Me.Width - 14, Me.Height - 14, 12, 12)
            e.Graphics.FillRectangle(New SolidBrush(Color.Gray), 2, Me.Height - 13, 116, 12)
            e.Graphics.DrawString("Rotate 90 degrees right", New Font("Arial", 8), New SolidBrush(Color.White), 2, Me.Height - 14)
        Else
            e.Graphics.FillRectangle(New SolidBrush(Color.DarkGreen), Me.Width - 14, Me.Height - 14, 12, 12)
        End If
        e.Graphics.DrawEllipse(New Pen(New SolidBrush(Color.White), 1), Me.Width - 11, Me.Height - 11, 6, 6)
        e.Graphics.DrawEllipse(New Pen(New SolidBrush(Color.White), 1), Me.Width - 25, Me.Height - 11, 6, 6)
        If overButton = 1 Then
            e.Graphics.FillRectangle(New SolidBrush(Color.Green), Me.Width - 25, Me.Height - 6, 4, 4)
        Else
            e.Graphics.FillRectangle(New SolidBrush(Color.DarkGreen), Me.Width - 25, Me.Height - 6, 4, 4)
        End If
        If overButton = 2 Then
            e.Graphics.FillRectangle(New SolidBrush(Color.Green), Me.Width - 8, Me.Height - 6, 4, 4)
        Else
            e.Graphics.FillRectangle(New SolidBrush(Color.DarkGreen), Me.Width - 8, Me.Height - 6, 4, 4)
        End If
        e.Graphics.FillPolygon(New SolidBrush(Color.White), New Point() {New Point(Me.Width - 7, Me.Height - 8), New Point(Me.Width - 3, Me.Height - 8), New Point(Me.Width - 5, Me.Height - 4)})
        e.Graphics.FillPolygon(New SolidBrush(Color.White), New Point() {New Point(Me.Width - 26, Me.Height - 8), New Point(Me.Width - 21, Me.Height - 8), New Point(Me.Width - 25, Me.Height - 4)})
    End Sub

    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
        ' Handle rotation adjustment button clicks.
        If e.X >= Me.Width - 28 AndAlso e.X <= Me.Width - 2 AndAlso e.Y >= Me.Height - 14 AndAlso e.Y <= Me.Height - 2 Then
            If e.X <= Me.Width - 16 Then
                rotation -= 90
            ElseIf e.X >= Me.Width - 14 Then
                rotation += 90
            End If
            If rotation < 0 Then
                rotation += 360
            End If
            rotation = rotation Mod 360
            dbx = -10
            dby = -10
        Else
            UpdateAngle(e.X, e.Y)
        End If
        Me.Refresh()
    End Sub

    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            UpdateAngle(e.X, e.Y)
            overButton = -1
        ElseIf e.X >= Me.Width - 28 AndAlso e.X <= Me.Width - 16 AndAlso e.Y >= Me.Height - 14 AndAlso e.Y <= Me.Height - 2 Then
            overButton = 1
        ElseIf e.X >= Me.Width - 14 AndAlso e.X <= Me.Width - 2 AndAlso e.Y >= Me.Height - 14 AndAlso e.Y <= Me.Height - 2 Then
            overButton = 2
        Else
            overButton = -1
        End If
        Me.Refresh()
    End Sub

    Private Sub UpdateAngle(ByVal mx As Integer, ByVal my As Integer)
        ' Store mouse coordinates.
        dbx = mx
        dby = my

        ' Translate y coordinate input to GetAngle function to correct for ellipsoid distortion.
        Dim widthToHeightRatio As Double = System.Convert.ToDouble(Me.Width) / System.Convert.ToDouble(Me.Height)
        Dim tmy As Integer
        If my = 0 Then
            tmy = my
        ElseIf my < Me.Height / 2 Then
            tmy = Me.Height / 2 - Fix((Me.Height / 2 - my) * widthToHeightRatio)
        Else
            tmy = Me.Height / 2 + Fix(System.Convert.ToDouble(my - Me.Height / 2) * widthToHeightRatio)
        End If
        ' Retrieve updated angle based on rise over run.
        angle = (GetAngle(Me.Width / 2, Me.Height / 2, mx, tmy) - rotation) Mod 360
    End Sub

    Private Function GetAngle(ByVal x1 As Integer, ByVal y1 As Integer, ByVal x2 As Integer, ByVal y2 As Integer) As Double
        Dim degrees As Double

        ' Avoid divide by zero run values.
        If x2 - x1 = 0 Then
            If y2 > y1 Then
                degrees = 90
            Else
                degrees = 270
            End If
        Else
            ' Calculate angle from offset.
            Dim riseoverrun As Double = System.Convert.ToDouble(y2 - y1) / System.Convert.ToDouble(x2 - x1)
            Dim radians As Double = Math.Atan(riseoverrun)
            degrees = radians * (System.Convert.ToDouble(180) / Math.PI)

            ' Handle quadrant specific transformations.           
            If x2 - x1 < 0 OrElse y2 - y1 < 0 Then
                degrees += 180
            End If
            If x2 - x1 > 0 AndAlso y2 - y1 < 0 Then
                degrees -= 180
            End If
            If degrees < 0 Then
                degrees += 360
            End If
        End If
        Return degrees
    End Function
End Class

Public Class AngleEditorTestControl
    Inherits System.Windows.Forms.UserControl
    Private int_angle As Double

    <BrowsableAttribute(True), EditorAttribute(GetType(AngleEditor), GetType(System.Drawing.Design.UITypeEditor))> _
    Public Property Angle() As Double
        Get
            Return int_angle
        End Get
        Set(ByVal Value As Double)
            int_angle = Value
        End Set
    End Property

    Public Sub New()
        int_angle = 90
        Me.Size = New Size(190, 42)
        Me.BackColor = Color.Beige
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        If Me.DesignMode Then
            e.Graphics.DrawString("Use the Properties Window to access", New Font("Arial", 8), New SolidBrush(Color.Black), 3, 2)
            e.Graphics.DrawString("the AngleEditor UITypeEditor by", New Font("Arial", 8), New SolidBrush(Color.Black), 3, 14)
            e.Graphics.DrawString("configuring the ""Angle"" property.", New Font("Arial", 8), New SolidBrush(Color.Black), 3, 26)
        Else
            e.Graphics.DrawString("This example requires design mode.", New Font("Arial", 8), New SolidBrush(Color.Black), 3, 2)
        End If
    End Sub
End Class

[C#] 
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace AngleEditor
{
    // This UITypeEditor can be associated with Int32, Double and Single
    // properties to provide a design-mode angle selection interface.
    public class AngleEditor : System.Drawing.Design.UITypeEditor
    {        
        public AngleEditor()
        {
        }

        // Indicates whether the UITypeEditor provides a form-based (modal) dialog, 
        // drop down dialog, or no UI outside of the properties window.
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")] 
        public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }

        // Displays the UI for value selection.
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, System.IServiceProvider provider, object value)
        {            
            // Return the value if the value is not of type Int32, Double and Single.
            if( value.GetType() != typeof(double) && value.GetType() != typeof(float) && value.GetType() != typeof(int) )
                return value;

            // Uses the IWindowsFormsEditorService to display a 
            // drop-down UI in the Properties window.
            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if( edSvc != null )
            {
                // Display an angle selection control and retrieve the value.
                AngleControl angleControl = new AngleControl((double)value);
                edSvc.DropDownControl( angleControl );

                // Return the value in the appropraite data format.
                if( value.GetType() == typeof(double) )
                    return angleControl.angle;
                else if( value.GetType() == typeof(float) )
                    return (float)angleControl.angle;
                else if( value.GetType() == typeof(int) )
                    return (int)angleControl.angle;
            }
            return value;
        }

        // Draws a representation of the property's value.
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]   
        public override void PaintValue(System.Drawing.Design.PaintValueEventArgs e)
        {
            int normalX = (e.Bounds.Width/2);
            int normalY = (e.Bounds.Height/2);
            
            // Fill background and ellipse and center point.
            e.Graphics.FillRectangle(new SolidBrush(Color.DarkBlue), e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);            
            e.Graphics.FillEllipse(new SolidBrush(Color.White), e.Bounds.X+1, e.Bounds.Y+1, e.Bounds.Width-3, e.Bounds.Height-3);
            e.Graphics.FillEllipse(new SolidBrush(Color.SlateGray), normalX+e.Bounds.X-1, normalY+e.Bounds.Y-1, 3, 3);
            
            // Draw line along the current angle.
            double radians = ((double)e.Value*Math.PI) / (double)180;
            e.Graphics.DrawLine( new Pen(new SolidBrush(Color.Red), 1), normalX+e.Bounds.X, normalY+e.Bounds.Y, 
                e.Bounds.X+ ( normalX + (int)( (double)normalX * Math.Cos( radians ) ) ),
                e.Bounds.Y+ ( normalY + (int)( (double)normalY * Math.Sin( radians ) ) ) );
        }
        
        // Indicates whether the UITypeEditor supports painting a 
        // representation of a property's value.
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
        public override bool GetPaintValueSupported(System.ComponentModel.ITypeDescriptorContext context)
        {
            return true;
        }
    }

    // Provides a user interface for adjusting an angle value.
    internal class AngleControl : System.Windows.Forms.UserControl
    {
        // Stores the angle.
        public double angle;
        // Stores the rotation offset.
        private int rotation = 0;
        // Control state tracking variables.
        private int dbx = -10;
        private int dby = -10;
        private int overButton = -1;
        
        public AngleControl(double initial_angle)
        {
            this.angle = initial_angle;                 
            this.SetStyle( ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true );
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            // Set angle origin point at center of control.
            int originX = (this.Width/2);
            int originY = (this.Height/2);            
            
            // Fill background and ellipse and center point.
            e.Graphics.FillRectangle(new SolidBrush(Color.DarkBlue), 0, 0, this.Width, this.Height);
            e.Graphics.FillEllipse(new SolidBrush(Color.White), 1, 1, this.Width-3, this.Height-3);            
            e.Graphics.FillEllipse(new SolidBrush(Color.SlateGray), originX-1, originY-1, 3, 3);
            
            // Draw angle markers.
            int startangle = (270-rotation)%360;
            e.Graphics.DrawString(startangle.ToString(), new Font("Arial", 8), new SolidBrush(Color.DarkGray), (this.Width/2)-10, 10);
            startangle = (startangle+90)%360;
            e.Graphics.DrawString(startangle.ToString(), new Font("Arial", 8), new SolidBrush(Color.DarkGray), this.Width-18, (this.Height/2)-6);
            startangle = (startangle+90)%360;
            e.Graphics.DrawString(startangle.ToString(), new Font("Arial", 8), new SolidBrush(Color.DarkGray), (this.Width/2)-6, this.Height-18);
            startangle = (startangle+90)%360;
            e.Graphics.DrawString(startangle.ToString(), new Font("Arial", 8), new SolidBrush(Color.DarkGray), 10, (this.Height/2)-6);

            // Draw line along the current angle.         
            double radians = ((((angle+rotation)+360)%360)*Math.PI) / (double)180;
            e.Graphics.DrawLine( new Pen(new SolidBrush(Color.Red), 1), originX, originY, 
                originX + (int)( (double)originX * (double)Math.Cos( radians )   ),
                originY + (int)( (double)originY * (double)Math.Sin( radians ) ) );
            
            // Output angle information.
            e.Graphics.FillRectangle(new SolidBrush(Color.Gray), this.Width-84, 3, 82, 13);
            e.Graphics.DrawString("Angle: "+angle.ToString("F4"), new Font("Arial", 8), new SolidBrush(Color.Yellow), this.Width-84, 2);
            // Draw square at mouse position of last angle adjustment.
            e.Graphics.DrawRectangle(new Pen(new SolidBrush(Color.Black), 1), dbx-2, dby-2, 4, 4);
            // Draw rotation adjustment buttons.
            if( overButton == 1 )
            {
                e.Graphics.FillRectangle(new SolidBrush(Color.Green), this.Width-28, this.Height-14, 12, 12);
                e.Graphics.FillRectangle(new SolidBrush(Color.Gray), 2, this.Height-13, 110, 12);    
                e.Graphics.DrawString("Rotate 90 degrees left", new Font("Arial", 8), new SolidBrush(Color.White), 2, this.Height-14);
            }
            else            
                e.Graphics.FillRectangle(new SolidBrush(Color.DarkGreen), this.Width-28, this.Height-14, 12, 12);
            if( overButton == 2 )
            {
                e.Graphics.FillRectangle(new SolidBrush(Color.Green), this.Width-14, this.Height-14, 12, 12);
                e.Graphics.FillRectangle(new SolidBrush(Color.Gray), 2, this.Height-13, 116, 12);    
                e.Graphics.DrawString("Rotate 90 degrees right", new Font("Arial", 8), new SolidBrush(Color.White), 2, this.Height-14);
            }
            else
                e.Graphics.FillRectangle(new SolidBrush(Color.DarkGreen), this.Width-14, this.Height-14, 12, 12);
            e.Graphics.DrawEllipse(new Pen(new SolidBrush(Color.White), 1), this.Width-11, this.Height-11, 6, 6);
            e.Graphics.DrawEllipse(new Pen(new SolidBrush(Color.White), 1), this.Width-25, this.Height-11, 6, 6);
            if( overButton == 1 )
                e.Graphics.FillRectangle(new SolidBrush(Color.Green), this.Width-25, this.Height-6, 4, 4);
            else
                e.Graphics.FillRectangle(new SolidBrush(Color.DarkGreen), this.Width-25, this.Height-6, 4, 4);
            if( overButton == 2 )
                e.Graphics.FillRectangle(new SolidBrush(Color.Green), this.Width-8, this.Height-6, 4, 4);
            else
                e.Graphics.FillRectangle(new SolidBrush(Color.DarkGreen), this.Width-8, this.Height-6, 4, 4);
            e.Graphics.FillPolygon(new SolidBrush(Color.White), new Point[] { new Point(this.Width-7, this.Height-8), new Point(this.Width-3, this.Height-8), new Point(this.Width-5, this.Height-4) });
            e.Graphics.FillPolygon(new SolidBrush(Color.White), new Point[] { new Point(this.Width-26, this.Height-8), new Point(this.Width-21, this.Height-8), new Point(this.Width-25, this.Height-4) });            
        }

        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {            
            // Handle rotation adjustment button clicks.
            if( e.X >= this.Width-28 && e.X <= this.Width-2 && e.Y >= this.Height-14 && e.Y <= this.Height-2 )
            {
                if( e.X <= this.Width-16 )
                    rotation -= 90;
                else if( e.X >= this.Width-14 )
                    rotation += 90;
                if( rotation < 0 )
                    rotation += 360;
                rotation = rotation%360;
                dbx=-10;
                dby=-10;
            }
            else
                UpdateAngle(e.X, e.Y);                
            this.Refresh();        
        }

        protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            if( e.Button == MouseButtons.Left )
            {
                UpdateAngle(e.X, e.Y);                                        
                overButton = -1;                
            }
            else if( e.X >= this.Width-28 && e.X <= this.Width-16 && e.Y >= this.Height-14 && e.Y <= this.Height-2 )
                overButton = 1;
            else if( e.X >= this.Width-14 && e.X <= this.Width-2 && e.Y >= this.Height-14 && e.Y <= this.Height-2 )
                overButton = 2;
            else
                overButton = -1;
            this.Refresh();
        }

        private void UpdateAngle(int mx, int my)
        {
            // Store mouse coordinates.
            dbx = mx;
            dby = my;

            // Translate y coordinate input to GetAngle function to correct for ellipsoid distortion.
            double widthToHeightRatio =  (double)this.Width/(double)this.Height;                        
            int tmy;
            if( my == 0 )
                tmy = my;
            else if( my < this.Height/2 )
                tmy = (this.Height/2)-(int)(((this.Height/2)-my)*widthToHeightRatio);                
            else
                tmy = (this.Height/2)+(int)((double)(my-(this.Height/2))*widthToHeightRatio);
            
            // Retrieve updated angle based on rise over run.
            angle = (GetAngle(this.Width/2, this.Height/2, mx, tmy)-rotation)%360;                                    
        }

        private double GetAngle(int x1, int y1, int x2, int y2)
        {  
            double degrees;
           
            // Avoid divide by zero run values.
            if( x2-x1 == 0 )
            {
                if( y2 > y1 )
                    degrees = 90;
                else
                    degrees = 270;
            }
            else
            {
                // Calculate angle from offset.
                double riseoverrun = (double)(y2-y1)/(double)(x2-x1);
                double radians = Math.Atan( riseoverrun );
                degrees = radians * ((double)180/Math.PI);
                
                // Handle quadrant specific transformations.       
                if( (x2-x1) < 0 || (y2-y1) < 0 )
                    degrees += 180;
                if( (x2-x1) > 0 && (y2-y1) < 0 )
                    degrees -= 180;
                if( degrees < 0 )
                    degrees += 360;
            }
            return degrees;
        }
    }

    public class AngleEditorTestControl : System.Windows.Forms.UserControl
    {
        private double int_angle;

        [BrowsableAttribute(true)]
        [EditorAttribute(typeof(AngleEditor), typeof(System.Drawing.Design.UITypeEditor))]        
        public double Angle
        {
            get
            { return int_angle; }
            set
            { int_angle = value; }
        }        

        public AngleEditorTestControl()
        {
            int_angle = 90;
            this.Size = new Size(190, 42);
            this.BackColor = Color.Beige;            
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            if( this.DesignMode )
            {
                e.Graphics.DrawString("Use the Properties Window to access", new Font("Arial", 8), new SolidBrush(Color.Black), 3,2);
                e.Graphics.DrawString("the AngleEditor UITypeEditor by", new Font("Arial", 8), new SolidBrush(Color.Black), 3,14);
                e.Graphics.DrawString("configuring the \"Angle\" property.", new Font("Arial", 8), new SolidBrush(Color.Black), 3,26);
            }
            else
                e.Graphics.DrawString("This example requires design mode.", new Font("Arial", 8), new SolidBrush(Color.Black), 3,2);
        }
    }
}

[C++] 
#using <mscorlib.dll>
#using <System.Windows.Forms.dll>
#using <System.Drawing.dll>
#using <System.dll>

using namespace System;
using namespace System::ComponentModel;
using namespace System::Drawing;
using namespace System::Drawing::Design;
using namespace System::Windows::Forms;
using namespace System::Windows::Forms::Design;

namespace AngleEditor {
   // Provides a user interface for adjusting an angle value.
   __gc class AngleControl : public System::Windows::Forms::UserControl {
      // Stores the angle.
   public:
      double  angle;
      // Stores the rotation offset.
   private:
      int  rotation;
      // Control state tracking variables.
      int  dbx;
      int  dby;
      int  overButton;

   public:
      AngleControl(double initial_angle) {
         this->angle = initial_angle;
         this->SetStyle(static_cast<ControlStyles>(ControlStyles::DoubleBuffer | ControlStyles::AllPaintingInWmPaint), true);
         rotation = 0;
         dbx = -10;
         dby = -10;
         overButton = -1;
      }

   protected:
      void OnPaint(System::Windows::Forms::PaintEventArgs* e) {
         // Set angle origin point at center of control.
         int originX = (this->Width/2);
         int originY = (this->Height/2);

         // Fill background and ellipse and center point.
         e->Graphics->FillRectangle(new SolidBrush(Color::DarkBlue), 0, 0, this->Width, this->Height);
         e->Graphics->FillEllipse(new SolidBrush(Color::White), 1, 1, this->Width-3, this->Height-3);
         e->Graphics->FillEllipse(new SolidBrush(Color::SlateGray), originX-1, originY-1, 3, 3);

         // Draw angle markers.
         int startangle = (270-rotation)%360;
         e->Graphics->DrawString(startangle.ToString(), new System::Drawing::Font(S"Arial", 8.0), new SolidBrush(Color::DarkGray), (float)(this->Width/2)-10, (float)10);
         startangle = (startangle+90)%360;
         e->Graphics->DrawString(startangle.ToString(), new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::DarkGray), (float)this->Width-18, (float)(this->Height/2)-6);
         startangle = (startangle+90)%360;
         e->Graphics->DrawString(startangle.ToString(), new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::DarkGray), ((float)this->Width/2)-6, (float)this->Height-18);
         startangle = (startangle+90)%360;
         e->Graphics->DrawString(startangle.ToString(), new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::DarkGray), (float)10, (float)(this->Height/2)-6);

         // Draw line along the current angle.
         double radians = ((((int)(angle+rotation)+360)%360)*Math::PI) / (double)180;
         e->Graphics->DrawLine(new Pen(new SolidBrush(Color::Red), 1), originX, originY,
            originX + (int)((double)originX * (double)Math::Cos(radians)),
            originY + (int)((double)originY * (double)Math::Sin(radians)));

         // Output angle information.
         e->Graphics->FillRectangle(new SolidBrush(Color::Gray), this->Width-84, 3, 82, 13);
         e->Graphics->DrawString(String::Format(S"Angle: {0}", angle.ToString(S"F4")), new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::Yellow), (float)this->Width-84, (float)2);
         // Draw square at mouse position of last angle adjustment.
         e->Graphics->DrawRectangle(new Pen(new SolidBrush(Color::Black), 1), dbx-2, dby-2, 4, 4);
         // Draw rotation adjustment buttons.
         if (overButton == 1) {
            e->Graphics->FillRectangle(new SolidBrush(Color::Green), this->Width-28, this->Height-14, 12, 12);
            e->Graphics->FillRectangle(new SolidBrush(Color::Gray), 2, this->Height-13, 110, 12);
            e->Graphics->DrawString(S"Rotate 90 degrees left", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::White), (float)2, (float)this->Height-14);
         } else
            e->Graphics->FillRectangle(new SolidBrush(Color::DarkGreen), this->Width-28, this->Height-14, 12, 12);
         if (overButton == 2) {
            e->Graphics->FillRectangle(new SolidBrush(Color::Green), this->Width-14, this->Height-14, 12, 12);
            e->Graphics->FillRectangle(new SolidBrush(Color::Gray), 2, this->Height-13, 116, 12);
            e->Graphics->DrawString(S"Rotate 90 degrees right", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::White), (float)2, (float)this->Height-14);
         } else
            e->Graphics->FillRectangle(new SolidBrush(Color::DarkGreen), this->Width-14, this->Height-14, 12, 12);
         e->Graphics->DrawEllipse(new Pen(new SolidBrush(Color::White), 1), this->Width-11, this->Height-11, 6, 6);
         e->Graphics->DrawEllipse(new Pen(new SolidBrush(Color::White), 1), this->Width-25, this->Height-11, 6, 6);
         if (overButton == 1)
            e->Graphics->FillRectangle(new SolidBrush(Color::Green), this->Width-25, this->Height-6, 4, 4);
         else
            e->Graphics->FillRectangle(new SolidBrush(Color::DarkGreen), this->Width-25, this->Height-6, 4, 4);
         if (overButton == 2)
            e->Graphics->FillRectangle(new SolidBrush(Color::Green), this->Width-8, this->Height-6, 4, 4);
         else
            e->Graphics->FillRectangle(new SolidBrush(Color::DarkGreen), this->Width-8, this->Height-6, 4, 4);

         Point temp0 [] = { Point(this->Width-7, this->Height-8),  Point(this->Width-3, this->Height-8),  Point(this->Width-5, this->Height-4)};
         e->Graphics->FillPolygon(new SolidBrush(Color::White), temp0);

         Point temp1 [] = { Point(this->Width-26, this->Height-8),  Point(this->Width-21, this->Height-8),  Point(this->Width-25, this->Height-4)};
         e->Graphics->FillPolygon(new SolidBrush(Color::White), temp1);
      }

      void OnMouseDown(System::Windows::Forms::MouseEventArgs* e) {
         // Handle rotation adjustment button clicks.
         if (e->X >= this->Width-28 && e->X <= this->Width-2 && e->Y >= this->Height-14 && e->Y <= this->Height-2) {
            if (e->X <= this->Width-16)
               rotation -= 90;
            else if (e->X >= this->Width-14)
               rotation += 90;
            if (rotation < 0)
               rotation += 360;
            rotation = rotation%360;
            dbx=-10;
            dby=-10;
         } else
            UpdateAngle(e->X, e->Y);
         this->Refresh();
      }

      void OnMouseMove(System::Windows::Forms::MouseEventArgs* e) {
         if (e->Button == MouseButtons::Left) {
            UpdateAngle(e->X, e->Y);
            overButton = -1;
         } else if (e->X >= this->Width-28 && e->X <= this->Width-16 && e->Y >= this->Height-14 && e->Y <= this->Height-2)
            overButton = 1;
         else if (e->X >= this->Width-14 && e->X <= this->Width-2 && e->Y >= this->Height-14 && e->Y <= this->Height-2)
            overButton = 2;
         else
            overButton = -1;
         this->Refresh();
      }

   private:
      void UpdateAngle(int mx, int my) {
         // Store mouse coordinates.
         dbx = mx;
         dby = my;

         // Translate y coordinate input to GetAngle function to correct for ellipsoid distortion.
         double widthToHeightRatio =  (double)this->Width/(double)this->Height;
         int tmy;
         if (my == 0)
            tmy = my;
         else if (my < this->Height/2)
            tmy = (this->Height/2)-(int)(((this->Height/2)-my)*widthToHeightRatio);
         else
            tmy = (this->Height/2)+(int)((double)(my-(this->Height/2))*widthToHeightRatio);

         // Retrieve updated angle based on rise over run.
         angle = (int)(GetAngle(this->Width/2, this->Height/2, mx, tmy)-rotation)%360;
      }

      double GetAngle(int x1, int y1, int x2, int y2) {
         double degrees;

         // Avoid divide by zero run values.
         if (x2-x1 == 0) {
            if (y2 > y1)
               degrees = 90;
            else
               degrees = 270;
         } else {
            // Calculate angle from offset.
            double riseoverrun = (double)(y2-y1)/(double)(x2-x1);
            double radians = Math::Atan(riseoverrun);
            degrees = radians * ((double)180/Math::PI);

            // Handle quadrant specific transformations.
            if ((x2-x1) < 0 || (y2-y1) < 0)
               degrees += 180;
            if ((x2-x1) > 0 && (y2-y1) < 0)
               degrees -= 180;
            if (degrees < 0)
               degrees += 360;
         }
         return degrees;
      }
   };

   // This UITypeEditor can be associated with Int32, Double and Single
   // properties to provide a design-mode angle selection interface.
public __gc class AngleEditor : public System::Drawing::Design::UITypeEditor {
public:
   AngleEditor() {
   }

   // Indicates whether the UITypeEditor provides a form-based (modal) dialog,
   // drop down dialog, or no UI outside of the properties window.
   System::Drawing::Design::UITypeEditorEditStyle GetEditStyle(System::ComponentModel::ITypeDescriptorContext* context) {
      return UITypeEditorEditStyle::DropDown;
   }

   // Displays the UI for value selection.
   Object* EditValue(System::ComponentModel::ITypeDescriptorContext* context, System::IServiceProvider* provider, Object* value) {
      // Return the value if the value is not of type Int32, Double and Single.
      if (value->GetType() != __typeof(double) && value->GetType() != __typeof(float) && value->GetType() != __typeof(int))
         return value;

      // Uses the IWindowsFormsEditorService* to display a
      // drop-down UI in the Properties window.
      IWindowsFormsEditorService* edSvc = dynamic_cast<IWindowsFormsEditorService*>(provider->GetService(__typeof(IWindowsFormsEditorService)));
      if (edSvc != 0) {
         // Display an angle selection control and retrieve the value.
         AngleControl* angleControl = new AngleControl(*dynamic_cast<double __gc *>(value));
         edSvc->DropDownControl(angleControl);

         // Return the value in the appropraite data format.
         if (value->GetType() == __typeof(double))
            return __box(angleControl->angle);
         else if (value->GetType() == __typeof(float))
            return __box((float)angleControl->angle);
         else if (value->GetType() == __typeof(int))
            return __box((int)angleControl->angle);
      }
      return value;
   }

   // Draws a representation of the property's value.
   void PaintValue(System::Drawing::Design::PaintValueEventArgs* e) {
      int normalX = (e->Bounds.Width/2);
      int normalY = (e->Bounds.Height/2);

      // Fill background and ellipse and center point.
      e->Graphics->FillRectangle(new SolidBrush(Color::DarkBlue), e->Bounds.X, e->Bounds.Y, e->Bounds.Width, e->Bounds.Height);
      e->Graphics->FillEllipse(new SolidBrush(Color::White), e->Bounds.X+1, e->Bounds.Y+1, e->Bounds.Width-3, e->Bounds.Height-3);
      e->Graphics->FillEllipse(new SolidBrush(Color::SlateGray), normalX+e->Bounds.X-1, normalY+e->Bounds.Y-1, 3, 3);

      // Draw line along the current angle.
      double radians = (*dynamic_cast<double __gc *>(e->Value)*Math::PI) / (double)180;
      e->Graphics->DrawLine(new Pen(new SolidBrush(Color::Red), 1), normalX+e->Bounds.X, normalY+e->Bounds.Y,
         e->Bounds.X+ (normalX + (int)((double)normalX * Math::Cos(radians))),
         e->Bounds.Y+ (normalY + (int)((double)normalY * Math::Sin(radians))));
   }

   // Indicates whether the UITypeEditor supports painting a
   // representation of a property's value.
   bool GetPaintValueSupported(System::ComponentModel::ITypeDescriptorContext* context) {
      return true;
   }
};

public __gc class AngleEditorTestControl : public System::Windows::Forms::UserControl {
private:
   double  int_angle;

public:
   [BrowsableAttribute(true)]
   [EditorAttribute(__typeof(AngleEditor), __typeof(System::Drawing::Design::UITypeEditor))]
   __property double get_Angle() { return int_angle; }
   __property void set_Angle(double value) { int_angle = value; }

   AngleEditorTestControl() {
      int_angle = 90;
      this->Size =  System::Drawing::Size(190, 42);
      this->BackColor = Color::Beige;
   }

protected:
   void OnPaint(System::Windows::Forms::PaintEventArgs* e) {
      if (this->DesignMode) {
         e->Graphics->DrawString(S"Use the Properties Window to access", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::Black), 3, 2);
         e->Graphics->DrawString(S"the AngleEditor UITypeEditor by", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::Black), 3, 14);
         e->Graphics->DrawString(S"configuring the \"Angle\" property.", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::Black), 3, 26);
      } else
         e->Graphics->DrawString(S"This example requires design mode.", new System::Drawing::Font(S"Arial", 8), new SolidBrush(Color::Black), 3, 2);
   }
};
}

[JScript] No example is available for JScript. To view a Visual Basic, C#, or C++ example, click the Language Filter button Language Filter in the upper-left corner of the page.

Requirements

Namespace: System.Drawing.Design

Platforms: Windows 98, Windows NT 4.0, Windows Millennium Edition, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 family

Assembly: System.Drawing (in System.Drawing.dll)

See Also

UITypeEditor Members | System.Drawing.Design Namespace | ITypeDescriptorContext | IServiceProvider