search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

.NET PropertyGrid Control - ListBox, ComboBox, and Custom Classes


By Robbe Morris
Printer Friendly Version
View My Articles
143 Views
    

Utilizing a ListBox, TextBox, ComboBox, TreeView, or custom class as a property in a .NET Windows Forms PropertyGrid control can seem like a daunting task. Even resizing the PropertyGrid column widths seems impossible. Today's article will show you how.


  Download Source Code

I like to use the scheme authored by Tony Allowatt called Bending the .NET PropertyGrid to Your Will.  It makes it much easier to create UITypeEditors of your own and incorporate them into the PropertyGrid control.

The code sample below goes into detail regarding how to easily create reusable UITypeEditors for common tasks such as ListBox, TextBox, ComboBox, or other standard control display as well as literally showing custom forms as your UITypeEditor.

Probably the most confusing aspect of working with UITypeEditors is how to pass complex data or sets of data in and out of them.  In my experience, this is most easily accomplished by creating a generic custom container with collections of generic custom items.  I use these custom items a lot for reusable methods to populate various controls in both a web and windows environment.

In the EggHeadCafe.Shared assembly of the sample, you'll find the classes CustomItemList and CustomItem.  This forms the generic data container as a single object that can be set as the PropertyGrid item's "value". You send in the CustomItemList or CustomItem to the UITypeEditor, modify the class or classes from within the UITypeEditor, and return the whole object back to the PropertyGrid control.

The big picture to grasp here is that if you create a container with everything you need, you can pass anything you want in and out of any UITypeEditor you create.  Most of the time, you can make your UITypeEditors reusable.  However, there will be times when you'll want to make some that are aware of your business classes or other user interface control population methods.

When the PropertyGrid control attempts to render the selected "value", it will call the .ToString() of whichever object it has.  So, you can use any object type and override it's .ToString() method to render whatever you want.  You can see this in my code sample in the ListBox editor as well as the MyClassEditor via the EggHeadCafe.TypeEditors assembly.  The MyClassEditor also shows how to force the PropertyGrid to raise a SetValue event on the same referenced object.  It will not raise the event on its own if you change a property value on the custom class.

You'll want to review the SetPropertyGridFromRecord and SetRecordFromPropertyGrid methods to learn how to get the complex objects from the PropertyGrid control and use their values.

On a side note, I've left some code in the PropertyGridController to resize the PropertyGrid control's columns.  It does have some paint and usability issues but it is there if you want to tweak it.

Here are a few screen shots followed by some of the source code







using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using EggHeadCafe.WindowsForms;
using EggHeadCafe.Shared;
using EggHeadCafe.TypeEditors;

namespace Demo
{
    public partial class Form3 : Form
    {
        private PropertyTable _recordPropertyBag = new PropertyTable();
        private const string _propertyDescription = "Description";
        private const string _propertyTextBox = "TextBox";
        private const string _propertyHtml = "Html Editor";
        private const string _propertyMyClass = "My Class";
        private const string _propertyFileName = "Filename";
        private const string _propertyListBox = "ListBox Editor";
       
        public Form3()
        {
            InitializeComponent();
        }

        private void Form3_Load(object sender, EventArgs e)
        {
           try
           {
               LoadPropertyGridForRecord();
               SetPropertyGridFromRecord();
           //    PropertyGridController.SetLabelWidth(this.propertyGridRecord,.25); 
           }
           catch (Exception ex) { MessageBox.Show(ex.Message); }
        }
 
