Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / Windows Forms
Article

Context menu for the custom properties in the C# PropertyGrid

Rate me:
Please Sign up or sign in to vote.
4.44/5 (9 votes)
29 Jan 2006CPOL3 min read 147.8K   1.2K   52   34
Context menu for the custom properties in the C# PropertyGrid, allowing the user to reset the properties, the same way as in Visual Studio.

There is another window inside:

There is another window inside

Changing the name also, not only the value:

Changing the name also, not only the value

Introduction

I came to a point where I needed to allow the user to reset the values in the property grid, the same way Visual Studio does (using a context menu when right clicking on the property). This is useful for some reasons:

  • You don't have to remember the old value of the property. The controls does it all for you.
  • The control displays the modified properties using bold, so it's easy to see which properties have been modified.

Background

After searching a great deal on the internet, I still didn't find anything. Then it occurred to me to use the same old friend, the Spy++ tool that comes with Visual Studio. Then I noticed there is another window inside what we think is the property grid. That other window does all the impressive work that makes the property grid so famous.

Since there is no direct way to get our hands on that window, I had to go brute force on it:

C#
propertyGrid1.GetChildAtPoint( new Point(40,40) )

Now, I had the window. All I had to do was to get my hands on the messages the window receives and look for messages that indicate a right click inside.

This is done by adding a filter to all the messages in the application's loop and looking for the messages addressed to our window:

C#
Application.AddMessageFilter( new PropertyGridMessageFilter( 
        propertyGrid1.GetChildAtPoint( new Point(40,40) ), 
        new MouseEventHandler( propGridView_MouseUp )) );

Since doing this in the OnLoad event handler failed (Object reference not set to an instance of an object), I now do it in a timer event handler that lets the entire GUI to instantiate:

C#
private void timerLoad_Tick(object sender, System.EventArgs e)
{
    timerLoad.Stop();

    // this fails if called directly in OnLoad
    // since the control didn't finish creating itself: 
    Application.AddMessageFilter( 
        new PropertyGridMessageFilter( 
            propertyGrid1.GetChildAtPoint( new Point(40,40) ),
        new MouseEventHandler( propGridView_MouseUp )) );
}

The filter looks for WM_?BUTTONUP messages and makes a call to a provided delegate:

C#
public bool PreFilterMessage(ref Message m)
{
    if( ! this.Control.IsDisposed && m.HWnd == 
          this.Control.Handle && MouseUp != null)
    {
        MouseButtons mb = MouseButtons.None;
        
        switch( m.Msg )
        {
            case  0x0202 : /*WM_LBUTTONUP, see winuser.h*/
                mb = MouseButtons.Left;
                break;
            case  0x0205 : /*WM_RBUTTONUP*/
                mb = MouseButtons.Right;
                break;
        }

        if( mb != MouseButtons.None )
        {
            MouseEventArgs e = 
            new MouseEventArgs( mb, 1, m.LParam.ToInt32() & 0xFFff, 
                                m.LParam.ToInt32() >> 16, 0 );
        
            MouseUp( Control, e );
        }
    }
    return false;
}

When the form's method is called by the filter, I just get the propertyGrid1.SelectedGridItem.PropertyDescriptor and display the context menu containing the Reset and ChangeName options.

Here you can see the Reset option displaying the default value so the user has the new and the default value next to each other:

reset

What if the user wants to rename the property itself

First, the program displays an on-site textbox for the user to alter the property name in:

C#
/// <SUMMARY>
/// This is a special feature to allow renaming of the property itself
/// </SUMMARY>
private void menuItemChangeName_Click(object sender, System.EventArgs e)
{
    GenericPropertyDescriptor gpd = 
        propertyGrid1.SelectedGridItem.PropertyDescriptor 
        as GenericPropertyDescriptor;
    if( gpd != null )
    {
        TextBox t = new TextBox();

        t.Text = gpd.Property.PropertyName;
        // so we know what property is about 
        t.Tag = gpd;
        // to capture Enter & Escape 
        t.KeyUp += new KeyEventHandler(textbox_name_KeyUp);
        // to allow hidding of the textbox
        // by clicking somewhere else: 
        t.MouseUp += new MouseEventHandler(textbox_name_MouseUp); 
        // i want all the mouse messages
        // to be sent to the textbox 
        t.Capture = true;

        // placing the textbox in the form,
        // right under the mouse: 
        Point mp = MousePosition; mp.Offset( -3, -3 );
        t.Location = PointToClient( mp );
        Controls.Add( t );
        t.BringToFront();
        t.Focus();

        // clears the initial selection of the text 
        t.Select( t.Text.Length, 0 );
        
        // making the control obvious to the user: 
        t.BackColor = Color.Yellow;
        t.ForeColor = Color.Red;
    }
}

Once the textbox is on the screen, the content can be validated by pressing Enter, or discarded by pressing Escape:

C#
private void textbox_name_KeyUp(object sender, KeyEventArgs e)
{
    switch( e.KeyData )
    {
        case Keys.Enter:
        {// keep the changes: 
            TextBox t = sender as TextBox;
            ( (GenericPropertyDescriptor) 
               t.Tag ).Property.PropertyName = t.Text != "" ? 
               t.Text : "must type something or" + 
               " the control will go berserk" ;

            RemoveTextBox( sender );

            // refresh the grid since it can't
            // possibly know we changed something: 
            propertyGrid1.Refresh(); 
        }break;

        case Keys.Escape:
        {// just "go home" 
            RemoveTextBox( sender );
        }break;
    }
}

Using the code

