Hi all,
I was wondering if it is possible by using an appropriate custom CodeDomSerializer to generate code outside of the InitializeComponent() method.
All the examples I've seen generate a few additional statements inside this method.
But, for instance can I add another instance variable to the form/control Or add a new method
thanks,
Bert

fancy code generation
jdeguenther
The root designer is used for real-time data binding, alarms, etc.
StephenBarclay
___AUTUMNS_ENDING___
It is possible to use the DTE and the code model to get some info instead of the deserializer. You can do whatever you want this way, but you have to synchronize it with the codedomserializer yourself. Anyway, I wrote some code to inject a member into a class using the DTE. Here it is:
internal class CodeInjector
{
public static void InsertCode(DTE dte, IDesignerHost host, string code)
{
Document ourDoc = FindOurDocument(dte, host);
// OpenCodeDoc(dte, ourDoc); // may not be necessary
// find our class
string className = host.RootComponentClassName;
FileCodeModel cm = ourDoc.ProjectItem.FileCodeModel;
CodeClass cc = null;
foreach (CodeElement ce in cm.CodeElements)
{
cc = FindClass(ce, className);
if (cc != null)
break;
}
if (cc != null)
{
// first create codedom
CodeCommentStatement comment = new CodeCommentStatement(code);
// cannot generate code from a method -- have to use a type, or
// hope the method is already there
// generate a string from the codedom
CodeDomProvider provider;
if (ourDoc.Language == "CSharp")
provider = new Microsoft.CSharp.CSharpCodeProvider();
else if (ourDoc.Language == "VB")
provider = new Microsoft.VisualBasic.VBCodeProvider();
else
return;
ICodeGenerator generator = provider.CreateGenerator();
StringBuilder sb = new StringBuilder();
TextWriter writer = new IndentedTextWriter(new StringWriter(sb));
generator.GenerateCodeFromStatement(comment, writer, null);
writer.Flush();
// if the method already exists, delete body
EditPoint start = null;
foreach (CodeElement ce in cc.Members)
{
CodeFunction cf = ce as CodeFunction;
if (cf != null && cf.Name == "test")
{
start = cf.StartPoint.CreateEditPoint();
start.LineDown(2);
start.StartOfLine();
EditPoint end = cf.EndPoint.CreateEditPoint();
end.LineUp(1);
end.EndOfLine();
start.Delete(end);
break;
}
}
if (start == null)
{
if (ourDoc.Language == "CSharp")
{
CodeFunction cf = cc.AddFunction("test", vsCMFunction.vsCMFunctionFunction,
vsCMTypeRef.vsCMTypeRefInt, -1, vsCMAccess.vsCMAccessPrivate, null);
start = cf.StartPoint.CreateEditPoint();
start.LineDown(2);
start.StartOfLine();
}
else
{
// TODO: here error message
}
}
// insert the string into the function
start.Insert(sb.ToString());
}
}
private static CodeClass FindClass(CodeElement root, string className)
{
CodeClass cc = root as CodeClass;
if (cc != null)
{
if (cc.FullName == className)
return cc;
foreach (CodeElement ce in cc.Members)
{
if (ce is EnvDTE.CodeNamespace || ce is CodeClass)
{
cc = FindClass(ce, className);
if (cc != null)
return cc;
}
}
}
else
{
EnvDTE.CodeNamespace cn = root as EnvDTE.CodeNamespace;
if (cn != null)
{
foreach (CodeElement ce in cn.Members)
{
if (ce is EnvDTE.CodeNamespace || ce is CodeClass)
{
cc = FindClass(ce, className);
if (cc != null)
return cc;
}
}
}
}
return null;
}
private static Document FindOurDocument(DTE dte, IDesignerHost host)
{
foreach (Document doc in dte.Documents)
{
if (doc.Kind != Constants.vsDocumentKindText)
continue;
foreach (Window window in doc.Windows)
{
IDesignerHost windowHost = window.Object as IDesignerHost;
if (windowHost != null && windowHost == host)
return doc;
}
}
return null;
}
private static void OpenCodeDoc(DTE dte, Document document)
{
bool codeOpen = dte.get_IsOpenFile(Constants.vsViewKindCode, document.FullName);
if (!codeOpen)
{
ItemOperations ItemOp = dte.ItemOperations;
ItemOp.OpenFile(document.FullName, Constants.vsViewKindCode);
}
}
}
kanand
AndrewKnight
Nora6207
Andreas Sieber
First, be wary that many languages can generate richer code than they can parse. To date, only Managed C++ can parse everthing it generates in the code dom. So, if you try to get too fancy, you could get errors reparsing your code. Also, parsing code is done on demand, so the more methods you add the slower you will make the designer.
Second, don't assume that we will forever have a single method called InitializeComponent that stores all of our settings. You'll notice that it's hard to find that method today (you can actually check the CotextStack in your serializer for CodeMemberMethod to find out what method you're in), and we did that on purpose. It's possible (although we're not currently planning on doing it) that we may find that a huge method doesn't JIT as efficiently as several smaller ones, so we could*space
space*it up for runtime performance reasons.
Even internally we have a lot of people who want to change the way code is generated. It's a natural thing to want to do, but it does have pitfalls down the road you may not see. Most of the time, when I see people doing custom code generation, it's because they really want an object model that is different from what Windows Forms currently offers. Often that's not the best choice for customers who are having enough trouble adapting to the new world of Windows Forms -- here, consistency can be a good thing.
Christian Kuendig
I also agree that there are so many unknown services available for consumption at design time...
I have figured out many available services by going through the MSDN documentation for the ComponentModel (and nested) namespace, but keep coming accross other "undocumented" services that are available, and useful.
Anyone know of a place / mechanism to "discover" the services that are available
lennon60000
IComponentChangeService, IConfigurationService, IContainer, IDesignerHost, IDesignerLoaderService, IDesignerSerializationManager, IDesignerSerializationService, IEventBindingService, IEventHandlerService, IExtenderProviderService, IHelpService, IInheritanceService, ILicenseReaderWriterService, IMenuCommandService, IMenuEditorService, INameCreationService, IOleCommandTarget, IOverlayService, IPropertyValueUIService, IReferenceService, IResourceService, ISelectionService, ISelectionUIService, ISplitWindowService, ITypeDescriptorFilterService, ITypeResolutionService, CodeDomProvider, CodeTypeDeclaration, ComponentTray, InheritanceUI, ManagedPropertiesService, ProjectItem, SerializationResourceManager
Many thanks to "Zero Range" for posting this list.
Aw Ali
The fanciest piece of code generation it does is described (at the end of the article) in the snippet below.
As you can see, all it does is insert a statement at the beginning and one at the end of the CodeStatementCollection that makes up (I guess) the code of the InitializeComponent method.
But that is not what I want. I want to generate code OUTSIDE of the InitalizeComponent method. If I want to generate for instance the definition of a new instance variable, I can't do that in the InitializeComponent method for obvious reasons.
--- excerpt from article ----
public override object Serialize(
IDesignerSerializationManager manager,
object value)
{
// first, locate and invoke the default serializer for
// the ButtonArray's base class (UserControl)
//
CodeDomSerializer baseSerializer =
(CodeDomSerializer)manager.GetSerializer(
typeof(ButtonArray).BaseType,
typeof(CodeDomSerializer));
object codeObject = baseSerializer.Serialize(manager, value);
// now add some custom code
//
if (codeObject is CodeStatementCollection)
{
// add a custom comment to the code.
//
CodeStatementCollection statements =
(CodeStatementCollection)codeObject;
statements.Insert(0,
new CodeCommentStatement(
"This is a custom comment added by a custom serializer on " +
DateTime.Now.ToLongDateString()));
// call a custom method.
//
CodeExpression targetObject =
base.SerializeToReferenceExpression(manager, value);
if (targetObject != null)
{
CodeMethodInvokeExpression methodCall =
new CodeMethodInvokeExpression(targetObject, "CustomMethod");
statements.Add(methodCall);
}
}
// finally, return the statements that have been created
return codeObject;
}
}
--- end ---
Bert