Flexible Combo Box and Editing Control free download

Flexible Combo Box and Editing Control free download

Flexible Combo Box allows using any Control In ComboBox or in EditingControl for DataGridView, in Windows Forms Controls (C#, VB)

Why Do I Create a Flexible Combo Box and Editing Control?

  1. I need to extend the DataGridViewColumn without rewriting it each time.
  2. I need the flexibility combobox to accept any control as a dropdown.

What Does The Flexible Combo Box and Editing Control Do and Why is it Useful?

  1. Build your own grid column or your own combo simply.
  2. Use any control in the data grid column dynamically during the run and design time.
  3. It comes with a control converter and a control editor to change the control type that you create at design time.
  4. Both control converter and the control editor use as TypeConverter and Editor attributes in developing new controls.

What problem does Flexible Combo Box Solve?

  1. Our component has a control property that creates controls of different types of design time and stores the created control as a simple text value.
  2. Use the Value property name of the drop-down control or editing control to specify the witch property to used to get the control value, for example:
    Control Value Property Name
    NumericUpDown Text
    CheckedListBox CheckedItems
    TimeEdit Value
  3. When using a list or a collectionfor the Value property name; the library will convert the value to string that contains all items in the collection.

Using the Flexible Combo Box and Editing Control

  1. Create a new form in the Windows application.
  2. Add reference to AnyControlComboColumn.
  3. Add a new DataGridView and add a new column and set its type to GridColumn.
  4. In the property window, go to EditingControl(Create New) a property and choose the control type from the dropdown list.
  5. From the toolbox, add to the form DropDownCombo.
  6. In the property window, go to DropDownButton property and expand it.
  7. Under DropDownButton, go to DropDownControl(Create New) property and choose the control type from the dropdown list.

Demo Application for Flexible Combo Box and Editing Control

The demo application contains DropDownCombo; from the dropdown control choose: CheckedListBox or DirList or FileList, and it contains a DataGridViewwith two columns – the first with NumericUpDown as an editing control and the second column with Mask Text Box as an edit control.

About Flexible Combo Box and Editing Control

The library contains the following classes:

  1. DropDownButton: This class provides a dropdown button that accepts any control as a dropdown control and any other control to be the display area.
  2. DropDownCombo is a text box that contains the above dropdown button and so it provides a text box with dropdown control; you can set or change then control at run time or at design time.
  3. ControlConverter and ControlEditor provide a technique to serialize any control to simple text,  and we store text in the form designer.
  4. EditingControl, GridCell and GridColumn provide the ability to use any control as an editing control in DataGridView.

About C# Code in This Article

I develop this library using VB. I get the C# code using code converters. For more information about code converters, please see:

About the DropDown Button

  1. The dropdown button is the major class for creating dropdown combos.
  2. If we add it to any control, I will dock it to the right side.
  3. It assigns two controls: the first is the dropdown control, and the second is the display area.
  4. The display area in the control will show the value of the created combo and could be a text box or rich text box or any control
  5. The dropdown control is the control that will be dropped when the dropdown button is clicked.
  6. To set up the dropdown control, you can choose control in the form or choose from a list that will contain all control types in system.windows.forms Assembly and assemblies that the application includes as a reference to them.
  7. When clicking the dropdown button, it will change the dropdown value to meet the value of the display area
  8. When the dropdown pop-up is closed, it will change the display area value to meet the value of the dropdown control
  9. DropDownCombo is a Text box that contains a dropdown button.

About the GridColumn

  1. GridColumn is the class, which is used to extend the DataGridView to accept any control as editing control.
  2. To use this class, I need to use DataGridView, and add GridColum to it from DataGridView the Designer window.
  3. To set up the editing control, you can choose control in the form or choose from a list that will contain all control types of system.windows.forms Assembly and assemblies that the application includes a reference to them.

About Control Converter and Control Properties Serialization

Before building the control property for the dropdown control, you need to serialize it to string to be stored in the form designer, but don’t use XmlSerializer to serialize control properties because:

  1. XmlSerializer not supported types:
    • ISite
    • IDictionary
    • Any class that Implements unsupported interface
    • Any class has the property that returns an unsupported interface
    • Control
    • PropertyDescriptorCollection
    • SortedList
    • Collection
    • TreeNodeCollection
    • TreeNode
    • Any class that Inherits not supported class
    • Any class has the property that returns unsupported class
  2. Also, XmlSerializer can serialize the following types, but it can’t de-serialize them:
    • ArrayList
    • List of Objects
    • ListBox.ObjectCollection
    • CheckedListBox.ObjectCollection
  3. XmlSerializer returns a multi-line value, and it is better to store the control property as simple text
  4. To test XmlSerializer, you can use the following code:
'VB
'this code is a part of the Flexible Combo Box and Editing Control 
Imports System.Xml.Serialization
Imports System.Reflection
Imports System.ComponentModel
Imports AnyControlComboColumn
Imports System.Windows.Forms

Public Module Module1
    Public Function Serialize(ByVal Value As Object) As String
        Try
            If Value Is Nothing Then
                Return ""
                Exit Function
            End If
            Dim Serializer As New XmlSerializer(Value.GetType)
            Dim vMemoryStream As New IO.MemoryStream
            Serializer.Serialize(vMemoryStream, Value)
            Dim s = System.Text.Encoding.UTF8.GetString(vMemoryStream.ToArray)
            s = s.Replace("<?xml version=""1.0""?>", _
		"<?xml version=""1.0"" encoding=""utf-8""?>")
            vMemoryStream = New IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(s))

            s = System.Text.Encoding.UTF8.GetString(vMemoryStream.ToArray)

            'Try to Deserialize
            Try
                Dim f = Serializer.Deserialize(vMemoryStream)
            Catch ex As Exception
                Return ""
            End Try
            Return s
        Catch ex As Exception
            Return ""
        End Try
    End Function
