I am implementing Code Generation for WindowsForm control at Design-Time, using a Custom CodeDomSerializer.
Here is what I have.
A user control i.e. MyControl written
in C#.
MyControl has a property
MyControlProperty of type ObjectXXX
that is accessible publicly.(like
myControl.MyControlProperty).
The type ObjectYYY has a public property
PropertyXXX of type Collection.
The ObjectXXX has a internal field of type ObjectYYY.
The ObjectXXX should be initialized by passing Collection (which
is nothing but ObjectYYY.PropertyXXX).
The code generated should be as given in the code snippet below.
Line1. NamespaceX.NamespaceY.ObjectXXX x = new NamespaceX.NamespaceY.ObjectXXX(NamespaceX.NamespaceY.ObjectYYY.PropertyXXX);
Line2. myControl.MyControlProperty = x;
I succeeded in generating the aforementioned code at Design-Time by writing a custom CodeDomSerializer FOR C# Language.
But, if i use MyControl for developing an application in C++ Language, the DOT operator is serialized for both ScopeResolution and Pointer-To-Member operator.
What I am doing for code in Line1 is,
string fullyQualifiedName = "NamespaceX.NamespaceY.ObjectYYY.PropertyXXX"; // HERE VARIABLE NAME IS HARDCODED WITH TWO TYPES OF OPERATORS
CodeExpression[] parameters = new CodeExpression[] {new CodeVariableReferenceExpression(fullyQualifiedName);};
CodeStatement code = new CodeVariableDeclarationStatement(typeof(ObjectXXX), "objectXXX1", new CodeObjectCreateExpression(new CodeTypeReference(typeof(ObjectXXX)), parameters));
generatedCode.Add(code); //generatedCode has the final code
For Line2,
CodeExpression codeLhs = new CodeVariableReferenceExpression(myControlVariable + "." + "MyControlProperty"); // HERE Pointer-To-Member IS HARDCODED AS DOT
CodeExpression codeRhs = new CodeVariableReferenceExpression("objectXXX1");
CodeAssignStatement codeAssignStmt = new CodeAssignStatement(codeLhs, codeRhs);
generatedCode.Add(codeAssignStmt); //generatedCode has the final code
Obviously the C++ Designer generated code should have '::' operator(and not DOT) for the ScopeResolution and '->' for the Pointer-To-Member resolution. I was not able to figure out how to make the code serialization for any CLR supported language.
How to solve this problem?
-Thanks a bunch
Dattebayo
Thanks for the quick reply.
I found the solution.
What i need was generating code containing property access and generating code for of .NET types.
To generate code that accesses a property, one should use CodePropertyReferenceExpression. This solves my problem with Line2.
To generate code that contains a Type, one should use Code CodeTypeReferenceExpression.
This combined with CodePropertyReferenceExpression solved problem with Line1.
Now, I am able to generate code properly w.r.t. the Language in use.
//For C# The code would be
NamespaceX.NamespaceY.ObjectXXX x = new NamespaceX.NamespaceY.ObjectXXX(NamespaceX.NamespaceY.ObjectYYY.PropertyXXX);
this.myControl.MyControlProperty = x;
//For C++ The code would be
NamespaceX::NamespaceY::ObjectXXX x = new NamespaceX::NamespaceY::ObjectXXX(NamespaceX::NamespaceY::ObjectYYY::PropertyXXX);
this->myControl->MyControlProperty = x;
Not sure if this will help, but have you looked at MyGeneration its a multi language code generator written in .Net. It doesn't use CodeDomSerializer, but it does generate good code - maybe it'll solve your underlying problem without having to re-invent the wheel?
Related
I'm in the middle of trying to transfer VBA code over to C# so that form application I made is standalone. I'm having issues with trying to get the text from a shape that is within a shape(in a group). Here is an example in VBA that works flawless.
Dim text as String
text = groupShape.shapes.item("rectangle").text
This is returning the correct value.
In C# I am using the microsoft.office.interop.visio.dll reference. I've been able to read in shape names just fine so I know I have the application and document objects working fine. In C# the code above looks like:
var text = "";
text = Doc.Shapes.ItemU("groupShape").Shapes.ItemU("rectangle").text
This is not returning the correct value. It is saying that it will return "Object" so I believe its not returning a string but an object.
What can I do to solve this in order to return the text? Thanks!
EDIT:
Here is the actual code I am writing. The vba and the c#
VBA:
Dim tempShape As Shape
Set tempShape = ActiveDocument.Pages(pageName).Shapes.Item("MainTable")
txtJobName.value = tempShape.Shapes.Item("textJobName").Text
c#
IVisio.Shape tempShape = Doc.Pages[Loaded_Page.Name].Shapes.ItemU["MainTable"];
Txt_JobName.Text = tempShape.Shapes.ItemU["textJobName"].Characters.Text;
ANOTHER EDIT:
I've also tried going into the shape data properties by referencing the cell that the text is in and still no luck.
Txt_JobName.Text = tempShape.Shapes.ItemU["textJobName"].CellsSRC[8, 0, 0].ResultStr[0];
First, try using , .Item not .ItemU, those two are different functions. The default one (one that you are using implicitly in VBA) is Item. You may be actually addressing a different shape in c#
Second, .Text should be starting with capital T, the code with small t should not compile. Make sure that .text in lowercase is not an extension method you imported from somewhere, (unrelated to Visio)
Third caveat - to make sure to get text from shape as you see it in UI, it is better to use shape.Characters.Text, because in case shape contains fields, the shape.Textwill return placeholders ('obj') for those places where the field text should go. But this applies to both VBA and C#
Fourth.. groupShape in VBA appears to be a variable name, not shape name. From the code it's not clear what is the name of that group shape. How do you get that one in VBA? May be worth checking.
I'm not very familiar with the Visio type library, but this:
groupShape.shapes.item("rectangle")
Is returning a Shape object. Grab that object reference instead of discarding it with another dot. VBA is being "helpful" here, and happily lets you write late-bound calls that will only be resolved at runtime - C# will only be that permissive with the dynamic keyword, and you typically don't want to code against dynamic - you want to work with strong types:
Dim rectangle As Shape
Set rectangle = groupShape.shapes.item("rectangle")
Dim rectangleText As String
rectangleText = rectangle.Text
The C# / interop code needs to do the same:
var groupShape = (Shape)Doc.Shapes.ItemU("groupShape");
var rectangle = (Shape)groupShape.Shapes.ItemU("rectangle");
var rectangleText = rectangle.Text // assuming Text is defined on the Shape interface
In other words, always be aware of the types you're working with; if a member returns an object and you need it to be a more specific type, you need to cast as appropriate before you can have compile-time validation of the member call, or cast to dynamic if you don't care for compile-time validation and defer them to run-time:
var text = (string)((dynamic)groupShape.Shapes.ItemU("rectangle")).Text
Notice the cast to string; text will be a string. Forget that cast and your code will have to deal with dynamic spreading throughout the execution paths like a cancer and ruining your type safety - and you don't want that.
As always, I come to find out the reason the .Text was returning "Obj" is that the shape did not have any text in it. If there is text, it will return it. I got too caught up into the return, "Obj". I wonder why it doesn't return empty text or null if there is no text in the shape. Anyways, all the other answers on this question are very helpful and they lead me to finding my problem.
I’m developing a mobile application which among other things, it receives data from medical devices via Bluetooth. To this end, I’m using an Android SDK in JAR via binding to my Xamarin project. Here’s the class that holds the returned data from the decompiled JAR:
package com.contec.jar.contec08a;
import java.util.ArrayList;
public class DeviceData
{
public ArrayList<byte[]> mData_blood = new ArrayList();
public ArrayList<byte[]> mData_oxygen = new ArrayList();
public ArrayList<byte[]> mData_normal_blood = new ArrayList();
}
What is of interest is the mData_blood. Each element of the array list corresponds to a patient. Each byte array is the medical data for each patient. The Xamarin framework makes some changes, i.e. the name of the property becomes MDataBlood.
Problem: when I receive the above class and property, the casting fails BUT NOT in the debugger. Here’s what I mean: the ‘as’ operator that implement the cast fails (returns null) but in the debugger the very same expression correctly shows the data.
What’s more, the ‘is’ operator returns false in runtime (the canDo variable is false) but true in the debugger. I’ve tried all methods of casting I’m familiar with, even using –but not mixing – Android objects. The highlighted line which casts the IList to List produces a nice exception. I’m at a total loss. Any suggestions would be greatly appreciated.
Here's the screenshot from the debugger which illustrates all the above:
Edit1: The MDataBlood[0] evaluates as generic Java object. When reviewing its properties, its isArray is set to true. By checking the decompiled source of the SDK, I determined that is indeed byte array.
In Xamarin, there are some castings that might fail when the objects come from java types. In that cases you should use JavaCast<TResult> instead of a regular casting.
Try this, instead of your current approach:
// Since MDataBlood is exposed as an IList property of MDeviceData,
// we first need to cast it to IJavaObject
var dataBloodRaw = (IJavaObject)dm.MDeviceData.MDataBlood;
// Then we have access to its JavaCast method
var dataBlood = dataBloodRaw.JavaCast<JavaList<byte[]>>();
JavaList<T> extends Java.Lang.Object, so it can be used as the TResult parameter for JavaCast<TResult>. Also, it implements IList, so you should be able to loop through it.
Anyway, if you need a List<byte[]> you can use the casted JavaList for creating it:
var dataBloodList = dataBlood.ToList();
Or, alternatively
var dataBloodList = new List<byte[]>(dataBlood);
Yet another thing you could just try is to Java-cast dataBloodRaw to a JavaList<object> and then loop through its objects, trying to cast each one of them as a byte[].
Plan B
If none of the above works, I suggest you to take a look at this answer where it is suggested to disable linking for Release, which can be done in your project properties:
Keep in mind that a side effect of this last option will be the impact on the final size of your application.
i'm about to make a graduation project application
this application is gonna some way receive a description for a situation , and then accordingly generate c# code
i want to know in what field i need to search or how to autogenerate C# code
Have a look at Kathleen Dollard's book on this if you can. She has a website for this topic as well.
You have three options essentially:
Brute-force - creating the code files yourself in a text file
CodeDOM generation - MS's built in way of creating code.
XSLT - What Kathleen uses.
T4 templates can help too -
http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx
And you could also generate IL on the fly. ;)
CodeDOM
I've done a wrapper around codedom. You only need to create your own C# script and specify the types being used.
Example
public interface IWorld
{
string Hello(string value);
}
string code = #"namespace MyNamespace
{
class Temp : IWorld
{
public string Hello(string value)
{
return "World " + value;
}
}
}";
Compiler compiler = new Compiler();
compiler.AddType(typeof(string));
compiler.Compile(code);
var obj = compiler.CreateInstance<IWorld>();
string result = obj.Hello("World!");
Note that it was a long time ago that I wrote it. The example might not work 100%. (The Compiler class do work, the example might use it incorrectly).
Compiler source code: http://fadd.codeplex.com/SourceControl/changeset/view/65227#925984
Reflection.Emit
You can also generate IL using Reflection.Emit: http://msdn.microsoft.com/en-us/library/3y322t50.aspx
It's a bit harder but more flexible, since CodeDOM generates a new Assembly each type you compile code.
There is a set of MatLab tools that generates C/C++ code from state-charts and data-flow diagrams:
Real Time Workshop
Real-Time Workshop Embedded Coder
Stateflow Coder
You should dig into it.
What will be the "description of a solution" in your case?
I am playing around with the interop between C# and IronRuby. I have noticed that if I define a property in Ruby using attr_accessor, it is presented to C# as a property. If, on the other hand, I create the exact same code manually, it comes back as a method.
For example, take this code:
var engine = IronRuby.Ruby.CreateEngine();
string script = #"
class Test
attr_accessor :automatic
def manual
#manual
end
def manual=(val)
#manual = val
end
def initialize
#automatic = ""testing""
#manual = ""testing""
end
end
Test.new
";
var testObject = engine.Execute(script);
var automatic = testObject.automatic;
var manual = testObject.manual;
When you look at the C# automatic variable, the value is a string of "testing". If you look at the C# manual variable, it is type IronRuby.Builtins.RubyMethod.
Ultimately, I want to create my own properties in Ruby that can be used in C#, but I can't seem to make them be visible as properties like attr_accessor does.
I THINK, that there is some magic going on in the Module code of the Ruby source code (ModuleOps.cs:DefineAccessor). Is there any way to do this in Ruby code directly?
This particular problem was discussed further in the IronRuby-Core thread:
http://rubyforge.org/pipermail/ironruby-core/2010-July/007154.html
CodeVariableDeclarationStatement hashTableParam = new CodeVariableDeclarationStatement();
hashTableParam.Name = "hashtable";
hashTableParam.Type = new CodeTypeReference(typeof(System.Collections.Hashtable));
In the above code i tried to do a basic declaration for hashTable.
Now i need to added values to the hash table(key,value).
ht.Add("testKey","testData")
i.e how can i generate such code to add keys and values to HashTable programatically using codeDom
There is probably a way to do this that is more CodeDom oriented, which would allow for easier cross language support, but this will work
Assuming you already have a class created and a method, create a new CodeSnippetExpression and put the code you want in it, and then add that to your method body.