The properties are stored in a collection that implements ICustomTypeDescriptor (this has been explained in many articles, I'm not explaining it again here). This collection returns the custom properties when asked by the property grid (propertyGrid1.SelectedObject = properties;). Adding properties to this collection is quite easy:

C#
properties = new GenericPropertyCollection_CustomTypeDescriptor();
properties.AddProperty( new GenericProperty( "Integer", 
                       (int) 1, "Custom", "Int32" ) );
properties.AddProperty( new GenericProperty( "Float disabled", 
                        4.5f, "Custom", "Single", 
                        new ReadOnlyAttributeEditor() ) );

Making one of the properties read only (disabled) is also easy, just tag-it with the ReadOnlyAttributeEditor.

The collection can be used as storage for the data, there is no need for a parallel structure. Accessing the data can be done like this:

C#
foreach( GenericProperty gp in properties )
    MessageBox.Show( gp.Value.ToString() );

Points of Interest

Many things can be done by accessing the insides of the operating system or common controls. Platform Invoke and filtering of messages can be used to create new, helpful, and original effects.

I find interesting the on-site editing of the property name, using the small textbox that disappears when the user is done with it. It's something like the Undo control in Visual Studio that is placed over all the controls (toolbars, edit window).

History

  • January 29th, 2006 - first version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Romania Romania
Areas Of Expertise:


APIs:

.Net Framework
MFC
Win32

ActiveX (documents, controls, automation)

Graphics:

DirectX
OpenGL

Databases:

SQL Server
ADO
Odbc

Web Applications:

AJAX
ASP.Net
Web Services
ActiveX controls

Programming Languages:

C/C++
C#
Pascal
Basic
Python
Javascript
XHTML

Proud to have been part of the following team(s):

Project Viewer - Housatonic

Silent Hunter 4 - Ubisoft Romania

Personal web site (programing C++ C# ASP.NET, CV, jokes, games)

RedGoblin - boardgames store (a project i'm working on)





Comments and Discussions

 
QuestionMessage filter Pin
Markfyoung89310-Mar-17 6:18
Markfyoung89310-Mar-17 6:18 
Question.net 4.5 Pin
Markfyoung8933-Mar-17 6:42
Markfyoung8933-Mar-17 6:42 
QuestionHow to change backcolor or textcolor of a single cell (GridItem / property) in the .NET PropertyGrid? [modified] Pin
RobTheRealX16-Aug-09 23:56
RobTheRealX16-Aug-09 23:56 
QuestionHi, do you know how to get the display text of a GridItem which is not selected? Pin
sinbao1-Dec-07 2:29
sinbao1-Dec-07 2:29 
GeneralTimer is not necessary Pin
2LM17-Oct-07 7:54
2LM17-Oct-07 7:54 
AnswerRe: Timer is not necessary Pin
Mihai Maerean17-Oct-07 8:21
Mihai Maerean17-Oct-07 8:21 
QuestionHow to do multi row select on property grid Pin
Alias8821-Dec-06 13:50
Alias8821-Dec-06 13:50 
AnswerRe: How to do multi row select on property grid Pin
Mihai Maerean21-Dec-06 18:36
Mihai Maerean21-Dec-06 18:36 
GeneralRe: How to do multi row select on property grid Pin
Alias8821-Dec-06 19:20
Alias8821-Dec-06 19:20 
Generalquestion about the artical Pin
roey frid1-Aug-06 21:14
roey frid1-Aug-06 21:14 
AnswerRe: question about the artical Pin
Mihai Maerean3-Aug-06 19:59
Mihai Maerean3-Aug-06 19:59 
Generalquestion about the article Pin
roey frid24-Jul-06 5:32
roey frid24-Jul-06 5:32 
QuestionRe: question about the article Pin
Mihai Maerean26-Jul-06 9:35
Mihai Maerean26-Jul-06 9:35 
AnswerRe: question about the article Pin
roey frid26-Jul-06 19:19
roey frid26-Jul-06 19:19 
AnswerRe: question about the article Pin
Mihai Maerean26-Jul-06 23:22
Mihai Maerean26-Jul-06 23:22 
GeneralRe: question about the article Pin
roey frid1-Aug-06 21:09
roey frid1-Aug-06 21:09 
Generalpropertygrid custom properties question Pin
roey frid18-Jul-06 22:22
roey frid18-Jul-06 22:22 
AnswerRe: propertygrid custom properties question Pin
Mihai Maerean18-Jul-06 23:07
Mihai Maerean18-Jul-06 23:07 
GeneralRe: propertygrid custom properties question Pin
roey frid18-Jul-06 23:31
roey frid18-Jul-06 23:31 
AnswerRe: propertygrid custom properties question Pin
Mihai Maerean19-Jul-06 0:06
Mihai Maerean19-Jul-06 0:06 
GeneralRe: propertygrid custom properties question Pin
roey frid19-Jul-06 0:24
roey frid19-Jul-06 0:24 
GeneralGetting the window handle and setting the context menu Pin
Corneliu Tusnea31-Jan-06 12:54
Corneliu Tusnea31-Jan-06 12:54 
GeneralRe: Getting the window handle and setting the context menu Pin
Mihai Maerean31-Jan-06 17:00
Mihai Maerean31-Jan-06 17:00 
GeneralRe: Getting the window handle and setting the context menu Pin
Ian P Johnson21-Jun-07 7:54
professionalIan P Johnson21-Jun-07 7:54 
GeneralRe: Getting the window handle and setting the context menu Pin
RobTheRealX17-Aug-09 0:15
RobTheRealX17-Aug-09 0:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.