        #region Load Property Grid For Record
        private void LoadPropertyGridForRecord()
        {
 
            try
            {
 
                this._recordPropertyBag = new PropertyTable();
 
                this._recordPropertyBag.SetValue += new PropertySpecEventHandler(RecordBag_SetValue);

                this._recordPropertyBag.Properties.Add(new PropertySpec(
                                                                          _propertyDescription,
                                                                          typeof(string),
                                                                         "Properties",
                                                                         "Description"));

                this._recordPropertyBag.Properties.Add(new PropertySpec(_propertyFileName,
                                                                          typeof(UIFileNameEditor),
                                                                          "Properties",
                                                                          "Location of the file.",
                                                                          "",
                                                                          typeof(UIFileNameEditor),
                                                                          typeof(System.Drawing.Design.UITypeEditor)));

                this._recordPropertyBag.Properties.Add(new PropertySpec(_propertyTextBox,
                                                                          typeof(UITextBoxEditor),
                                                                          "Properties",
                                                                     "Some large text value goes in this box",
                                                                          "",
                                                                          typeof(UITextBoxEditor),
                                                                          typeof(System.Drawing.Design.UITypeEditor)));

                this._recordPropertyBag.Properties.Add(new PropertySpec(_propertyHtml,
                                                                          typeof(UIHtmlEditor),
                                                                          "Properties",
                                                                           _propertyHtml,
                                                                          "some sample html",
                                                                           typeof(UIHtmlEditor),
                                                                           typeof(System.Drawing.Design.UITypeEditor)));

                this._recordPropertyBag.Properties.Add(new PropertySpec(_propertyMyClass,
                                                                          typeof(UIMyClassEditor),
                                                                          "Properties",
                                                                          _propertyMyClass,
                                                                          "",
                                                                          typeof(UIMyClassEditor),
                                                                          typeof(System.Drawing.Design.UITypeEditor)));
 
                this._recordPropertyBag.Properties.Add(new PropertySpec(_propertyListBox,
                                                                          typeof(UIListBoxEditor),
                                                                          "Properties",
                                                                          _propertyListBox,
                                                                           "",
                                                                           typeof(UIListBoxEditor),
                                                                           typeof(System.Drawing.Design.UITypeEditor)));

            
                this.propertyGridRecord.SelectedObject = this._recordPropertyBag;
                this.propertyGridRecord.Refresh();

            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Record Bag Set Value
        private void RecordBag_SetValue(object sender,
                                          PropertySpecEventArgs e)
        {
            try
            {
                SetRecordFromPropertyGrid(e.Property.Name);
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }

        }
        #endregion

        #region Set Record From Property Grid
        private void SetRecordFromPropertyGrid(string propertyName)
        {
    
            CustomItemList list = null;
            CustomItem myClass = null;
       
            try
            {
 
                // this sample method simulates populating your data class or data set
                // with data returned from the property grid.

                switch (propertyName)
                {
                    case _propertyDescription:
                            this.txtOutput.Text += (string)this._recordPropertyBag[propertyName] + "\r\n";
                            break;
                    case _propertyFileName: 
                        this.txtOutput.Text += (string)this._recordPropertyBag[propertyName] + "\r\n";
                            break;
                    case _propertyTextBox:
                            this.txtOutput.Text += (string)this._recordPropertyBag[propertyName] + "\r\n";
                            break;
                    case _propertyHtml:
                        this.txtOutput.Text += (string)this._recordPropertyBag[propertyName] + "\r\n";
                            break;
                    case _propertyMyClass:
                            myClass = (CustomItem)this._recordPropertyBag[propertyName];
                            this.txtOutput.Text += myClass.Description + "\r\n";
                            break;
                    case _propertyListBox:
                            
                            list = (CustomItemList)this._recordPropertyBag[propertyName];
                            
                            if (list.SelectedItems != null)
                            {
                              for(int i=0;i<list.SelectedItems.Count;i++)
                             {
                                this.txtOutput.Text += list.SelectedItems[i].Description + "\r\n";
                              }
                            }
                            
                           break;
                    default:
                        break;
                }

            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }
        #endregion

        #region Set Property Grid From Record
        private void SetPropertyGridFromRecord()
        {


            try
            {
 
                 this._recordPropertyBag[_propertyDescription] = "text description of record";
                 this._recordPropertyBag[_propertyFileName] = @"c:\temp";
                 this._recordPropertyBag[_propertyTextBox] = "Some long text goes here";
                 this._recordPropertyBag[_propertyMyClass] = new CustomItem(Guid.Empty, "", false); 

                CustomItemList list = new CustomItemList(true);
                list.Items = new List<CustomItem>();
                bool selected = false;

                for (int i = 0; i < 3; i++)
                {
                    if (i == 2) { selected = true; }
                    list.Items.Add(new CustomItem(Guid.NewGuid(), "Test " + i.ToString(), selected));
                    selected = false;
                }

                this._recordPropertyBag[_propertyListBox] = list;
               
                this.propertyGridRecord.SelectedObject = this._recordPropertyBag;
                this.propertyGridRecord.Refresh();
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }

        }
        #endregion

        private void propertyGridRecord_Resize(object sender, EventArgs e)
        {
      //      PropertyGridController.SetLabelWidth(this.propertyGridRecord, .25); 
        }


    }
}



using System;
using System.Collections;
using System.Collections.Generic; 
using System.ComponentModel;
using System.Diagnostics; 
using System.Drawing.Design;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using EggHeadCafe.Shared;

namespace EggHeadCafe.TypeEditors
{
    public class UIListBoxEditor : UITypeEditor
    {
         IWindowsFormsEditorService _wfes = null;
         private ListBox _control  = null;
         private CustomItemList _list = null;
   
        public override UITypeEditorEditStyle GetEditStyle(
            ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown; 
        }
 
        public override object EditValue(ITypeDescriptorContext context,
                                         IServiceProvider provider,
                                         object value)
        {
   
                _wfes = provider.GetService(typeof(IWindowsFormsEditorService)) 
                             as IWindowsFormsEditorService;                                   
 
            if (_wfes == null) { return value; }
            if (value == null) { return value; }
 
            _control = new ListBox();
            _control.Name = "listbox1";
            _control.Height = 325;
            _control.Width = 300;
            _control.Dock = DockStyle.Fill; 
            _control.SelectedIndexChanged += new EventHandler(ListItemSelected);
      
            _list = (CustomItemList)value;
            _control.Height = _list.Height;
            _control.Width = _list.Width;

            if (_list.EnableMultiSelect)
            {
                _control.SelectionMode = SelectionMode.MultiExtended;
             }
                
             for (int i = 0; i < _list.Items.Count; i++)
             {
                    _control.Items.Add(_list.Items[i]);
                    _control.SetSelected(i, _list.Items[i].Selected);
              }
  
            _wfes.DropDownControl(_control);

            RefreshList();
      
            value = _list;
             
            return value;
        }
       
        private void RefreshList()
        {
            CustomItem item = null;

            _list = new CustomItemList(_list.EnableMultiSelect,_list.Height,_list.Width);
 
            for (int i = 0; i < _control.Items.Count; i++)
            {
                item = (CustomItem)_control.Items[i];
                item.Selected = _control.GetSelected(i);
                _list.Items.Add(item);
            }
        }
    
        private void ListItemSelected(object sender,EventArgs e)
        {
   
           switch (_control.SelectionMode)
           {
               case SelectionMode.MultiExtended:
               case SelectionMode.MultiSimple:
                       break;
               default:
                     _wfes.CloseDropDown();
                   break;
           }

        }
        
    }
}

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing.Design;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using EggHeadCafe.Shared;

namespace EggHeadCafe.TypeEditors
{
    public class UIMyClassEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(
            ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }


        #region Edit Value
        public override object EditValue(ITypeDescriptorContext context,
                                         IServiceProvider provider,
                                         object value)
        {
            IWindowsFormsEditorService wfes = provider.GetService(
                typeof(IWindowsFormsEditorService)) as
                IWindowsFormsEditorService;

            if (wfes == null) { return value; }

            UIMyClassEditorForm form = new UIMyClassEditorForm();

            if (value != null)
            {
              form.EditorValue =  (CustomItem)value;
            }
 
            form._wfes = wfes;
        
            wfes.DropDownControl(form);

            // Force the property grid to accept your referenced "equal"
            // copy of the object and raise the SetValue event.
            context.PropertyDescriptor.SetValue(this,form.EditorValue);  
            value = form.EditorValue;
           
            return value;
        }
        #endregion
        
    }
}
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Reflection;
using System.Text;

namespace EggHeadCafe.WindowsForms
{
    public class PropertyGridController
    {
        // This is a copied hack to set the property grid label width I found somewhere
        // online.  However, it does come with some usability issues with where
        // the user has to click to launch the editor and some paint issues.
        // I'll placed it in here for you to play with.