End Module
//C#
//this code is a part of the Flexible Combo Box and Editing Control
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Reflection;
using System.ComponentModel;
using AnyControlComboColumn;
using System.Windows.Forms;

namespace AnyControlComboColumn {
    public static class MSerialize {
        public static string Serialize(object Value) {
            try {
                if (Value == null) {
                    return "";
                    return null;
                }
                XmlSerializer Serializer = new XmlSerializer(Value.GetType());
                System.IO.MemoryStream vMemoryStream = new System.IO.MemoryStream();
                Serializer.Serialize(vMemoryStream, Value);
                var s = System.Text.Encoding.UTF8.GetString(vMemoryStream.ToArray());
                s = s.Replace("<?xml version=\"1.0\"?>", "<?xml version=\"1.0\" encoding=\"utf-8\"?>");

                vMemoryStream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(s));

                s = System.Text.Encoding.UTF8.GetString(vMemoryStream.ToArray());

                //>Try to Deserialize
                try {
                    var f = Serializer.Deserialize(vMemoryStream);
                } catch (Exception ex) {

                    return "";
                }
                return s;
            } catch (Exception ex) {
                return "";
            }
        }
    }
}

We don’t use the default ComponentConverter because:

  1. It only allows selecting from pre-created controls in the form.
  2. It does not allow you to choose a control that is not present in the form.
  3. We can use it at design time only but not at runtime.

What ComponentConverter does:

  1. Creates an instance of the control from the selected type.
  2. Convert the control to string stored in ControlText.
  3. Converts the control text to the control object.

Points of Interest about the Flexible Combo Box and Editing Control

To assign a dropdown control to the dropdown button, we use the following properties:

'VB
'this code is a part of the Flexible Combo Box and Editing Control
'Don't add TypeConverter attribute so this property use the default ComponentConverter
<DisplayName("DropDownControl(Created in the form)")> _
Public Property DropDownControl() As Control

<TypeConverter(GetType(ControlConverter)), _
Editor(GetType(ControlEditor), GetType(UITypeEditor)), _
DisplayName("DropDownControl(Create New)")> _
Public Property NewDropDownControl() As Control

<Description("a text that will be used to create new DropDownControl")> _
Public Overridable Property ControlText As String
//C#
//this code is a part of the Flexible Combo Box and Editing Control
//dont put DesignerSerializationVisibility(DesignerSerializationVisibility.Content
//this is only good for readonly property
//ControlConverter does not work automatically if property type inherits form componenet or control
//so we need a ControlText property
[TypeConverter(typeof(ControlConverter)), Editor(typeof(ControlEditor), typeof(UITypeEditor)), 
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 
Description("Create new control displayed when OpenDropDown."), 
DefaultValue(typeof(Control), "Nothing"), Category("DropDown"), 
DisplayName("DropDownControl(Create New)"), EditorBrowsable(EditorBrowsableState.Advanced)]
public Control NewDropDownControl { 
//...........
}

