Dynamic options dialog (using reflection) - c#

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.

Related

MonoTouch.Dialog: StringElement with value, but hide value in UI?

I want the user to select one out of many elements.
So I'm creating a long list of StringElement, each one with a specific caption. Each element is associated with a specific value. My ideas was to set the Value property of the StringElement. However, this makes the value being shown on the right side of the element.
How can I hide this value? I only need it when the user tapped an entry.
Except for the most basic settings-like dialog I end up (90% of the time) defining my own Element types. It solves many issues (like this one) and reduce duplicated code.
So you get something like:
class MyStringElement : StringElement {
public MyStringElement (string caption, string hiddenValue) : base (caption) {
HiddenValue = hiddenValue;
}
public string HiddenValue { get; set; }
}
You might also want to use the caption as the key to (an existing?) Dictionary<string,string> to reduce the memory requirement of each element (depending on how long your list turns out to be). In any case having your own Element type makes it easier to change its storage/behaviour in the future (with minimal impact elsewhere in your code).

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?

Hidden Workflow Arguments to System.Activities.ActivityBuilder

I have rehosted the Workflow designer, and the base activity that i load is the Activity Builder(workflowDesigner.Load (activityBuilder);)
I basically need to add in some details to the activity, and the value of which the user should not be able to edit (or may be even see on the designer surface). For this I am (currently) adding some arguments as follows where i can add in the extra information.
activityBuilder.Properties.Add (new DynamicActivityProperty
{
Name = "HiddenArgument",
Type = typeof (string),
Value = "Value that the user should not edit."
});
But as these arguments are visible on the designer surface in the Arguments Panel on the bottom of the designer, the user can edit this. i also have other arguments that the user is allowed to edit so therefore i cant disable the whole arguments pane.
I would just want to know how can i add my information to the workflow(and obviously save it in the *.XAML file) so that the user cant edit (or see) this information.
EXTRA DETAILS: I basically want something like, if i create a custom activity i can add properties with [Browsable(false)], which causes the user to not see the property on the right side pane but hold a value!
EditorBrowsableAttribute is what you would use in a normal argument on a regular Activity.
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public InArgument<string> Foo { get; set;}
[Browsable(false)] - Only this attribute avoid presentation of this argument in PropertyInspector.
[EditorBrowsable(EditorBrowsableState.Never)] - doesn't affect avialabitly of this argument from Property Inspector, it make the property inaccessible by intellisense.
[Browsable(false)]
public InArgument<string> Foo { get; set;}

How to deal with *many* context menus

