Is there a better way to create Form Controls for properties of a class? Right now, I iterate through all properties of a class and have a method to create the Form Control for that property based on the type of the property.
private void AddControl(PropertyInfo p, string name = null, Color? c = null, int count = 0)
{
Label newLabel = new Label();
TableLayoutPanel tp = new TableLayoutPanel();
Control ctrl = new Control();
tp.ColumnCount = 2;
tp.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F));
tp.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 215F));
tp.Margin = new Padding(0);
tp.Name = "tp";
tp.RowCount = 1;
tp.RowStyles.Add(new RowStyle(SizeType.Absolute, 28F));
tp.Size = new System.Drawing.Size(380, 28);
tp.Location = new Point(0, 14 + num * 28);
newLabel.Name = "lbl_" + num;
newLabel.Text = GetPropertyAttributes(p); // gets a custom attributes for the property
newLabel.AutoSize = true;
newLabel.Anchor = AnchorStyles.Left;
tp.Controls.Add(newLabel);
ToolTip myToolTip = new ToolTip();
switch (p.PropertyType.Name)
{
case "String":
ctrl = new TextBox();
ctrl.Tag = "String";
ctrl.Size = new System.Drawing.Size(170, 20);
goto case "common";
case "Boolean":
ctrl = new CheckBox();
goto case "common";
case "Array[]":
Console.WriteLine("Array");
break;
case "Int32":
ctrl = new TextBox();
ctrl.Tag = "Int";
ctrl.Size = new System.Drawing.Size(40, 20);
goto case "common";
case "Double":
ctrl = new TextBox();
ctrl.Tag = "Double";
ctrl.Size = new System.Drawing.Size(40, 20);
goto case "common";
case "IList`1":
if (ClassExists(p.Name)) // ClassExists checks if the property is a class itself
{
ctrl = new Button();
ctrl.Text = "Add";
ctrl.Tag = p;
ctrl.AutoSize = true;
ctrl.Click += new EventHandler(AddButton_Click);
}
else
{
// TODO create gridview
}
goto case "common";
case "common":
ctrl.Anchor = AnchorStyles.Left;
ctrl.Name = name ?? GetPropertyAttributes(p);
ctrl.Font = new Font("Consolas", 9);
ctrl.KeyPress += new KeyPressEventHandler(textBoxKeyPress);
if (c != null)
{
tp.BackColor = c ?? Color.Black;
}
tp.Controls.Add(ctrl);
formControls.Add(tp); // List<Control>
break;
default:
ctrl = new Button();
ctrl.Text = "Add";
ctrl.Tag = p;
ctrl.AutoSize = true;
ctrl.Click += new EventHandler(AddButton_Click);
goto case "common";
}
}
private void AddControls()
{
foreach (Control ctrl in formControls)
{
MainPanel.Controls.Add(ctrl); // MainPanel is a Panel Control
}
}
Can it be simplified? I may have to add more types to it.
-
\$\begingroup\$ yes, use xaml and a view model \$\endgroup\$Ewan– Ewan2015年09月27日 17:41:29 +00:00Commented Sep 27, 2015 at 17:41
-
\$\begingroup\$ Could you elaborate that a bit more? \$\endgroup\$vice_versa– vice_versa2015年09月27日 17:53:40 +00:00Commented Sep 27, 2015 at 17:53
-
2\$\begingroup\$ @Ewan ... it's winforms, not WPF. And what's more you should also elaborate \$\endgroup\$Robert Snyder– Robert Snyder2015年09月27日 18:10:56 +00:00Commented Sep 27, 2015 at 18:10
-
\$\begingroup\$ I recommend using the Property Grid class. It's a winforms class that does essentially what you are doing. For areas that you need a little more control there is the ability to edit how it shows certain types of values. Here is an example of use c-sharpcorner.com/UploadFile/mgold/… \$\endgroup\$Robert Snyder– Robert Snyder2015年09月27日 18:16:48 +00:00Commented Sep 27, 2015 at 18:16
-
1\$\begingroup\$ Thanks for the explanation, I didn't know that WPF is the newer language, I will give it a try, since I have no deadline at all, I have plenty of time to learn :) \$\endgroup\$vice_versa– vice_versa2015年09月29日 12:38:13 +00:00Commented Sep 29, 2015 at 12:38
1 Answer 1
In my commented I suggested a class that could do what you want it to do out of the box. However sometimes having a custom piece of code to show things in a better way is the goal. I can see this going in two different ways. One is to use a variation of the Visitor pattern and a list, and the other is to use a dictionary with Type
and the key, and the class that represents how to view it as the value.
The first way you could make the IVisitor
interface one of two ways.
public interface IVisotor
{
//option 1
void Accept(Control control);
//option 2
bool Accept(Control control);
}
then to implement you'd make a new class per type that you want to show a specific way. Here is an example with both.
public class StringVisitor : IVisitor
{
public bool Accept(PropertyInfo property, Control control)
{
if(property.PropertyType != typeof(string))
return false;
Accept(control);
return true;
}
public void Accept(Control control)
{
control = new TextBox();
control.Tag = "String";
control.Size = new System.Drawing.Size(170, 20);
}
}
Now in your class you have two options, make a List<IVisitor>
or make a Dictionary<Type, IVisitor>
. Here is an example of what I mean. It is by no means complete (and might even have a compiler error as I did it in Notepad++)
public class PropertyViewer
{
private readonly List<IVisitor> visitors1;
private readonly Dictionary<Type, IVisitor> visitors2;
public PropertyViewer()
{
visitors1 = new List
{
new StringVisitor(),
//...
};
visitors2 = new Dictionary<Type, IVisitor>()
{
{String, new StringVisitor()},
//...
}
}
private void AddControl(PropertyInfo p, string name = null, Color? c = null, int count = 0)
{
Control control = null;
//option 1
foreach(var visitor in visitors1)
{
if(visitor.Accept(p, control))
break;
}
//option 20
if(visitors2.ContainsKey(p.PropertyType))
{
visitors2[p.PropertyType].Accept(control);
}
//do your common control stuff here.
}
}
There are other ways as well but that should encapsulate your work some and make it easier to find where a mistake is. Because of the nature of your work on this it won't be super easy to test that everything is visually pleasing, but the code should be encapsulated enough to make it easy to adjust what you need adjusted.
-
\$\begingroup\$ Marking your answer as the right one, since it is the best approach \$\endgroup\$vice_versa– vice_versa2015年09月29日 07:34:43 +00:00Commented Sep 29, 2015 at 7:34