I just wanted to inform anyone looking into my WpfSplitButton control, that I have not been working on it since my last post about it and I do not have any plans of continuing with it. Hopefully it will serve as a good starting point for other developers. I must admit that it is not a long-term solution and should not be used in any final product. Although the code is yours to modify, extend, and use anyway you feel necessary.
February 4, 2010
C# CodeGenerationHelper – Dynamic Classes – Create Class From Hashtable at Run-time
Overview
Back a few months ago I had a class that needed (or so I thought) to be generated at run-time. The need arose from WPF controls only working with Properties, not fields, and more specifically ones that implemented INotifyPropertyChanged. (or dependency properties, but I typically save those for custom controls) After some research I created a simple straightforward helper class, CodeGenerationHelper, that would create a dynamic class at run-time from a hashtable. From a hashtable because my solution was working with data received through XML-RPC in the from of an extended hastable implementation. (XmlRpcStruct)
CodeGenerationHelper.cs Listing:
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.IO;
using Microsoft.CSharp;
namespace CodeGenerationTest
{
public static class CodeGenerationHelper
{
/// <summary>
/// Creates a dynamic class in memory from a hashtable. Each entry in the hashtable will become a field
/// with a corresponding property. The class will implement INotifyPropertyChanged for each of the
/// properties.
/// </summary>
/// <param name="namespaceName">The name of the namespace that will contain the dynamic class.</param>
/// <param name="className">The name of the dynamic class.</param>
/// <param name="source">The source hashtable that the members of the dynamic class will based off of.</param>
/// <param name="useKeysForNaming">If set to True, the keys of the hashtable will be converted to strings
/// and the field names and property names of the dynamic class will be based off of those strings. If
/// set to false, the fields and properties will be named in sequential order.</param>
/// <returns>Returns the C# code as a string.</returns>
public static string CreateClassFromHashtable(string namespaceName, string className, Hashtable source,
bool useKeysForNaming)
{
// Create compile unit.
CodeCompileUnit compileUnit = new CodeCompileUnit();
// Create namespace.
CodeNamespace dynamicNamespace = new CodeNamespace(namespaceName);
dynamicNamespace.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
// Create class.
CodeTypeDeclaration dynamicClass = new CodeTypeDeclaration(className);
dynamicClass.IsClass = true;
dynamicClass.BaseTypes.Add(new CodeTypeReference("System.ComponentModel.INotifyPropertyChanged"));
// Create PropertyChanged event; implement INotifyPropertyChanged.
CodeMemberEvent propertyChangedEvent = new CodeMemberEvent();
propertyChangedEvent.Name = "PropertyChanged";
propertyChangedEvent.Type = new CodeTypeReference("System.ComponentModel.PropertyChangedEventHandler");
propertyChangedEvent.Attributes = MemberAttributes.Public;
dynamicClass.Members.Add(propertyChangedEvent);
foreach (object key in source.Keys)
{
// Construct field and property names.
string fieldName = string.Format("_{0}", key.ToString());
string propertyName = key.ToString();
// Create field.
CodeMemberField dynamicField = new CodeMemberField(source[key].GetType(), fieldName);
dynamicField.InitExpression = new CodePrimitiveExpression(source[key]);
dynamicClass.Members.Add(dynamicField);
// Create property.
CodeMemberProperty dynamicProperty = new CodeMemberProperty();
dynamicProperty.Name = key.ToString();
dynamicProperty.Type = new CodeTypeReference(source[key].GetType());
// Create property - get statements.
dynamicProperty.GetStatements.Add(new CodeMethodReturnStatement(
new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
// Create property - set statements.
// Assign value to field.
dynamicProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
// Call PropertyChanged event.
// Create target object reference.
CodeEventReferenceExpression propertyChangedTargetObject = new CodeEventReferenceExpression(
new CodeThisReferenceExpression(), "PropertyChanged");
// Create parameters array.
CodeExpression[] propertyChangedParameters = new CodeExpression[]
{
new CodeThisReferenceExpression(),
new CodeObjectCreateExpression("System.ComponentModel.PropertyChangedEventArgs",
new CodeExpression[] { new CodePrimitiveExpression(propertyName) })
};
// Create delegate invoke expression and add it to the property's set statements; call PropertyChanged.
CodeDelegateInvokeExpression invokePropertyChanged = new CodeDelegateInvokeExpression(
propertyChangedTargetObject, propertyChangedParameters);
dynamicProperty.SetStatements.Add(invokePropertyChanged);
// Add property to class.
dynamicClass.Members.Add(dynamicProperty);
}
// Add class to namespace.
dynamicNamespace.Types.Add(dynamicClass);
// Add namespace to compile unit.
compileUnit.Namespaces.Add(dynamicNamespace);
// Generate CSharp code from compile unit.
StringWriter stringWriter = new StringWriter();
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
provider.GenerateCodeFromCompileUnit(compileUnit, stringWriter, new CodeGeneratorOptions());
stringWriter.Close();
return stringWriter.ToString();
}
public static object InstantiateClassFromCodeString(string codeString, string fullyQualifiedTypeName)
{
CSharpCodeProvider compiler = new CSharpCodeProvider();
CompilerParameters compilerParams = new CompilerParameters(new string[] { "System.dll" });
CompilerResults results = compiler.CompileAssemblyFromSource(compilerParams, new string[] { codeString });
return results.CompiledAssembly.CreateInstance(fullyQualifiedTypeName);
}
}
}
Now I’m not going to run through the details of how the class works, but I believe that it is well commented enough that with a little research it is easily understandable. I will demonstrate an example of how the class can be used.
Example Usage:
Hashtable rawCustomer = new Hashtable();
rawCustomer["Id"] = 123456789;
rawCustomer["FirstName"] = "Billy";
rawCustomer["LastName"] = "Bob";
rawCustomer["Rating"] = 84.5D;
string customerCode = CodeGenerationHelper.CreateClassFromHashtable("CodeGenerationTest", "Customer",
rawCustomer, true);
object customer = CodeGenerationHelper.InstantiateClassFromCodeString(customerCode, "CodeGenerationTest.Customer");
Now there isn’t much to this example simply because that’s as far as I’ve gone with it. I’m sure that it has many possible uses, but I will caution you about going down this route in the first place. A much better solution then generating code at run-time likely exists that will solve your particular problem. For me it was creating a DataTable from the hashtables I received from XML-RPC (for some reason this seems to work at least with the WPF DataGrid, I haven’t tried anything else as of yet). My point is I believe this is going into potentially dangerous areas and you should use this code generation method only as a last resort.
Okay, enough of that and back to the example. First you can see that I create a hashtable and fill it with name/value pairs. Notice though the Rating entry: it has a D suffix at the end of its value to force a type of double. When the code generation is executed this will be respected and enforced as you can see in the output below. Suffixes exist for each of the built-in value types and you can easily find them out with a little bit of research. (although I can’t recall what their formally referred to…) Next I pass the hashtable into the CreateClassFromHashtable method and I get back a complete string representation of our to-be dynamic class. (The output is shown in the section below) Finally we create an instance of our dynamic class. Now this is a very straight forward example and implementation, but I believe the information here provides a good starting point. If I end up continuing down the code generation path I will share my findings, and encourage others to do the same.
Example Output:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.3603
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DataGridTest {
using System.ComponentModel;
public class Customer : System.ComponentModel.INotifyPropertyChanged {
private double _Rating = 84.5;
private string _LastName = "Bob";
private int _Id = 123456789;
private string _FirstName = "Billy";
private double Rating {
get {
return this._Rating;
}
set {
this._Rating = value;
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Rating"));
}
}
private string LastName {
get {
return this._LastName;
}
set {
this._LastName = value;
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("LastName"));
}
}
private int Id {
get {
return this._Id;
}
set {
this._Id = value;
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Id"));
}
}
private string FirstName {
get {
return this._FirstName;
}
set {
this._FirstName = value;
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("FirstName"));
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
}
July 9, 2009
July 6, 2009
June 24, 2009
June 22, 2009
June 19, 2009
June 12, 2009
ArrayCasting Helper Class
I’ve found myself in the situation where I have an array of objects that I want to cast into an array of some other type. You’d think that this would be as simple as: (TheType[])objectArray, but unfortunately, one way or another, you have to loop through each object and cast it into the new type. This becomes a pain when you need to do it frequently.
May 21, 2009
Frame-Based Animation In WPF
Introduction
In WPF most animations are time-based, using storyboards to animate a objects properties over a certain period of time. That is great and works well, until an artist hands you a stack of images that are to be used as individual frames of an animation. This is my current situation, so I spent an hour or two trying to figure out a way to accomplish frame-based animations in WPF. This is what I came up with…