Introduction
Similar Library
- Xceed Combo and Grid
- DXperience
How this Library Differs
| Xceed | DXperience | This Library |
Direct assignment to any control to combo | Yes | No | Yes |
Direct assignment to any control to column | Yes | Choose for pre stored inline editors | Yes |
Supported Grid | Xceed Grid | DXperience Grid | DataGridView |
Can choose control that is not present in the form | No | No | Yes |
Freeware | No | No | Yes |
Lightweight simple library | No | No | Yes |
Has many other useful components | Yes | Yes | No |
Why Do I Create This Application?
- I need to extend the
DataGridViewColumn
without rewriting it each time. - I need a flexible
combobox
that accepts any control as a dropdown.
What Does This Application Do and Why is it Useful?
- It could be used to build your own grid column or your own combo in a simple way.
- Use the included control convert to clone copy and create controls dynamically at run and design time.
- Use the included control editor to change the control type that you create at design time.
- Both control converter and control editor could be used as
TypeConverter
and Editor
attribute in developing new controls.
What Problem is Solved
- Our component has a control property that creates controls of different types at design time and stores the created control as simple text value.
- Value property name of the drop down control or editing control could be specified as needed at design time, for example:
Control | Value Property Name |
NumericUpDown | Text |
CheckedListBox | CheckedItems |
TimeEdit | Value |
Value
property name could be list
or collection
and the value will converted to string
that contains all items in the collection.
Using the Code
- Create new forms in Windows application.
- Add reference to
AnyControlComboColumn
. - Add new Data Grid View and add new column and set its type to
GridColumn
. - In the property window, go to
EditingControl(Create New)
property and choose the control type from the drop down list. - From tool box, add to the form
DropDownCombo
. - In the property window, go to
DropDownButton
property and expand it. - Under
DropDownButton
, go to DropDownControl(Create New)
property and choose the control type form the drop down list.
The Demo Application
The demo application contains DropDownCombo
that we could choose the drop down control of it to be: CheckedListBox
or DirList
or FileList
, and it contains a DataGridView
with two columns - the first with NumericUpDown
as an editing control and the second column is with Mask Text Box as an editing control.
About this Library
This library contains the following classes:
DropDownButton
: This class provides a drop down button that accepts any control as a drop down control and any other control to be the display area. DropDownCombo
is a text box that contains the above drop down button and so it provides text box with drop down control that could be set or changed at run time or at design time. ControlConverter
and ControlEditor
provide technique to serialize any control to simple text to be stored in the form designer. EditingControl
, GridCell
and GridColumn
provide the ability to use any control as a editing control in DataGridView
.
About C# Code in This Article
This library is developed using VB. The C# code herein is obtained by code converters. For more information about code converters, please see:
About the DropDownButton
- The drop down button is the main class for creating drop down combos.
- When it is added to any control, it will be docked to the right side.
- It could be assigned with two controls: the first is the drop down control and the second is the display area.
- 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
- The drop down control is the control that will be dropped down when the drop down button is clicked.
- To set the drop down control, we could choose a 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 include a reference to them. - When clicking the drop down button, the drop down value will be changed to meet the value of the display area
- When the drop down popup is closed, the display area value will be changed to meet the value of the drop down control
DropDownCombo
is a Text box that contains drop down button.
About the GridColumn
GridColumn
is the class which is used to extend the DataGridView
to accept any control as editing control. - To use this class, we need to use
DataGridView
and add GridColum
to it from DataGridView
Designer window. - To set the editing control, we could choose a control in the form or choose form a list that will contain all control types in
system.windows.forms
assembly and assemblies that the application include a reference to them.
About ControlConverter and Control Properties Serialization
Before building the control property of the drop down control, we need to serialize it to string
to be stored in the form designer but don't use XmlSerializer
to serialize control properties because:
XmlSerializer
not supported types:
ISite
IDictionary
- Any class that Implements unsupported interface
- Any class has property that return unsupported interface
Control
PropertyDescriptorCollection
SortedList
Collection
TreeNodeCollection
TreeNode
- Any class that Inherits not supported class
- Any class has property that returns unsupported class
- Also
XmlSerializer
can serialize the following types, but it can't de-serialize them:
ArrayList
- List of Objects
ListBox.ObjectCollection
CheckedListBox.ObjectCollection
XmlSerializer
returns multi line value with and it is better to store the control property as simple text - To test
XmlSerializer
, we could use the following code:
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
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
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 {
var f = Serializer.Deserialize(vMemoryStream);
} catch (Exception ex) {
return "";
}
return s;
} catch (Exception ex) {
return "";
}
}
}
}
We don't use the default ComponentConverter
because:
- It only allows to select from a pre created controls in the form.
- It does not allow to choose a control that is not present in the form.
- It can be used at design time only and can't be used at run time.
What ComponentConverter
does:
- Creates an Instance of the control from the selected type.
- Converts the control to
string
to be stored as ControlText
. - Converts the control text to control object.
Points of Interest
To assign drop down control to the drop down button, we use the following properties:
<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
[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 {
}
[Description("The control displayed when OpenDropDown."),
DefaultValue(typeof(Control), "Nothing"), Category("DropDown"),
DisplayName("DropDownControl(Select form the form controls)")]
public Control DropDownControl {
}
- 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 NewDropDownControl
property is named in the property windows 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 could type the control name directly such as: TextBox
, Button
and so on. ControlText
property is a simple text property that accepts and returns a text that includes the control type name followed with the assembly name and the control none default properties values.
The control converter uses the following code to covert the text to 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;
}
}
- The
Text
format is TypeName@AssmblyName@ControlPropertyValues
. - First of all, the converter gets the type name and the assembly name properties values.
- It create a properties values dictionary which consist of a pair of properties names and their values converted to
string
using the property assigned type converter in addition to the assembly and type names. - Create an instance of the control using
CreateInstance
method. - To create the instance of the object form the type object, we use the following code:
Public Overloads Shared Function CreateInstance(ByVal vType As Type) As Control
Try
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 {
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;
}
}
- 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 drop down control, I use ToolStripDropDown
and ToolStripControlHost
:
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
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()
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
Pos = New Point(_DisplayArea.Left, _DisplayArea.Bottom + 1)
Popup.Show(frm, Pos, ToolStripDropDownDirection.BelowRight)
Catch ex As Exception
ErrMsg(ex)
End Try
End Sub
History