I'm re-writing in C# (with Winforms) an old VB6 app that uses a single context menu with multiple Items that change their Caption, Visible, and Enabled traits based on a monolithic function called "InitControls"
The function is 500 lines long and consists primarily of a switch statement that decides what controls to enable based on the selected item's tag (there's a tree view and list view; it selects the selected item from the active one and gets its tag). It then enables, disables, and modifies the text of the visible items, and clears any useless Separators. The original uses ActiveBar (a custom control) which allows it to change the text in one place and display the item in menus, context menus, and toolbars all at once.
I'm currently just re-implementing the logic line for line in C#, but I hate it because I'm not really fixing anything, just putting the problem into a new language (and possibly screwing it up in the process). I created a class that allowed me to change the text, enabled and visible properties of any "subscribed" Menu Items in one place and even add/remove event handlers for all subscriBed menu items. It works, and even seems apparently correct, but I'm pretty sure there's got to be a better way. My MainForm is ENORMOUS.
What is the standard .NET way of handling complex Context Menu and Toolbar logic?
From what I understand, you basically want to refactor a large switch-case method. Googling for "switch case refactoring" should give you several examples you can check out to find something that suits you best.
Usually, when you are refactoring a switch case, this means that you want to extract logic from each case block into a new class, possibly an implementation of an interface common to all cases. The right implentation of your class will depend on the condition of an individual case statement: this is called a Strategy pattern, because each condition demands a different strategy.
In your case, you need to slightly extend the pattern: you have a number of candidates for the context menu, each of them being able to handle a certain node type. In that case, your right-click handler needs to let them decide if they can provide functionality for a certain node.
[Edit]
To clarify a bit, I will provide a simple example.
I mentioned that individual implementations should be extracted into classes which implement the same interface, which should be responsible for changing menu items' appearance and state, based on the current condition.
interface IMenuStateManager
{
// this method updates state of one or
// more menu elements, according to the
// specified selected node info
void UpdateState(ISelectedNodeInfo info);
}
Our first, basic implementation of the IMenuStateManager interface will do nothing more that simply call other managers' implementations. This is called a Composite object pattern, because it allows us to treat a group of objects as a single object:
// composite class for a list of menu managers
class CompositeMenuStateManager : IMenuStateManager
{
private readonly IMenuStateManager[] _childManagers;
// params keyword will allow as to pass a comma separated list
// of managers, which is neat
public CompositeMenuStateManager(params IMenuStateManager[] managers)
{
_childManagers = managers;
}
// this is where the job gets done, but composite
// class doesn't do much work by itself
public void UpdateState(ISelectedNodeInfo info)
{
// allow each state manager to change its state
foreach (IMenuStateManager mgr in _childManagers)
{
mgr.UpdateState(info);
}
}
}
Now, you still have an enormous list of possible menu candidates, but now their logic is separated into different classes, and then wrapped in a single composite object.
IMenuStateManager _menuManager = new CompositeMenuStateManager
(
// note: each menu "manager" can manage one or more
// items, if you find it useful.
// For example, ClipboardMenuStateManager can be
// a composite manager itself (cut/copy/paste).
new ClipboardMenuStateManager(some params),
new SomeOtherMenuItemManager(various params),
new YetAnotherMenuItemManager(various params),
...
);
I guess that menu states get updated when a node is selected, but this is something you should easily adapt to your app. That particular event handler delegates the whole responsibility to our composite menu manager:
void Node_Selected(sender object, EventArgs args)
{
// find out which node was clicked
Node node = object as Node;
// get the data (model) node for this tree node
INodeData data = node.Tag as INodeData;
// create some info which will be passed to the manager.
// you can pass information that might be useful,
// or just simply pass the node data itself
ISelectedNodeInfo info = new SelectedNodeInfo(data, some other stuff);
// let the manager do the rest of the job
_menuManager.UpdateState(info);
}
Since you will probably have three menu items doing the same job at the same time (main menu, context menu, toolbar), you will probably want to make each IMenuStateManager implementation update all three of them at the same time. The simplest way should be to to pass an array of ToolStripItem objects, which is the base abstract class for several different menu elements:
class PrintMenuManager : IMenuStateManager
{
private readonly ToolStripItem[] _items;
// this constructor can accept several menu elements
public PrintMenuManager(params ToolStripItem[] items)
{
_items = items;
}
public void UpdateState(ISelectedNodeInfo node)
{
foreach (ToolStripItem item in _items)
{
// if node is printable, enable
// all "print" menu items and buttons
item.Enabled = (node.IsPrintable);
}
}
}
When creating the PrintMenuManager instance, you can pass all buttons and menu items which are related:
// (this should be one of the child managers in
// the composite menu manager, but you get it)
IMenuStateManager printMnuManaegr = new PrintMenuManager
(
this.printMenuItem,
this.printContextMenuItem,
this.printToolbarButton,
);
Whew, this turned out to be a lengthy one at the end. :)
Ok, that's about it for a start.

Bind an object which has a property of type List<> to a repeater

I've created two classes in business layer.
the first one is called Users with id (int), pass (string) and privileges (Privilege) properties and the second one is called Privilege and has id (int) and privilegeName (string) properties.
I've a method that returns all the users, and I use a repeater (actually I bind it to a DataList to auto create the ItemTemplate for me and then use a repeater) and it works and displays all the properties well except for my List property. it generates instead something like this System.Collections.Generic.List`1[WebApplication2.Public.BLL.Users]
I want to display it in a friendly way like "User Privileges : Privi1, Privi2" but still I want to keep the layers of my application clean and structured, for example I won't store them in a database in the same table and just store them as a text and append it.
I hope to find a simple and good solution...Thanks in advance guys =)
PS : I don't want to display the object Privilege, I want to display privilege.privilegeName
When using repeaters, there are two approaches, one is the one suggested by Bugai13: to have a custom property that displays it. This is fine for certain types of nested data.
Your other option is to just have a repeater inside a repeater, and bind it appropriately (to what would be a list assigned to your main data object, depending on how you O/R Mapper works).
You can have the code for the custom display property not in the data model, but in your presentation layer somewhere (depending on your framework/design), so it's not a "bad" thing to do that. It's up to you, with whatever "feels" best.
Just create property at your Bussiness object, and bind it:
public string PrivilegiesString
{
get
{
var sb = new StringBuilder("User Privileges : ");
foreach(var item in privileges)
{
sb.AppendFormat("{0}, ",item.privilegeName);
}
return sb.ToString();
}
}

Categories