October 2009 - Blake Niemyjski

  • How-to: Build a custom UITypeEditor

    Recently I built a CodeSmith sample UITypeEditor that allows a user to enter in custom data into a DropDownList. In the following article I’ll show you what you need to do in order to build your own UITypeEditor.

     

    First you need to create a public class that will hold the data of the drop down list. In this example I named my class DropDownListProperty.

    public class DropDownListProperty
    {
    }

    Next we will need to add the properties and the constructors.

        public class DropDownListProperty
        {
            private List<string> _values = new List<string>();

            public DropDownListProperty()
            {
                SelectedItem = "None";
            }

            public DropDownListProperty(List<String> values)
            {
                if(values.Count > 0)
                    SelectedItem = values[0];
                else
                    SelectedItem = "None";

                Values = values;
            }

            public List<string> Values
            {
                get
                {
                    if (_values == null)
                        _values = new List<String>();

                    return _values;
                }
                set
                {
                    if(value != null)
                        _values = value;
                }
            }

            [Browsable(false)]
            public string SelectedItem { get; set; }
        }

    You'll notice that we have a public property called SelectedItem. This property will hold the initial value which will be the selected value when a user selects a choice. By default we set this to "None" in the constructor. We also set an attribute on the property Browsable(false). This tells the PropertyGrid not to display this property.

    We now want to override the ToString() method to the DropDownListProperty so the dropdown displays the current selected value.

    /// <summary>
    /// The value that we return here will be shown in the property grid.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return SelectedItem;
    }

    Now it is time to implement the class that controls how my class is displayed in the property grid. We will want to create a class that inherits from UITypeEditor.

    /// <summary>
    /// Provides a user interface for selecting a state property.
    /// </summary>
    public class DropDownListPropertyEditor : UITypeEditor
    {
    }

    Next we will add a private member variable named _service. We will need to declare this member variable because we will want to tie into an event in a little bit. Now it is time to override the EditValue method.

    /// <summary>
    /// Displays a list of available values for the specified component than sets the value.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
    /// <param name="provider">A service provider object through which editing services may be obtained.</param>
    /// <param name="value">An instance of the value being edited.</param>
    /// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        if (provider != null)
        {
        // This service is in charge of popping our ListBox.
        _service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));

            if (_service != null && value is DropDownListProperty)
            {
                var property = (DropDownListProperty) value;

                var list = new ListBox();
                list.Click += ListBox_Click;
       
                foreach (string item in property.Values)
                {
                    list.Items.Add(item);
                }

                // Drop the list control.
                _service.DropDownControl(list);

                if (list.SelectedItem != null && list.SelectedIndices.Count == 1)
                {
                    property.SelectedItem = list.SelectedItem.ToString();
                    value =  property;
                }
            }
        }

        return value;
    }

    It is important not to be overwhelmed by the code above. The object value that is passed in is the DropDownListProperty class that holds our data. All we need to do is some safe type checking (value is DropDownListProperty) and then cast the value. The _service variable holds the property grid control that we are interacting with.

    We create a ListBox object as that will hold our list of data (Values property from the DropDownListProperty class). It also exposes a Click event that will allow us to know when someone clicks on the drop down list. We will add an event handler ListBox_Click  to the Click event so we can close the drop down list. If we skipped this step then the list would always be shown.

    The next few lines just adds all our data into the Listox and calls DropDownControl(Control).  This shows the populated ListBox control.

    Finally we will set the SelectedItem to the Item that the user selected.

    It is time to add the method that we wired up to the Click event.

    private void ListBox_Click(object sender, EventArgs e)
    {
       if(_service != null)
          _service.CloseDropDown();
    }

    The last peice to this puzzle is to override the GetEditStyle method and return that we want to display a DropDown UITypeEditorEditStyle

    /// <summary>
    /// Gets the editing style of the <see cref="EditValue"/> method.
    /// </summary>
    /// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
    /// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        // We're using a drop down style UITypeEditor.
        return UITypeEditorEditStyle.DropDown;
    }

    Finally we will go back and add a Editor attribute to the DropDownListProperty class. This will tell the PropertyGrid that when this property type is loaded to use the new UITypeEditor class we created.

    [Editor(typeof(DropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]

    For more information please check out our Google Code repository. Attached is the source code for these two classes.

  • It's the littlest of things that we do, that make CodeSmith shine.

    During each release of CodeSmith, one thing we always do is test upgrade scenarios between each versions and check for backwards compatibility among other tests. Recently, I was amazed at how much change has taken place between CodeSmith 4.x and the latest CodeSmith 5.1.x release when it comes to the user interface. CodeSmith has made a huge effort to make CodeSmith rock solid, crazy fast,  and easy to use. I’d like to give everyone a quick inside look as to how I think CodeSmith has accomplished this.

    Our goal from the beginning was to make CodeSmith a top notch dependable tool. I think we have accomplished this through various forms. In the previous version of CodeSmith a user would have to report every bug they found by using the built in tool, support or the community. We have found that most of the time you’re in a hurry and stop using the tool or don’t report the issue. We have spent the time to make bug reporting much easier and provide us with as much possible as you want us to. So we can fix the bug with as little user intervention as possible. You can also choose in the options to send us feedback automatically, or you can choose the default option to send feedback at your digression.

    When a crash occurred in CodeSmith 4.x you had the option to send in a crash report via the following dialog. We have found that the previous approach doesn’t allow us to reproduce the issue as easily and most of the time doesn’t provide enough information.

    Since the introduction of CodeSmith 5.1.x we have added new functionality to CodeSmith that allows the user to selectively choose which pieces of information are sent back to us.

    If you click on the click here link you will be presented with the following dialog that lets you choose which pieces of information you want to send back.

    This additional information allows our project managers and team see what issues end users like yourself are experiencing so we can get a bug fix out quickly. If you provide your email address, we will contact you and let you know that we are looking into the issue. We have gathered a lot of feedback through the use of error reporting that has changed how we as a company work on bugs in the product.

    User feedback is very important to us. We review all user feedback that comes into CodeSmith via support, surveys, and the community forums. With the latest build of CodeSmith, It has become even easier to contact CodeSmith and let us know what you think. From the Help menu select feedback and you are prompted with the following dialog that allows you to ask a question, submit an idea or bug report. We have also included the functionality to search previous submitted ideas and vote on them.

    You might be thinking to yourself, CodeSmith might be stable, but is it faster with the new release. The answer to that question is YES. We have spent a great deal of time focusing on Template Caching to ensure you never need to recompile a template unless a change has taken place. We have also introduced a new feature we call deep load. Deep loading allows us to grab all your schema information in advance so we don’t need to make multiple round trips back to your database. We have also spent time working with performance and memory profilers tweaking CodeSmith for optimal performance. If you come across a scenario where you think CodeSmith is slow, please let us know by submitting a bug report.

    We have made improvements to the user interface across the board to allow you to know exactly how CodeSmith is working. Take for example, previously when CodeSmith started up you were prompted with a dialog like the following when samples were extracted:

    This dialog would freeze CodeSmith, so naturally you would think that CodeSmith had stopped working. We have since changed this to be multithreaded and also display you a progress bar.

    Another major change we have made is to simplify configuration.

    Can’t you tell this dialog was made by a developer ;). Let us know what you think of the new configuration dialog!

    We love being organized and providing you with all of our samples applications and templates. Below is a comparison between what you would have seen in CodeSmith 4.x and CodeSmith 5.x. Besides adding and updating a lot of the templates. We have now also made it easier to find the template you are looking for.

    In CodeSmith 5.1 we have also included the sample applications to most of the Framework Templates. You can find this in the  Documents\CodeSmith\Samples\v5.1\Projects\Framework-Samples folder on your computer.

    I hope that if you were a previous user of CodeSmith and you try out the latest version of 5.x you notice these major changes as well as the minor changes like threading enhancements so you can navigate around CodeSmith features much quicker.

     

windowscoding.com;
Copyright © 2008 Windows Coding
Microsoft and Microsoft logo's are trademarks of Microsoft Corporation.