Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Creating a Custom Collection for Use in a PropertyGrid

4.48/5 (13 votes)
15 Apr 2008GPL34 min read 2   3.1K  
Creating a custom collection for use in a PropertyGrid.

CustomCollectionApp.gif

CollectionEditor1.gif

Introduction

I've been working a lot lately on a database application at my current client. The application needed to be very dynamic since pieces of the database were always changing. There are also many different users that access the application (DEV, QA, Business Teams). Each team accessed different databases and different schemas. I wanted the user to be able to modify query settings on their own. I didn't want to have to account for every single database query. Using the property grid and user settings via the app.config file I was able to accomplish this. The only problem was a lot of the information I wanted to store in the app.config file didn't fit the standard types. I needed to figure out how to store settings from a custom collection and persist the settings. This code demonstrates how to create a custom collection that can be edited and saved to the app.config file via the property grid control.

Using the Code

In order to make this work there are a few things that the application must have. I will describe the following items:

  • Creating a .settings file
  • Creating the individual item we want to store in a collection
  • Creating the collection
  • Creating the property grid that holds our new collection

The first thing we need to do is create a settings file. You will want create a new Windows App project and add a new Settings file to the project (Settings.settings). The settings file should be moved under the Properties folder. Now that we've created the file we will push it aside for later.

Within the new solution you created we need to add a new project. This new project will be a class library (Organization). Once the class library is created we will need to add a new class that holds the individual item (Employee) that we will later make a collection of.

I've created a class called Employee that looks like the following:

C#
using System;
using System.Text;
using System.ComponentModel;
namespace Organization
{
    public class Employee
    {
        #region Private Variables
        private string firstName;
        private string lastName;
        private DateTime dateOfHire;
        #endregion

        #region Public Properties
        [Category("Employee")]
        [DisplayName("First Name")]
        [Description("The first name of the employee.")]
        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        [Category("Employee")]
        [DisplayName("Last Name")]
        [Description("The last name of the employee.")]
        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }

        [Category("Employee")]
        [DisplayName("Date of Hire")]
        [Description("The hire date of the employee.")]
        public DateTime DateOfHire
        {
            get { return dateOfHire; }
            set { dateOfHire = value; }
        }
        #endregion
    }
}

Basically, the block of code creates the information we want to store about each employee and then creates accessible properties to retrieve and save this information. I've added attributes to the code to control how these items will look in the property grid. I will discuss this later in the article.

Now that I've created the item my collection will be made of I can now create the collection (EmployeeCollection). We can do this by adding another class to our class library project (Organization).

I've created a class called EmployeeCollection that looks like the following:

C#
using System;
using System.Collections;
using System.Text;
namespace Organization
{
    public class EmployeeCollection : CollectionBase
    {
        public Employee this[int index]
        {
            get { return (Employee)List[index]; }
        }
        public void Add(Employee emp)
        {
            List.Add(emp);
        }
        public void Remove(Employee emp)
        {
            List.Remove(emp);
        }
    }
}

The next block of code allows us to customize the names of the members within the collection. The image below shows what the members look like if we do not create a custom collection editor. It is difficult to find the item you are looking for without clicking each item.

Image 3

I've created a class called EmployeeCollectionEditor that looks like the following:

C#
using System;
using System.ComponentModel.Design;
using System.Design;
using System.Drawing;
using System.Text;

namespace Organization
{
    public class EmployeeCollectionEditor : CollectionEditor
    {
        public EmployeeCollectionEditor(Type type)
            : base(type)
        {
        }

        protected override string GetDisplayText(object value)
        {
            Employee item = new Employee();
            item = (Employee)value;

            return base.GetDisplayText(string.Format("{0}, {1}",item.LastName,
                item.FirstName));
        }
    }
}

It is time that we re-visit the Settings file we created at the beginning of the article. The first thing we need to do is add a reference to the Class Library project we created (Organization). Once the reference has been added we can open up the .settings file. In row number one enter a name for the setting (Employees). Next we have to select a type. Near the bottom of the drop down list we can browse for more items. We need to browse for the the collection item we created (Organization.EmployeeCollection). Once we find the type it can be added. If done correctly, the 3rd column should show up with a ... button. If we click the button we can now add default values to our collection.

Now we are ready to work on the property grid. Before we add the property grid to the form, we need to setup the source object we will use. Add a new class (PropertyGridItems) to your project. The code that we will create is the source for all items in the property grid as well as the attributes that help control the display of the items.

I've created a class called PropertyGridItems that looks like the following:

C#
using System;
using System.ComponentModel;
using System.Text;
namespace CustomCollectionApp
{
    internal class PropertyGridItems
    {
        [Editor(typeof(Organization.EmployeeCollectionEditor),
            typeof(System.Drawing.Design.UITypeEditor))]
        [Category("Organization")]
        [DisplayName("Employees")]
        [Description("A collection of the employees within the organization")]
        public Organization.EmployeeCollection Employees
        {
            get { return Properties.Settings.Default.Employees; }
            set { Properties.Settings.Default.Employees = value; }
        }
    }
}

I've only added one item, but you can add as many as you like. I've added attributes that help describe how this will display in the property grid. What each attribute is is pretty self explanatory. As you can see in the get/set accessors we are referring to the settings file we just created. This allows us to retrieve and save items to our app.config file. I have also added a reference to the custom editor to give us a pretty display of items.

Go ahead and add the property grid to a form in the Windows App Project. We need to setup an event on the form load to populate the property grid.

I've added a Load event that looks like the following:

C#
#region Load Event
private void frmMain_Load(object sender, EventArgs e)
{
     myPropertyGrid.SelectedObject = new PropertyGridItems();
}
#endregion

Now that you've created this block of code you should be able to run the app and interact with our new custom collection.

Points of Interest

Within the source code I've also included code to save/reset your settings. I've also shown you how to loop through our new collection.

I won't go into detail, but the source for my main form is listed below:

C#
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace CustomCollectionApp
{
    public partial class frmMain : Form
    {
        #region Constructor
        public frmMain()
        {
            InitializeComponent();
        }
        #endregion 
        #region Load Event
        private void frmMain_Load(object sender, EventArgs e)
        {
            myPropertyGrid.SelectedObject = new PropertyGridItems();
        }
        #endregion
        #region ToolStrip Button Clicks
        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            try
            {
                Properties.Settings.Default.Save();
                MessageBox.Show("Your settings were saved successfully.",
                   "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }
        private void defaultToolStripButton_Click(object sender, EventArgs e)
        {
            try
            {
                if (MessageBox.Show(
                    "Are you sure you want to load the default settings?" +
                    "All customized settings will be lost.",
                    "Load Defaults", MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    Properties.Settings.Default.Reset();
                    MessageBox.Show(
                        "Your default settings have been loaded successfully.",
                        "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }
        #endregion
        #region Button Clicks
        private void btnShow_Click(object sender, EventArgs e)
        {
            try
            {
                lstNames.Items.Clear();
                foreach (
                    Organization.Employee emp in Properties.Settings.Default.Employees)
                {
                    lstNames.Items.Add(emp.LastName + ", " + emp.FirstName);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }
        #endregion
    }
}

History

3/13/08 - Initial Release

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)