        #region Set Label Width
        public static void SetLabelWidth(PropertyGrid propertyGrid, int width)
        {
            Control grid = propertyGrid;
            Control propertyGridView = grid.Controls[2];

            Type propertyGridViewType = propertyGridView.GetType();

            FieldInfo fldLabelWidth = propertyGridViewType.GetField("labelWidth",
                                                 BindingFlags.Instance | BindingFlags.NonPublic);
            fldLabelWidth.SetValue(propertyGridView, width);
        }
        #endregion

        #region Set Label Width
        public static void SetLabelWidth(PropertyGrid propertyGrid, double percentage)
        {

            try
            {

                Control propertyGridView = propertyGrid.Controls[2];

                Type propertyGridViewType = propertyGridView.GetType();

                FieldInfo fldLabelWidth = propertyGridViewType.GetField("labelWidth",
                                                     BindingFlags.Instance
                                                     | BindingFlags.NonPublic);

                if (propertyGridView.Width < 1) { return; }

                fldLabelWidth.SetValue(propertyGridView, (int)((propertyGridView.Width * percentage)));

                propertyGrid.Invalidate();

            }
            catch (Exception) { throw; }

        }
        #endregion
    }
}


The rest of the code is in the download sample.


Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of EggHeadCafe.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.

button
Article Discussion: .NET PropertyGrid Control - ListBox, ComboBox, and Custom Classes
Robbe Morris posted at Wednesday, August 08, 2007 11:20 AM
Original Article