I’m using a standard C# WinForms Designer to let the user create a Dialog box. When the user wants to show the dialog box, I compile it into an Assembly file (a DLL) using my own subclass of CodeDomDesignerLoader, which I call FormEditorCodeDomDesignerLoader.
The code that creates the CodeCompileUnit in the FormEditorCodeDomDesignerLoader looks like this:
protected override CodeCompileUnit Parse()
{
DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof (Form));
IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));
return CreateCCU (idh);
}
private CodeCompileUnit CreateCCU (IDesignerHost idh)
{
idh.RootComponent.Site.Name = "Form1";
cg = new FormEditorCodeGen();
CodeCompileUnit ccu = cg.GetCodeCompileUnit(idh);
AssemblyName[] names = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
for (int i = 0; i < names.Length; i++)
{
Assembly assembly = Assembly.Load(names[i]);
ccu.ReferencedAssemblies.Add(assembly.Location);
}
codeCompileUnit = ccu;
return ccu;
}
internal class FormEditorCodeGen
{
private CodeCompileUnit codeCompileUnit;
private CodeNamespace ns;
private CodeTypeDeclaration myDesignerClass = new CodeTypeDeclaration();
private CodeMemberMethod initializeComponent = new CodeMemberMethod();
private IDesignerHost host;
private IComponent root;
/// <summary>
/// This function generates the default CodeCompileUnit template
/// </summary>
public CodeCompileUnit GetCodeCompileUnit(IDesignerHost host)
{
this.host = host;
IDesignerHost idh = (IDesignerHost)this.host.GetService(typeof(IDesignerHost));
root = idh.RootComponent;
Hashtable nametable = new Hashtable(idh.Container.Components.Count);
ns = new CodeNamespace("YetiFormEditor");
myDesignerClass = new CodeTypeDeclaration();
initializeComponent = new CodeMemberMethod();
CodeCompileUnit code = new CodeCompileUnit();
// Imports
ns.Imports.Add(new CodeNamespaceImport("System"));
ns.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
ns.Imports.Add(new CodeNamespaceImport("System.Windows.Forms"));
ns.Imports.Add(new CodeNamespaceImport("AGI.FormEditor"));
code.Namespaces.Add(ns);
myDesignerClass = new CodeTypeDeclaration(root.Site.Name);
myDesignerClass.BaseTypes.Add(typeof(Form).FullName);
IDesignerSerializationManager manager =
host.GetService(typeof(IDesignerSerializationManager)) as IDesignerSerializationManager;
ns.Types.Add(myDesignerClass);
// Constructor
CodeConstructor con = new CodeConstructor();
con.Attributes = MemberAttributes.Public;
con.Statements.Add(new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "InitializeComponent")));
myDesignerClass.Members.Add(con);
// InitializeComponent
initializeComponent.Name = "InitializeComponent";
initializeComponent.Attributes = MemberAttributes.Private;
initializeComponent.ReturnType = new CodeTypeReference(typeof(void));
myDesignerClass.Members.Add(initializeComponent);
codeCompileUnit = code;
return codeCompileUnit;
}
}// class
The code that takes the CodeCompileUnit and turns it into an Assembly (DLL) file looks like this:
public bool Build (string dllFileName)
{
Flush();
// We need to collect the parameters that our compiler will use.
CompilerParameters cp = new CompilerParameters();
AssemblyName[] assemblyNames = Assembly.GetEntryAssembly().GetReferencedAssemblies();
foreach (AssemblyName an in assemblyNames)
{
Assembly assembly = Assembly.Load(an);
cp.ReferencedAssemblies.Add(assembly.Location);
}
cp.GenerateExecutable = false; // only set to true for code that contains an entry point
cp.OutputAssembly = dllFileName;
// Remember our main class is not Form, but Form1!
string mainClass = "YetiFormEditor.Form1";
cp.MainClass = mainClass;
CSharpCodeProvider cc = new CSharpCodeProvider();
CompilerResults cr = cc.CompileAssemblyFromDom(cp, codeCompileUnit);
return !cr.Errors.HasErrors;
}
Here’s my problem:
Whenever any of the controls within the Dialog box being designed has a string Property that’s longer than 200 characters, the code in the generated DLL contains an implicit call to retrieve this string from the DLL Assembly’s resources – and the DLL Assembly doesn’t HAVE any string resources! This causes an exception when attempting to call dllAssembly.CreateInstance (“YetiFormEditor.Form1”);. If the string Property is 200 characters or shorter, this exception does not occur and the dialog box gets created successfully.
I looked at the C# code in the CodeCompileUnit (via CSharpCodeProvider.GenerateCodeFromCompileUnit() ). If the string Property is 200 characters or less I’ll see this:
this.comboBox1.MyProperty = "012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"12345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012345678901234567890123456789";
… but if the string Property is more than 200 characters I’ll see this:
this.comboBox1.MyProperty = resources.GetString("comboBox1.MyProperty");
I cannot believe no one else has run into this issue, considering how commonplace Properties longer than 200 characters must be (e.g. Image properties, the contents of a multi-line text edit, etc.). So:
Is there a way to either make the CodeCompileUnit (or the CompileAssemblyFromDom() call) explicitly include the string resources this generated code depends on, or a way to make the CodeCompileUnit/CompileAssemblyFromDom() generate code for long strings that doesn’t require resources?
Thanks!