Introduction
This article is going to explain how you can create a control during runtime, move the control in your form and finally how to add functionality to your control.
To do this task, this project comes in three parts:
- Add and move controls dynamically
- Code controls dynamically
- and finally serialize a form with all controls and all code to the harddrive
If you want to skip the whole reading part and start exploring, click on download demo project.
If you want to use the code yourself, just add a reference to all DLLs in the redist folder.
The code below shows how to do all 3 tasks:
using DynamicV2;
using Dyncontrols;
var Button=Extensions.Control_Create(typeof(Button), new Point(100, 100));
Extensions.Control_MoveandResize(Button);
new Codeeditor(Button).Show();
byte[] control=ControlFactory.Serialize(Button);
Background
Sometimes, we see that we want to make changes in the behaviour of our form. We need to add buttons and add events, but can we do all that during runtime?
Yes we can!
Some time ago, a little thing came out. It was called CodeDom Provider Class. It enables us to compile classes dynamically from scratch during runtime.
This basically means that we can create class instances out of thin air which we can use to subscribe and unsubscribe events and code for dynamic buttons.
To make things more convenient for you, I have used several assemblies written by other people for this project. I do not claim to have created any of them:
In my opinion by far the best C# editor control. Check it out:
Foundation for simple moving controls. I have added a snap to other control logic.
Foundation for the serialization of forms and controls. I made this recursive to handle controls with controls. (scrollbar, etc.)
Creating a Movable Control
Extensions.Control_MoveandResize(Button);
This also enables automatic layout positioning. Very handy.
Using the Compiler
The following lines are automatically created for you. You can enter new code in the editor window. All variables are casted into the right type. A doubleclick onto the event treeview
adds the code for the eventhandler
and enables easy code input. All variable names can be changed when the code window is closed. Meaning you can name all your buttons and checkboxes as you wish.
Below you see what actually happens:
#region Declarations
private System.Windows.Forms.CheckBox CheckBox0;
private System.Windows.Forms.GroupBox GroupBox0;
private System.Windows.Forms.Button Pressbutton;
private System.Windows.Forms.Form Mainwindow;
public void ControlInit(object Controlobj,List<control> Customercollection )
{
CheckBox0 = (System.Windows.Forms.CheckBox)Customercollection[0];
GroupBox0 = (System.Windows.Forms.GroupBox)Customercollection[1];
Pressbutton = (System.Windows.Forms.Button)Customercollection[2];
Mainwindow = (System.Windows.Forms.Form)Customercollection[3];
Pressbutton.Click+= new EventHandler(Pressbutton_Click);
}
#endregion Declarations
On the "other side" (not changeable part of the form) is this code: It compiles the string
and adds a reference to all controls into an object array passed to the changeclass which is created by codedom.
provider.CompileAssemblyFromSource(Parameters, syntaxEdit1.Text););
var o = results.CompiledAssembly.CreateInstance("Dynamicform.Changeclass");
MethodInfo setevents = o.GetType().GetMethod("ControlInit");
object[] arr = new object[2];
arr[0] = Patient;
arr[1] = Extensions.GetSelfAndChildrenRecursive(Patient.FindForm()).ToList();
try
{
setevents.Invoke();
}
If your code throws an exception, during execution there is a cryptic error message like: Unhandled exception in c826asdk.dll which won't help you.
So all exceptions are caught and the first two elements of the stack are displayed. --> Error in Button0_clickton0_click
.
Serializing a Form
Please check out:
Basically, all you have to do to serialize a control is to serialize all properties which are marked as serializable and store the parent and child references.
I have expanded the class to support serialisation to file or to byte[]
of a form or a control. If you wish to load a form from file, just write:
ControlFactory.Serializetofile(this, "Visuals.inf");
var form = ControlFactory.Deserializefromfile("Visuals.inf") as Form;
Application.Run(form);
Doesn't get any easier than that.
public static void Serializetofile(Control Serialize,string filename)
{
var controls = GetSelfAndChildrenRecursive(Serialize);
List<Tuple<CBFormCtrl, string,string>>
dictionary=new List<System.Tuple<CBFormCtrl,string,string>>();
foreach (Control k in controls)
{
if (k.Parent != null)
{
dictionary.Add(new Tuple<CBFormCtrl, string,string>
(new CBFormCtrl(k), k.GetHashCode().ToString(),k.Parent.GetHashCode().ToString()));
}
else
{
dictionary.Add(new Tuple<CBFormCtrl, string,
string>(new CBFormCtrl(k), k.GetHashCode().ToString(), "null")); }
}
var file = File.Create(filename);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(file, dictionary);
file.Close();
}
Points of interest
During the coding, I wanted to have icons for each control. I found out that there is a way to extract the toolbox image from any control!
var k=new BIcon.FromHandle(new Bitmap
(new System.Drawing.ToolboxBitmapAttribute(k.GetType()).GetImage(k)).GetHicon());
Conclusion
You can use this project to create working Windows Forms and import them in other applications. You can use parts of it for control serialization or control relocation. You can change all properties during runtime.
Limitations
Creating buttons moving them and making code for them during runtime is fun but it comes at a cost:
- Each time you edit your code, a new assembly is created and loaded into the appdomain. DLLs can not be unloaded from one appdomain.
- Not all properties are serializable. The buttons may look the same but they do not share most of the properties. All other links to controls but parent and children are lost.
- Debugging gets hard if you are working in a deserialized class instance.
- The codeditor uses the
tag
property of any control as storage space for the source code.