Introduction
Have you ever needed to collect simple information in an application that needs more than MessageBox? Developers can spend hours coding different forms for each data instance. This solution, InputDialog, will take a single, simple data type, or a more complex class, and display a form that can collect the information for the user and return an updated data object.
Each data type's input is restricted to valid values.
You can use both simple and more complex data types, as show by these samples:
Using the code
Simple Method Call
To just get a string (like in the small sample above) code:
string s = "";
if (InputDialog.Show("What is your name?", "", ref s) == DialogResult.OK)
{
}
Collecting more items can be done by passing in a class or structure. For example to get the larger sample above:
public class A
{
public enum AGender { Female, Male };
public string Name { get; set; }
public int Age { get; set; } = 25;
public bool Married { get; set; }
public AGender Gender { get; set; }
}
public void CollectA()
{
A a = new A();
InputDialog.Show("Please provide some basic information:", "Personal Info", ref a,
SystemIcons.Information.ToBitmap());
}
Component
If you're more of a visual coder, you can add this InputDialog.cs file into your project and then you should see an InputDialog item in your toolbox.
Adding this component to your form will place an item in the component tray:
You can then look over at the Properties panel and make changes there:
Calling this from your form would then be as simple as:
int data = 100;
inputDialog1.Data = data;
if (inputDialog1.ShowDialog(this) == DialogResult.OK)
data = (int)inputDialog1.Data;
And you get:
Control display using Attributes
If using a class or structure, you can even control the label and display order using the InputDialogItem attribute:
public class A
{
public enum AGender { Female, Male };
[InputDialogItem(Hidden = true)]
public bool unshownSetting = true;
[InputDialogItem("Full name", Order = 0)]
public string Name { get; set; }
[InputDialogItem(Order = 1)]
public int Age { get; set; } = 25;
public bool Married { get; set; }
[InputDialogItem(Order = 2)]
public AGender Gender { get; set; }
}
All public fields and properties will be shown until they have the InputDialogItem.Hidden property set to 'true'.
The order is somewhat arbitrary as reflection is used to gather those values. To specify the order, set the InputDialogItem.Order property. Any ordered items will proceed unordered items.
By default, the label for each item is the name of the field or property. To change it, use the first parameter of the InputDialogItem attribute and set it to the value you desire (like the Name property above).
The class above will produce this dialog:
Support for multiple types
All of the following types are supported as members of a class or structure or as stand-alone objects:
Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, Single, Enum, Decimal, DateTime, String, TimeSpan and Guid
Each will show an error if the user tries to add an invalid string and each prevents typing unsupported characters.
Lessons Learned
Using Reflection
To accomplish this input dialog, I'm of course using reflection. I determine the type of the object passed in and then use the various methods and properties of the Type class to decide how to build out the UI.
Since many of the types are actually structures or primitive types, I needed to pass in the value as a reference so I can change its value upon a successful return.
The nasty science of TableLayoutPanel
I wish I could say I spent most of my time coding. No. I spent most of my time debugging the strange machinations of the TableLayoutPanel. What did I learn?
- The Margin of the control inside the cell is very important. If the Margin is all zeros, then the control is treated differently than if there isn't a control. If the Margin has a value, the entire row or column is affected.
- Setting MinimumSize and MaximumSize are oftent the only ways to ensure the size of a cell.
- Getting a label to break rows is hard. Internally, it has the right answers for sizing, but fails to apply them.
- Set AutoSize = true and Dock = DockStyle.Top
- After label is added to the table, set its MinimumSize = Size if its Width < PreferredWidth
Cool event handling shortcuts
Ever need to handle an event but too lazy to build out the handler? Try this:
TextBox tb = new TextBox();
tb.Enter += (s, e) => tb.SelectAll()
This will cause the text in a TextBox to be selected whenever the control is entered.
History
11 Nov 2015 - Initial posting