mirror of https://github.com/dahall/Vanara.git
658 lines
25 KiB
C#
658 lines
25 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Vanara.Windows.Forms
|
|
{
|
|
/// <summary>
|
|
/// An input dialog that automatically creates controls to collect the values of the object supplied via the <see cref="Data"/> property.
|
|
/// </summary>
|
|
public class InputDialog : CommonDialog
|
|
{
|
|
private object data;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the data for the input dialog box. The data type will determine the type of input mechanism displayed. For simple
|
|
/// types, a <see cref="TextBox"/> with validation, or a <see cref="CheckBox"/> or a <see cref="ComboBox"/> will be displayed. For
|
|
/// classes and structures, all of the public, top-level, fields and properties will have input mechanisms shown for each. See
|
|
/// Remarks for more detail.
|
|
/// </summary>
|
|
/// <value>The data for the input dialog box.</value>
|
|
/// <remarks>TBD</remarks>
|
|
[DefaultValue(null), Browsable(false), Category("Data"), Description("The data for the input dialog box.")]
|
|
public object Data
|
|
{
|
|
get => data;
|
|
set => data = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the image to display on the top left corner of the dialog. This value can be <c>null</c> to display no image.
|
|
/// </summary>
|
|
/// <value>The image to display on the top left corner of the dialog.</value>
|
|
[DefaultValue(null), Category("Appearance"), Description("The image to display on the top left corner of the dialog.")]
|
|
[Localizable(true)]
|
|
public Image Image { get; set; }
|
|
|
|
/// <summary>Gets or sets the text prompt to display above all input options. This value can be <c>null</c>.</summary>
|
|
/// <value>The text prompt to display above all input options.</value>
|
|
[DefaultValue(null), Category("Appearance"), Description("The text prompt to display above all input options.")]
|
|
[Localizable(true), Bindable(true), Editor(typeof(System.ComponentModel.Design.MultilineStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
|
|
public string Prompt { get; set; }
|
|
|
|
/// <summary>Gets or sets the input dialog box title.</summary>
|
|
/// <value>The input dialog box title. The default value is an empty string ("").</value>
|
|
[DefaultValue(""), Category("Window"), Description("The input dialog box title.")]
|
|
[Localizable(true), Bindable(true)]
|
|
public string Title { get; set; } = "";
|
|
|
|
/// <summary>Displays an input dialog in front of the specified object and with the specified prompt, caption, data, and image.</summary>
|
|
/// <param name="owner">An implementation of <see cref="IWin32Window"/> that will own the modal dialog box.</param>
|
|
/// <param name="prompt">The text prompt to display above all input options. This value can be <c>null</c>.</param>
|
|
/// <param name="caption">The caption for the dialog.</param>
|
|
/// <param name="data">
|
|
/// The data for the input. The data type will determine the type of input mechanism displayed. For simple types, a
|
|
/// <see cref="TextBox"/> with validation, or a <see cref="CheckBox"/> or a <see cref="ComboBox"/> will be displayed. For classes
|
|
/// and structures, all of the public, top-level, fields and properties will have input mechanisms shown for each. See Remarks for
|
|
/// more detail.
|
|
/// </param>
|
|
/// <param name="image">
|
|
/// The image to display on the top left corner of the dialog. This value can be <c>null</c> to display no image.
|
|
/// </param>
|
|
/// <param name="width">The desired width of the <see cref="InternalInputDialog"/>. A value of <c>0</c> indicates a default width.</param>
|
|
/// <returns>
|
|
/// Either <see cref="DialogResult.OK"/> or <see cref="DialogResult.Cancel"/>. On OK, the <paramref name="data"/> parameter will
|
|
/// include the updated values from the <see cref="InternalInputDialog"/>.
|
|
/// </returns>
|
|
/// <remarks></remarks>
|
|
public static DialogResult Show(IWin32Window owner, string prompt, string caption, ref object data, Image image = null, int width = 0)
|
|
{
|
|
using var dlg = new InternalInputDialog(prompt, caption, image, data, width);
|
|
var ret = owner == null ? dlg.ShowDialog() : dlg.ShowDialog(owner);
|
|
if (ret == DialogResult.OK)
|
|
data = dlg.Data;
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>Displays an input dialog with the specified prompt, caption, data, and image.</summary>
|
|
/// <param name="prompt">The text prompt to display above all input options. This value can be <c>null</c>.</param>
|
|
/// <param name="caption">The caption for the dialog.</param>
|
|
/// <param name="data">
|
|
/// The data for the input. The data type will determine the type of input mechanism displayed. For simple types, a
|
|
/// <see cref="TextBox"/> with validation, or a <see cref="CheckBox"/> or a <see cref="ComboBox"/> will be displayed. For classes
|
|
/// and structures, all of the public, top-level, fields and properties will have input mechanisms shown for each. See Remarks for
|
|
/// more detail.
|
|
/// </param>
|
|
/// <param name="image">
|
|
/// The image to display on the top left corner of the dialog. This value can be <c>null</c> to display no image.
|
|
/// </param>
|
|
/// <param name="width">The desired width of the <see cref="InternalInputDialog"/>. A value of <c>0</c> indicates a default width.</param>
|
|
/// <returns>
|
|
/// Either <see cref="DialogResult.OK"/> or <see cref="DialogResult.Cancel"/>. On OK, the <paramref name="data"/> parameter will
|
|
/// include the updated values from the <see cref="InternalInputDialog"/>.
|
|
/// </returns>
|
|
/// <remarks></remarks>
|
|
public static DialogResult Show(string prompt, string caption, ref object data, Image image = null, int width = 0) => Show(null, prompt, caption, ref data, image, width);
|
|
|
|
/// <summary>Resets all properties to their default values.</summary>
|
|
public override void Reset() { }
|
|
|
|
/// <summary>
|
|
/// <para>This API supports the.NET Framework infrastructure and is not intended to be used directly from your code.</para>
|
|
/// <para>Specifies a common dialog box.</para>
|
|
/// </summary>
|
|
/// <param name="hwndOwner">A value that represents the window handle of the owner window for the common dialog box.</param>
|
|
/// <returns><c>true</c> if the data was collected; otherwise, <c>false</c>.</returns>
|
|
protected override bool RunDialog(IntPtr hwndOwner) => Show(NativeWindow.FromHandle(hwndOwner), Prompt, Title, ref data, Image) == DialogResult.OK;
|
|
|
|
/// <summary>Get input based on automatic interpretation of Data object.</summary>
|
|
internal class InternalInputDialog : Form
|
|
{
|
|
private const int prefWidth = 340;
|
|
|
|
private static readonly Dictionary<Type, char[]> keyPressValidChars = new Dictionary<Type, char[]>
|
|
{
|
|
[typeof(byte)] = GetCultureChars(true, false, true),
|
|
[typeof(sbyte)] = GetCultureChars(true, true, true),
|
|
[typeof(short)] = GetCultureChars(true, true, true),
|
|
[typeof(ushort)] = GetCultureChars(true, false, true),
|
|
[typeof(int)] = GetCultureChars(true, true, true),
|
|
[typeof(uint)] = GetCultureChars(true, false, true),
|
|
[typeof(long)] = GetCultureChars(true, true, true),
|
|
[typeof(ulong)] = GetCultureChars(true, false, true),
|
|
[typeof(double)] = GetCultureChars(true, true, true, true, true, true),
|
|
[typeof(float)] = GetCultureChars(true, true, true, true, true, true),
|
|
[typeof(decimal)] = GetCultureChars(true, true, true, true, true),
|
|
[typeof(TimeSpan)] = GetCultureChars(true, true, false, new[] { '-' }),
|
|
[typeof(Guid)] = GetCultureChars(true, false, false, "-{}()".ToCharArray()),
|
|
};
|
|
|
|
private static readonly Size minSize = new Size(193, 104);
|
|
|
|
private static readonly Type[] simpleTypes = { typeof(Enum), typeof(decimal), typeof(DateTime),
|
|
typeof(DateTimeOffset), typeof(string), typeof(TimeSpan), typeof(Guid) };
|
|
|
|
private static readonly Dictionary<Type, Predicate<string>> validations = new Dictionary<Type, Predicate<string>>
|
|
{
|
|
[typeof(byte)] = s => byte.TryParse(s, out var _),
|
|
[typeof(sbyte)] = s => sbyte.TryParse(s, out var _),
|
|
[typeof(short)] = s => short.TryParse(s, out var _),
|
|
[typeof(ushort)] = s => ushort.TryParse(s, out var _),
|
|
[typeof(int)] = s => int.TryParse(s, out var _),
|
|
[typeof(uint)] = s => uint.TryParse(s, out var _),
|
|
[typeof(long)] = s => long.TryParse(s, out var _),
|
|
[typeof(ulong)] = s => ulong.TryParse(s, out var _),
|
|
[typeof(char)] = s => char.TryParse(s, out var _),
|
|
[typeof(double)] = s => double.TryParse(s, out var _),
|
|
[typeof(float)] = s => float.TryParse(s, out var _),
|
|
[typeof(decimal)] = s => decimal.TryParse(s, out var _),
|
|
[typeof(DateTime)] = s => DateTime.TryParse(s, out var _),
|
|
[typeof(TimeSpan)] = s => TimeSpan.TryParse(s, out var _),
|
|
[typeof(Guid)] = s => { try { var n = new Guid(s); return true; } catch { return false; } },
|
|
};
|
|
|
|
private readonly List<MemberInfo> items = new List<MemberInfo>();
|
|
private Panel borderPanel;
|
|
private TableLayoutPanel buttonPanel;
|
|
private Button cancelBtn;
|
|
private IContainer components;
|
|
private object dataObj;
|
|
private ErrorProvider errorProvider;
|
|
private Image image;
|
|
private Button okBtn;
|
|
private string prompt;
|
|
private TableLayoutPanel table;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="InternalInputDialog"/> class.</summary>
|
|
public InternalInputDialog() => InitializeComponent();
|
|
|
|
internal InternalInputDialog(string prompt, string caption, Image image, object data, int width) : this()
|
|
{
|
|
Width = width;
|
|
this.prompt = prompt;
|
|
Text = caption;
|
|
this.image = image;
|
|
Data = data;
|
|
}
|
|
|
|
/// <summary>Gets or sets the data.</summary>
|
|
/// <value>The data.</value>
|
|
[DefaultValue(null), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public object Data
|
|
{
|
|
get => dataObj;
|
|
set
|
|
{
|
|
if (value == null)
|
|
value = string.Empty;
|
|
|
|
items.Clear();
|
|
|
|
if (IsSimpleType(value.GetType()))
|
|
items.Add(null);
|
|
else
|
|
{
|
|
foreach (var mi in value.GetType().GetMembers(BindingFlags.Instance | BindingFlags.Public))
|
|
{
|
|
if (GetAttr(mi)?.Hidden ?? false)
|
|
continue;
|
|
if (mi is FieldInfo fi && IsSupportedType(fi.FieldType))
|
|
{
|
|
items.Add(fi);
|
|
}
|
|
else if (mi is PropertyInfo pi && IsSupportedType(pi.PropertyType) && pi.GetIndexParameters().Length == 0 && pi.CanWrite)
|
|
{
|
|
items.Add(pi);
|
|
}
|
|
}
|
|
|
|
items.Sort((x, y) => (GetAttr(x)?.Order ?? int.MaxValue) - (GetAttr(y)?.Order ?? int.MaxValue));
|
|
}
|
|
|
|
dataObj = value;
|
|
|
|
BuildTable();
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets or sets the image.</summary>
|
|
/// <value>The image.</value>
|
|
[DefaultValue(null)]
|
|
public Image Image
|
|
{
|
|
get => image;
|
|
set
|
|
{
|
|
if (image == value) return;
|
|
image = value;
|
|
BuildTable();
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets or sets the prompt.</summary>
|
|
/// <value>The prompt.</value>
|
|
[DefaultValue(null)]
|
|
public string Prompt
|
|
{
|
|
get => prompt;
|
|
set
|
|
{
|
|
if (prompt == value) return;
|
|
prompt = value;
|
|
BuildTable();
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets or sets the width of the control.</summary>
|
|
public new int Width
|
|
{
|
|
get => base.Width;
|
|
set
|
|
{
|
|
if (value == 0) value = prefWidth;
|
|
value = Math.Max(minSize.Width, value);
|
|
MinimumSize = new Size(value, minSize.Height);
|
|
MaximumSize = new Size(value, int.MaxValue);
|
|
}
|
|
}
|
|
|
|
private bool HasPrompt => !string.IsNullOrEmpty(Prompt);
|
|
|
|
private static object ConvertFromStr(string value, Type destType)
|
|
{
|
|
if (destType == typeof(string))
|
|
return value;
|
|
if (value.Trim() == string.Empty)
|
|
return destType.IsValueType ? Activator.CreateInstance(destType) : null;
|
|
if (typeof(IConvertible).IsAssignableFrom(destType))
|
|
try { return Convert.ChangeType(value, destType); } catch { }
|
|
return TypeDescriptor.GetConverter(destType).ConvertFrom(value);
|
|
}
|
|
|
|
private static string ConvertToStr(object value)
|
|
{
|
|
return value switch
|
|
{
|
|
null => string.Empty,
|
|
|
|
IConvertible _ => value.ToString(), _ => (string)TypeDescriptor.GetConverter(value).ConvertTo(value, typeof(string)),
|
|
};
|
|
}
|
|
|
|
private static int GetBestHeight(Control c)
|
|
{
|
|
using var g = c.CreateGraphics();
|
|
return TextRenderer.MeasureText(g, c.Text, c.Font, new Size(c.Width, 0), TextFormatFlags.WordBreak).Height;
|
|
}
|
|
|
|
private static char[] GetCultureChars(bool digits, bool neg, bool pos, bool dec = false, bool grp = false, bool e = false)
|
|
{
|
|
var c = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
|
|
var l = new List<string>();
|
|
if (digits) l.AddRange(c.NativeDigits);
|
|
if (neg) l.Add(c.NegativeSign);
|
|
if (pos) l.Add(c.PositiveSign);
|
|
if (dec) l.Add(c.NumberDecimalSeparator);
|
|
if (grp) l.Add(c.NumberGroupSeparator);
|
|
if (e) l.Add("Ee");
|
|
var sb = new System.Text.StringBuilder();
|
|
foreach (var s in l)
|
|
sb.Append(s);
|
|
var ca = sb.ToString().ToCharArray();
|
|
Array.Sort(ca);
|
|
return ca;
|
|
}
|
|
|
|
private static char[] GetCultureChars(bool timeChars, bool timeSep, bool dateSep, char[] other)
|
|
{
|
|
var c = System.Globalization.CultureInfo.CurrentCulture;
|
|
var l = new List<string>();
|
|
if (timeChars) l.AddRange(c.NumberFormat.NativeDigits);
|
|
if (timeSep) { l.Add(c.DateTimeFormat.TimeSeparator); l.Add(c.NumberFormat.NumberDecimalSeparator); }
|
|
if (dateSep) l.Add(c.DateTimeFormat.DateSeparator);
|
|
if (other != null && other.Length > 0) l.Add(new string(other));
|
|
var sb = new System.Text.StringBuilder();
|
|
foreach (var s in l)
|
|
sb.Append(s);
|
|
var ca = sb.ToString().ToCharArray();
|
|
Array.Sort(ca);
|
|
return ca;
|
|
}
|
|
|
|
private static bool IsSimpleType(Type type) => type.IsPrimitive || type.IsEnum || simpleTypes.Contains(type) || Convert.GetTypeCode(type) != TypeCode.Object ||
|
|
type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]);
|
|
|
|
private static bool IsSupportedType(Type type)
|
|
{
|
|
if (typeof(IConvertible).IsAssignableFrom(type))
|
|
return true;
|
|
var cvtr = TypeDescriptor.GetConverter(type);
|
|
return cvtr.CanConvertFrom(typeof(string)) && cvtr.CanConvertTo(typeof(string));
|
|
}
|
|
|
|
/// <summary>Binds input text values back to the Data object.</summary>
|
|
private void BindToData()
|
|
{
|
|
for (var i = 0; i < items.Count; i++)
|
|
{
|
|
var item = items[i];
|
|
var itemType = GetItemType(item);
|
|
|
|
// Get value from control
|
|
var c = table.Controls[$"input{i}"];
|
|
var box = c as CheckBox;
|
|
var val = box?.Checked ?? ConvertFromStr(c.Text, itemType);
|
|
|
|
// Apply value to dataObj
|
|
if (item == null)
|
|
dataObj = val;
|
|
else if (item is PropertyInfo)
|
|
((PropertyInfo)item).SetValue(dataObj, val, null);
|
|
else
|
|
((FieldInfo)item).SetValue(dataObj, val);
|
|
}
|
|
}
|
|
|
|
private Control BuildInputForItem(int i)
|
|
{
|
|
var item = items[i];
|
|
var itemType = GetItemType(item);
|
|
|
|
// Get default text value
|
|
object val;
|
|
if (item == null)
|
|
val = dataObj;
|
|
else if (item is PropertyInfo)
|
|
val = ((PropertyInfo)item).GetValue(dataObj, null);
|
|
else
|
|
val = ((FieldInfo)item).GetValue(dataObj);
|
|
var t = ConvertToStr(val);
|
|
|
|
// Build control type
|
|
Control retVal;
|
|
if (itemType == typeof(bool))
|
|
{
|
|
retVal = new CheckBox { AutoSize = true, Checked = (bool)val, Margin = new Padding(0, 7, 0, 0), MinimumSize = new Size(0, 20) };
|
|
}
|
|
else if (itemType.IsEnum)
|
|
{
|
|
var cb = new ComboBox { Dock = DockStyle.Fill, DropDownStyle = ComboBoxStyle.DropDownList };
|
|
cb.Items.AddRange(Enum.GetNames(itemType));
|
|
cb.Text = t;
|
|
retVal = cb;
|
|
}
|
|
else
|
|
{
|
|
var tb = new TextBox { CausesValidation = true, Dock = DockStyle.Fill, Text = t, UseSystemPasswordChar = GetAttr(item)?.UsePasswordChar ?? false };
|
|
tb.Enter += (s, e) => tb.SelectAll();
|
|
if (itemType == typeof(char))
|
|
tb.KeyPress += (s, e) => e.Handled = !char.IsControl(e.KeyChar) && tb.TextLength > 0;
|
|
else
|
|
tb.KeyPress += (s, e) => e.Handled = IsInvalidKey(e.KeyChar, itemType);
|
|
tb.Validating += (s, e) =>
|
|
{
|
|
var invalid = TextIsInvalid(tb, itemType);
|
|
e.Cancel = invalid;
|
|
errorProvider.SetError(tb, invalid ? $"Text must be in a valid format for {itemType.Name}." : "");
|
|
};
|
|
tb.Validated += (s, e) => errorProvider.SetError(tb, "");
|
|
errorProvider.SetIconPadding(tb, -18);
|
|
retVal = tb;
|
|
}
|
|
|
|
// Set standard props
|
|
// TODO: Change out '7' for DPI specific spacing
|
|
retVal.Margin = new Padding(items.Count == 1 && HasPrompt && items[0] == null ? 4 : 0, 7, 0, 0);
|
|
retVal.Name = $"input{i}";
|
|
return retVal;
|
|
}
|
|
|
|
private Label BuildLabelForItem(int i)
|
|
{
|
|
var item = items[i];
|
|
var lbl = new Label { AutoSize = true, Dock = DockStyle.Left, Margin = new Padding(0, 0, 1, 0) };
|
|
if (item != null)
|
|
{
|
|
lbl.Text = (GetAttr(item)?.Label ?? item.Name) + ":";
|
|
// TODO: Change out '10' for spacing needed to align label text with TextBox and '4' for DPI specific spacing
|
|
lbl.Margin = new Padding(0, 10, 4, 0);
|
|
}
|
|
return lbl;
|
|
}
|
|
|
|
private void BuildTable()
|
|
{
|
|
table.SuspendLayout();
|
|
|
|
// Clear out last layout
|
|
table.Controls.Clear();
|
|
while (table.RowStyles.Count > 1)
|
|
table.RowStyles.RemoveAt(1);
|
|
|
|
table.RowCount = items.Count + (HasPrompt ? 1 : 0);
|
|
|
|
// Icon
|
|
if (Image != null)
|
|
{
|
|
table.Controls.Add(new PictureBox { Image = Image, Size = Image.Size, Margin = new Padding(0, 0, 7, 0), TabStop = false }, 0, 0);
|
|
table.SetRowSpan(table.GetControlFromPosition(0, 0), table.RowCount);
|
|
}
|
|
|
|
var hrow = 0;
|
|
|
|
// Add header row if needed
|
|
Label lbl = null;
|
|
if (HasPrompt)
|
|
{
|
|
lbl = new Label
|
|
{
|
|
AutoSize = true,
|
|
Text = Prompt,
|
|
Dock = DockStyle.Top,
|
|
UseMnemonic = false,
|
|
Font = new Font(Font.FontFamily, Font.Size * 4 / 3),
|
|
ForeColor = Color.FromArgb(19, 112, 171),
|
|
Margin = new Padding(items.Count == 1 && items[0] == null ? 1 : 0, 0, 0, 0)
|
|
};
|
|
table.Controls.Add(lbl, 1, hrow++);
|
|
table.SetColumnSpan(lbl, 2);
|
|
}
|
|
|
|
// Build rows for each item
|
|
for (var i = 0; i < items.Count; i++)
|
|
{
|
|
if (i + hrow > 0)
|
|
table.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
table.Controls.Add(BuildLabelForItem(i), 1, i + hrow);
|
|
table.Controls.Add(BuildInputForItem(i), 2, i + hrow);
|
|
}
|
|
|
|
table.ResumeLayout();
|
|
|
|
if (HasPrompt && lbl != null && lbl.PreferredWidth > lbl.Width)
|
|
lbl.MinimumSize = lbl.Size;
|
|
}
|
|
|
|
private void CancelBtn_Click(object sender, EventArgs e) => Close();
|
|
|
|
private InputDialogItemAttribute GetAttr(MemberInfo mi) => mi is null ? null : (InputDialogItemAttribute)Attribute.GetCustomAttribute(mi, typeof(InputDialogItemAttribute), true);
|
|
|
|
private Type GetItemType(MemberInfo mi) => mi == null ? dataObj.GetType() : ((mi as PropertyInfo)?.PropertyType ?? ((FieldInfo)mi).FieldType);
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
components = new Container();
|
|
buttonPanel = new TableLayoutPanel();
|
|
okBtn = new Button();
|
|
cancelBtn = new Button();
|
|
borderPanel = new Panel();
|
|
table = new TableLayoutPanel();
|
|
errorProvider = new ErrorProvider(components);
|
|
buttonPanel.SuspendLayout();
|
|
((ISupportInitialize)errorProvider).BeginInit();
|
|
SuspendLayout();
|
|
// buttonPanel
|
|
buttonPanel.AutoSize = true;
|
|
buttonPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
|
buttonPanel.BackColor = SystemColors.Control;
|
|
buttonPanel.ColumnCount = 3;
|
|
buttonPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
|
|
buttonPanel.ColumnStyles.Add(new ColumnStyle());
|
|
buttonPanel.ColumnStyles.Add(new ColumnStyle());
|
|
buttonPanel.Controls.Add(okBtn, 1, 0);
|
|
buttonPanel.Controls.Add(cancelBtn, 2, 0);
|
|
buttonPanel.Dock = DockStyle.Bottom;
|
|
buttonPanel.Location = new Point(0, 25);
|
|
buttonPanel.Margin = new Padding(0);
|
|
buttonPanel.MinimumSize = new Size(177, 40);
|
|
buttonPanel.Name = "buttonPanel";
|
|
buttonPanel.Padding = new Padding(10, 8, 10, 9);
|
|
buttonPanel.RowCount = 1;
|
|
buttonPanel.RowStyles.Add(new RowStyle());
|
|
buttonPanel.Size = new Size(177, 40);
|
|
buttonPanel.TabIndex = 1;
|
|
// okBtn
|
|
okBtn.Location = new Point(10, 8);
|
|
okBtn.Margin = new Padding(0, 0, 7, 0);
|
|
okBtn.MinimumSize = new Size(75, 23);
|
|
okBtn.Name = "okBtn";
|
|
okBtn.Size = new Size(75, 23);
|
|
okBtn.TabIndex = 1;
|
|
okBtn.Text = "OK";
|
|
okBtn.UseVisualStyleBackColor = true;
|
|
okBtn.Click += new EventHandler(OkBtn_Click);
|
|
// cancelBtn
|
|
cancelBtn.DialogResult = DialogResult.Cancel;
|
|
cancelBtn.Location = new Point(92, 8);
|
|
cancelBtn.Margin = new Padding(0);
|
|
cancelBtn.MinimumSize = new Size(75, 23);
|
|
cancelBtn.Name = "cancelBtn";
|
|
cancelBtn.Size = new Size(75, 23);
|
|
cancelBtn.TabIndex = 2;
|
|
cancelBtn.Text = "&Cancel";
|
|
cancelBtn.UseVisualStyleBackColor = true;
|
|
cancelBtn.Click += new EventHandler(CancelBtn_Click);
|
|
// borderPanel
|
|
borderPanel.BackColor = Color.FromArgb(223, 223, 223);
|
|
borderPanel.Dock = DockStyle.Bottom;
|
|
borderPanel.Location = new Point(0, 24);
|
|
borderPanel.Margin = new Padding(0);
|
|
borderPanel.MinimumSize = new Size(0, 1);
|
|
borderPanel.Name = "borderPanel";
|
|
borderPanel.Size = new Size(177, 1);
|
|
borderPanel.TabIndex = 3;
|
|
// table
|
|
table.AutoSize = true;
|
|
table.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
|
table.ColumnCount = 3;
|
|
table.ColumnStyles.Add(new ColumnStyle());
|
|
table.ColumnStyles.Add(new ColumnStyle());
|
|
table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
|
|
table.Dock = DockStyle.Fill;
|
|
table.Location = new Point(0, 0);
|
|
table.Margin = new Padding(0);
|
|
table.Name = "table";
|
|
table.Padding = new Padding(10);
|
|
table.RowCount = 1;
|
|
table.RowStyles.Add(new RowStyle());
|
|
table.Size = new Size(177, 24);
|
|
table.TabIndex = 0;
|
|
// errorProvider
|
|
errorProvider.ContainerControl = this;
|
|
// InternalInputDialog
|
|
AcceptButton = okBtn;
|
|
AutoSize = true;
|
|
AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
|
BackColor = SystemColors.Window;
|
|
CancelButton = cancelBtn;
|
|
ClientSize = new Size(prefWidth, 65);
|
|
Controls.Add(table);
|
|
Controls.Add(borderPanel);
|
|
Controls.Add(buttonPanel);
|
|
Font = new Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
|
MinimumSize = new Size(prefWidth, minSize.Height);
|
|
MaximumSize = new Size(prefWidth, int.MaxValue);
|
|
Name = "InternalInputDialog";
|
|
StartPosition = FormStartPosition.CenterParent;
|
|
buttonPanel.ResumeLayout(false);
|
|
((ISupportInitialize)errorProvider).EndInit();
|
|
ResumeLayout(false);
|
|
PerformLayout();
|
|
}
|
|
|
|
private bool IsInvalidKey(char keyChar, Type itemType)
|
|
{
|
|
if (char.IsControl(keyChar))
|
|
return false;
|
|
keyPressValidChars.TryGetValue(itemType, out var chars);
|
|
if (chars != null)
|
|
{
|
|
var si = Array.BinarySearch(chars, keyChar);
|
|
System.Diagnostics.Debug.WriteLine($"Processed key {keyChar} as {si} position.");
|
|
if (si < 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void OkBtn_Click(object sender, EventArgs e)
|
|
{
|
|
BindToData();
|
|
DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
|
|
private bool TextIsInvalid(TextBox tb, Type itemType)
|
|
{
|
|
if (string.IsNullOrEmpty(tb.Text))
|
|
return false;
|
|
validations.TryGetValue(itemType, out var p);
|
|
return p != null && !p(tb.Text);
|
|
}
|
|
|
|
private class RegexTextBox : TextBox
|
|
{
|
|
public string RegexPattern { get; set; }
|
|
|
|
protected override void OnKeyPress(KeyPressEventArgs e) =>
|
|
//System.Text.RegularExpressions.Regex.IsMatch()
|
|
base.OnKeyPress(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows a developer to attribute a property or field with text that gets shown instead of the field or property name in an <see cref="InputDialog"/>.
|
|
/// </summary>
|
|
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
|
|
public sealed class InputDialogItemAttribute : Attribute
|
|
{
|
|
/// <summary>Initializes a new instance of the <see cref="InputDialogItemAttribute"/> class.</summary>
|
|
public InputDialogItemAttribute() { }
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="InputDialogItemAttribute"/> class.</summary>
|
|
/// <param name="label">The label to use in the <see cref="InputDialog"/> as the label for this field or property.</param>
|
|
public InputDialogItemAttribute(string label) => Label = label;
|
|
|
|
/// <summary>Gets or sets a value indicating whether this item is hidden and not displayed by the <see cref="InputDialog"/>.</summary>
|
|
/// <value><c>true</c> if hidden; otherwise, <c>false</c>.</value>
|
|
public bool Hidden { get; set; } = false;
|
|
|
|
/// <summary>Gets or sets the label to use in the <see cref="InputDialog"/> as the label for this field or property.</summary>
|
|
/// <value>The label for this item.</value>
|
|
public string Label { get; }
|
|
|
|
/// <summary>Gets or sets the order in which to display the input for this field or property within the <see cref="InputDialog"/>.</summary>
|
|
/// <value>The display order for this item.</value>
|
|
public int Order { get; set; } = int.MaxValue;
|
|
|
|
/// <summary>Gets or sets a value indicating whether text boxes should display text using the system default password character.</summary>
|
|
/// <value><c>true</c> if using a password character; otherwise, <c>false</c>.</value>
|
|
public bool UsePasswordChar { get; set; } = false;
|
|
}
|
|
} |