[DefaultValue(""), Browsable(false), 
        Description("a text that will be used to create new DropDownControl")]
public virtual string ControlText {
//.................
}

//Don't add TypeConverter attribute so this property use the default ComponentConverter
[Description("The control displayed when OpenDropDown."), 
DefaultValue(typeof(Control), "Nothing"), Category("DropDown"), 
DisplayName("DropDownControl(Select form the form controls)")]
public Control DropDownControl {
//................
}
  1. The DropDownControl property is named in the property windows as DropDownControl (created in the form) uses the default ComponentConverter to choose the control from already created control that is present in the parent form
  2. NewDropDownControl property is named in the property window as DropDownControl (create new) uses our control converter and control editor to select a control from a list containing all supported controls for applications or we can type the control name directly such as TextBox, Button and so on.
  3. ControlText property is a simple text property that accepts and returns a text that includes the control type name followed by the assembly name and the control none default properties values.

The control converter uses the following code to convert the text to control:

'VB
'this code is a part of the Flexible Combo Box and Editing Control
Public Overrides Function ConvertFrom(ByVal context As ITypeDescriptorContext, _
 ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
    Try
        If value Is Nothing OrElse (Not TypeOf value Is String) _
        OrElse String.IsNullOrEmpty(CType(value, String)) Then
            Return Nothing
            Exit Function
        End If
        Dim vPropertyValues As New Dictionary(Of String, String)

        Dim TypeParts = Split(CType(value, String), "@")
        If UBound(TypeParts) <> 2 Then
            ReDim Preserve TypeParts(2)
        End If
        If String.IsNullOrEmpty(TypeParts(1)) Then
            TypeParts(1) = WinFormsAssemblyName
        End If
        vPropertyValues.Add(c_TypeName, TypeParts(0))
        vPropertyValues.Add(c_AssemblyName, TypeParts(1))

        Dim aPropertyValues() As String = Split(TypeParts(2), ",")
           For i As Integer = 0 To UBound(aPropertyValues)
              If (Not String.IsNullOrEmpty(aPropertyValues(i))) AndAlso _
                aPropertyValues(i).Contains(":"c) Then
                Dim KeyAndValue = Split(aPropertyValues(i), ":")
                vPropertyValues.Add(KeyAndValue(0).Trim, KeyAndValue(1).Trim)
            End If
        Next
        Return Me.CreateInstance(Nothing, vPropertyValues)
    Catch ex As Exception
        ErrMsg(ex)
        Return Nothing
    End Try
End Function
public override object ConvertFrom(ITypeDescriptorContext context, 
                System.Globalization.CultureInfo culture, object value) {
    try {
        if (value == null || (! (value is string)) || string.IsNullOrEmpty(Convert.ToString(value))) {
            return null;
            return null;
        }
        Dictionary<string,> vPropertyValues = new Dictionary<string,>();

        string[] TypeParts = Microsoft.VisualBasic.Strings.Split(Convert.ToString(value), 
                                 "@", -1, Microsoft.VisualBasic.CompareMethod.Binary);
        if (TypeParts.GetUpperBound(0) != 2)
            Array.Resize(ref TypeParts, 3);
        if (string.IsNullOrEmpty(TypeParts[1]))
            TypeParts[1] = WinFormsAssemblyName;
        vPropertyValues.Add(c_TypeName, TypeParts[0]);
        vPropertyValues.Add(c_AssemblyName, TypeParts[1]);

        string[] aPropertyValues = Microsoft.VisualBasic.Strings.Split(TypeParts[2], 
                                    ",", -1, Microsoft.VisualBasic.CompareMethod.Binary);
        for (int i = 0; i <= aPropertyValues.GetUpperBound(0); i++) {
            if ((! (string.IsNullOrEmpty(aPropertyValues[i]))) && aPropertyValues[i].Contains(":")) {
                string[] KeyAndValue = Microsoft.VisualBasic.Strings.Split(aPropertyValues[i], ":", 
                                             -1, Microsoft.VisualBasic.CompareMethod.Binary);
                vPropertyValues.Add(KeyAndValue[0].Trim(), KeyAndValue[1].Trim());
            }
        }
        return CreateInstance(null, vPropertyValues);
    } catch (Exception ex) {
        _ErrMsg.ErrMsg(ex);
        return null;
    }
}
  1. The Text format is TypeName@AssmblyName@ControlPropertyValues.
  2. First, the converter gets the type name and the assembly name properties values.
  3. It creates a properties values dictionary that comprises a pair of properties names and their values converted to string using the property assigned type converter and the assembly and type names.
  4. Create an instance of the control using CreateInstance method.
  5. To create an instance of the object from the type object, we use the following code:
    'VB
    'this code is a part of the Flexible Combo Box and Editing Control
    Public Overloads Shared Function CreateInstance(ByVal vType As Type) As Control
            Try
                '####### Dim c As New vType is not correct
                If vType Is Nothing Then
                    Return Nothing
                    Exit Function
                End If
                Dim ConstructorInfo = vType.GetConstructor({})
                If ConstructorInfo Is Nothing Then
                    Return Nothing
                    Exit Function
                End If
                Return CType(ConstructorInfo.Invoke({}), Control)
            Catch ex As Exception
                ErrMsg(ex)
                Return Nothing
            End Try
        End Function
    public static Control CreateInstance(Type vType) {
        try {
            //> Dim c As New vType is not correct
            if (vType == null) {
                return null;
                return null;
            }
            var ConstructorInfo = vType.GetConstructor(new System.Type[0]);
            if (ConstructorInfo == null) {
                return null;
                return null;
            }
            return (Control)(ConstructorInfo.Invoke(new System.Type[0]));
        } catch (Exception ex) {
            _ErrMsg.ErrMsg(ex);
            return null;
        }
    }
  6. To get the type object from the type name, we use the following code:
    Dim vAssembly = System.Reflection.Assembly.Load(AssemblyName)
    Dim vType = vAssembly.GetType(TypeName)
    var vAssembly = System.Reflection.Assembly.Load(AssemblyName);
    var vType = vAssembly.GetType(TypeName);

To view the dropdown control, I use ToolStripDropDown and ToolStripControlHost:

'VB
'this code is a part of the Flexible Combo Box and Editing Control
Private WithEvents Popup As ToolStripDropDown
Private Host As ToolStripControlHost

Public Property DropDownControl() As Control
    Get
        Return _DropDownControl
    End Get
    Set(ByVal value As Control)
        Try
           '....some code for testing and preparing value the cloning it to
            NewControl = ControlConverter.Clone(value)
            Popup.Items.Clear()
            Host = Nothing
            _DropDownControl = NewControl
            _DropDownControl.Margin = Padding.Empty
            _DropDownControl.Padding = Padding.Empty
            Host = New ToolStripControlHost(_DropDownControl)
            Host.Margin = Padding.Empty
            Host.Padding = Padding.Empty
            Host.AutoSize = False
            Host.Size = value.Size
            Popup.Items.Add(Host)
            Popup.Size = Host.Size
            Popup.AutoSize = True
            _DropDownControl.Location = New Point(0, 0)
            If _DisplayArea IsNot Nothing Then
                DisplayAreaValue = DropDownControlValue
            End If
            OnClosed(Nothing, Nothing)
        Catch ex As Exception
            ErrMsg(ex)
        End Try
    End Set
End Property

Protected Overrides Sub OnClick(ByVal e As EventArgs)
    Try
        MyBase.OnClick(e)
        If Me.DroppedDown Then
            Me.CloseDropDown()
        Else
            Me.OpenDropDown() 'Using Popup.Show method
        End If
    Catch ex As Exception
        ErrMsg(ex)
    End Try
End Sub

Public Overridable Sub OpenDropDown()
    Try
        If _DropDownControl Is Nothing OrElse Host Is Nothing OrElse _
        _DisplayArea Is Nothing Then Exit Sub
        Dim frm = _DisplayArea.FindForm
        Dim Pos As Point
        'additional code to adjust the position according to Drop Down Direction
        Pos = New Point(_DisplayArea.Left, _DisplayArea.Bottom + 1)
        Popup.Show(frm, Pos, ToolStripDropDownDirection.BelowRight)

    Catch ex As Exception
        ErrMsg(ex)
    End Try
End Sub

End of the article [this code is a part of the Flexible Combo Box and Editing Control]

totop