How to render template by dotliquid from custom object? - c#

I need dynamic rendering email template from the specific object from my domain model. I know I can create objects and inherit it from Drop class. Also, I know I can add the specific type to safe type globally, but this doesn't solve the problem.
How to render a template from my specific types?
var message = _template.Render(Hash.FromAnonymousObject(new {Item = User}));
public class User { public string Name { get; set;}.....}
Safe type doesn't suitable for this task because in my specific object has nested objects and I need an access to them too.
Template.RegisterSafeType(typeof(User,string[] allowedMembers));
I can inherit my specific types from Drop class of dotliquid assembly but I think it doesn't help me.

RegisterSafeType is the solution if you want to avoid Drop and the alternatives.
Simply register all the relevant types (approximate code):
Template.RegisterSafeType(typeof(User), userAllowedMembers);
Then say User has a property of type Address. You can simply continue the registration with
Template.RegisterSafeType(typeof(Address), addressAllowedMembers);

Dim TemplateContent = CacheHelper.GetFileContentsNonCached("/Path/To/Template/File.htm")
Dim TemplateParsed = DotLiquid.Template.Parse(TemplateContent)
Return TemplateParsed.Render(Hash.FromAnonymousObject(New With {
.Item = User,
......
}))
Above code is in VB.Net but easy to convert to C#.
This doesn't require to register any type safe etc.

Related

Use reflection to get actual return type form Task<List<Dictionary<string,object>>> instead of object

I am trying to get the actual object that is contained within a list that itself is contained within a task.
e.g. method has the following signature e.g.
public async Task<List<Dictionary<string,object>>> GetData()
i am currently using something like this:
var member = type.GetMembers()[0];
var returntype = member.ReturnType.GetGenericArguments();
var temp = member.ReturnType.GetGenericArguments()[0];
if (temp.GetGenericArguments().Count() > 0)
{
temp.GetTypeInfo().GetGenericArguments();
var innerttype = temp.GetGenericArguments()[0].FullName;
}
Currently the above code (which is not complete but just an extract from actual code) return system.object as fullname instead of Dictionary.
Any suggestions to solve this are welcome.
If you declare your dictionary to be of type <string, object> and and only ever insert objects inside it and not something higher up in the graph, you'll only ever get object typed objects out of it.
If you're storing all kinds of things in there and need to get their concrete type so you can interact with them, try to make them all conform to an interface first. Then, put the interface type in there replacing "object." If that doesn't work you can use generics. However, you'll still need to know the type ahead of time in order to be able to interact with it.
If you really have no idea what's in there and want to dig into it dynamically, that's precisely what Reflection was built for. You could also look into the dynamic type.

Binding a class to another class's Property

The title may sound a little strange, so I'll try to explain my problem:
Lets say I have a class that holds some Information:
class InfoHolder
{
public int MyInfo1 {get; set;}
public int MyInfo2 { get; set; }
}
Then I have another class, that does something with an info:
class InfoGUIRepresenter
{
// Display an int in some kind of GUI
// Allow the user to change the int via the GUI
}
Now I would need two objects of the representer class, to expose my Info-class to the user: One representer for each of the two infos. To achieve that it would be nice to pass each of the properties as some kind of "parameter" to my representer classes.
But of course that's not possible in C#. Another solution would be to pass the names of the properties and then use reflection to access them - not very nice!
Is there any solution to this? Maybe some kind of architecture that addresses this kind problem?
Thanks!
One option is to pass two delegates - one for the setter and one for the getter:
var holder = new InfoHolder();
var representer = new InfoGUIRepresenter(() => holder.MyInfo1,
value => holder.MyInfo1 = value);
Then your InfoGUIRepresenter constructor would take a Func<int> and an Action<int>. (Or you could make the representer generic, for different types of property.)
You mention display "in some kind of GUI" which implies you are using a GUI framework. Surely that framework supports a well defined model for this sort of situation. Eg MVVM if you are using WPF (your info objects are models, your UI is a view and you can do binding from view to model via view model properties bound to UI element properties). I.e are you not trying to reinventing the wheel here?

Using user controls having his name on a string

i have a problem, the functionality I'm looking for exactly is:
I have a grid and datagrid, according to the line to select the datagrid there will be to introduce a user control or other user controls are different pictures I've made polylinesegments, bezier cuadratic ... to introduce the call will name, which build on a string, but I have no way to call it correctly.
This is what I do and it works by putting the full name:
d48.Children.Add(new tratsPintados.end148());
But put the string, tells me not find the path in the project, what I want is to find the path inside the string.
d48.Children.Add(new thestring());
Any ideas?
If you need to instantiate some class based on its name (without real reference), you will need to use Reflection.
Maybe you can do some lookup by name for the class you need, and then use Activator.CreateInstance to call its default constructor.
I hope this is what you want, the question text is quite confusing to me.
using System.Reflection;
public object GetObjectFromString()
{
string objectName = "WpfApplication1.uc1";
Type newType = Type.GetType(objectName, true, true);
object o = Activator.CreateInstance(newType);
// do what you want with the 'o' variable, maybe cast it to the type you want.
}

How can you cast to a type using the type name as a string?

Ok, I've thumped on this idea all day now, and I have reached the part where I admit I just flat out don't know. It's possible that what I'm doing is just stupid and there is a better way, but this is where my thinking has brought me.
I am attempting to use a generic method to load forms in WinForms:
protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new()
{
// Do some stuff
}
The forms are loaded by a ToolStripMenuItem (either through the selection of the item or using the Open Windows menu item). They are lazy-loaded, so there are fields for the forms within the MDI parent, but they are null until they are needed. I have a common method used for ToolStripMenuItem_Click that handles all of the menu item clicks. The method has no real way of knowing which form is being called for except that the name of the ToolStripMenuItem matches a pattern chosen for the form class names they correspond to. So, using the name of the ToolStripMenuItem, I can divine the name of the type of form being requested and the name of the private field allocated to store the reference for that form.
Using that, I can either use a growing/contracting switch statement with hard-coded types and string matches to call method with the specific type set (undesirable), or I can use Reflection to get the field and create the instance of the type. The problem to me is, System.Activator.CreateInstance provides an ObjectHandler that can't be cast to the types that I need. Here is a snippet of what I have so far:
string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", "");
string formType = formName.Substring(1);
FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance);
FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType);
}
this.LoadForm(ref formToLoad, false);
fi.SetValue(this, formToLoad);
I know the string name of the type that goes in for (????) but at compile-time I do not know the type because it changes. I have tried a bunch of ways to get this cast/instantiation to work, but none have been successful. I would very much like to know if it's possible to perform such a cast knowing the type only as a string. I tried using Type.GetType(string, string) to perform the cast, but the compiler didn't like it. If someone has a different idea on how to load the forms dynamically because I'm just doing it stupidly, please let me know about it.
This problem is usually resolved by casting to a common base class or interface of all potential types.
In C# 4, you can also assign it to a dynamic variable to hold the return value and call arbitrary methods on it. The methods will be late bound. However, I prefer to stick to the former solution whenever possible.
You'd be better off with the other overload that takes a Type and using e.g. Type.GetType(string).
FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
formToLoad =
(FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType"));
}
According to what you have, FormWithWorker must be (at least) as base class of the type you are instantiating, so you can do this:
FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
formToLoad = (FormWithWorker)System.Activator.CreateInstance("MyAssemblyName", formType);
}
While a common interface is one way to approach this problem, interfaces aren't practical for all scenerioes. The decision above is one of going with a factory pattern (switch statement - concrete class selection) or use reflection. There's a stack post that tackles this problem. I believe you can directly apply this to your issue:
Method Factory - case vs. reflection

Dynamic options dialog (using reflection)

Does anyone know of a good component (C# WinForms) which would allow creating an options (settings) form, given a custom class with a bunch of properties? I am not looking for something shiny, but something merely better than a property grid. I can easily take care of the visual part, but I simply don't want to lose time doing reflection to add and bind controls if it already exists.
I am pretty sure I've seen a Visual Studio options-like form somewhere before, which was created dynamically (with some attributes attached to the properties of the class, to allow grouping and additional info).
[Edit] For example, I might have an options class:
public class Options : SerializableOptions<Options>
{
[Category("General")]
[Name("User name")]
[Description("Some text")]
public string Username { get; set; }
[Category("General")]
[Name("Log in automatically")]
public bool LogInAutomatically { get; set; }
[Category("Advanced")]
// ConnectionType is enum
public ConnectionType ConnectionType { get; set; }
// ...
}
After passing it to this form, it would create two panels ("General" and "Advanced"), with a CheckBox and a TextBox on the first panel, and one ComboBox (with all available enums) on the second panel.
If there isn't such a control, what do you guys use? Manually add, populate, format and bind controls for each option?
I'm not aware of any controls that allow you to do this, but it isn't difficult to do yourself. The easiest way is to create the dialog shell, a user control which acts as the base class for the options "panels", one (or more) attribute to control the name and grouping information, and an interface (which the user control implements).
Each of your custom options panels derives from the user control and overrides some sort of Initialize() and Save() method (provided by the user control). It also provides your attribute (or attributes) that determine the name/grouping information.
In the dialog shell, reflectively inspect all public types from your assembly (or all loaded assemblies) looking for types that implement your interface. As you find a type, get the attributes to determine where to place it in your grouping (easiest thing here is to use a tree view), call Activator.CreateInstance to create an instance of the user control and store it in the Tag property. When the user clicks on an entry in the grouping (a tree node), get the Tag and set the panel which contains the user control to the object in the Tag property. Finally, when the user clicks "OK" on the dialog, loop through the tree nodes, get the Tag property and call the Save method.
Update:
Another option would be to use a property grid control. It doesn't have a "pretty" UI look to it, but it is very functional, already supports grouping by a category attribute, and allows a great deal of flexibility. You could go with a single property grid that shows all of the options, or go with a "hybrid" approach with a tree view that groups by major functions (plugin, capability, etc.), probably based on the type. When the user clicks that node, give the property grid the object instance. The only drawback to this approach is that when changes are made to the property grid values they are "live" in that the underlying property is immediately changed, which means there is no concept of "Cancel" short of saving a copy of each value that could change and performing some type of "reset" yourself.
I don't know if such a control exists, but writing the required reflection code is really not that hard. E.g. something like this:
// the class for which to create an UI
public class MyClass
{
public string Text { get; set; }
public int ID { get; set; }
}
...
// basic reflection code to build the UI for an object
var obj = new MyClass() { Text="some text", ID=3};
foreach (var pi in obj.GetType().GetProperties())
{
var name = pi.Name;
var type = pi.PropertyType;
var value = pi.GetValue(obj, null);
//now setup the UI control for this property and display the value
}
I accidentally found something similar to this, I remebered that I had this problem a while ago and thought I should share it.
Here is a simple example: http://blog.denouter.net/2008/08/simple-reflection-form.html. It uses reflection to create several controls based on object's properties.

Categories