C# - binding textbox to a cell in grid rows in WinForms App

I have an input panel with several textboxes and a DBGrid. This is connected to binding source to 2 joined tables (a parent - child one-to-many on a RID field in both tables) This was originally developed to view entered records, but now the client wants to be able to edit all the data in the joined tables, including the RID fields. The first concern is if the RID gets changed in the textbox, the rows have to have the RID column changed after the textbox edit. The RID field is hidden in the grid.

Secondly, they also want the ability to add new records in this form as well. At this point, upon an add new row, the value of the RID textbox needs to be inserted into the hidden RID field in the grid for the first row, and each consecutive row added (there can be any number of rows) So basically, OnAddNew in the grid needs to set Column RID.Value = TextBoxRID.value. Now to add a twist, lets say the user enters the new RID in the textbox, then enters several rows in the Grid, and goes back and changes the Textbox RID, there needs to be a routine that will update the RID column fields in the Grid when the textbox change has been completed. I don't want to check/change on every keystroke in the textbox, but maybe on a lostfocus?

Keep in mind that these fields (textbox & grid) are bound to a dataset via a BindingSource and aTableAdapter.

Any help would be appreciated.
IntelOneAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

louisfrCommented:
Is the RID something else than a Record ID?
Is there a valid reason to want to change it?
0
it_saigeDeveloperCommented:
You could implement a dynamic form that binds the input controls to the associated databounditem's properties; e.g. -

Form1.cs -
using System;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Windows.Forms;

namespace EE_Q29078537
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void OnLoad(object sender, EventArgs e)
        {
            Globals.Technicians = new BindingList<Person>
            {
                new Person { ID = 1, FirstName = "--- Unassigned ---" },
                new Person { ID = 2, FirstName = "Peter" },
                new Person { ID = 3, FirstName = "Paul" },
                new Person { ID = 4, FirstName = "Mary" }
            };

            Globals.Tasks = new BindingList<Task>
            {
                new Task { ID = 1, Name = "Sweep", AssignedTo = Globals.Technicians[0] },
                new Task { ID = 2, Name = "Mop", AssignedTo = Globals.Technicians[0] },
                new Task { ID = 3, Name = "Wash", AssignedTo = Globals.Technicians[0] },
                new Task { ID = 4, Name = "Dry", AssignedTo = Globals.Technicians[0] }
            };

            bindingSource1.DataSource = Globals.Tasks;
            dataGridView1.DataSource = bindingSource1;
        }

        private void OnClick(object sender, EventArgs e)
        {
            if (sender is ToolStripMenuItem)
            {
                var mnu = sender as ToolStripMenuItem;
                if (mnu.Equals(viewDetailToolStripMenuItem))
                {
                    var task = dataGridView1?.SelectedRows?.Cast<DataGridViewRow>()?.First()?.DataBoundItem as Task;
                    var editor = new DynamicForm<Task>("Task", task);
                    editor.ShowDialog();
                    dataGridView1.Refresh();
                }
            }
        }

        private void OnCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (sender is DataGridView)
            {
                var grid = sender as DataGridView;
                if (grid.Columns[e.ColumnIndex].Name.Equals(nameof(Task.AssignedOn)) || grid.Columns[e.ColumnIndex].Name.Equals(nameof(Task.CompletedOn)))
                {
                    var date = default(DateTime);
                    e.Value = DateTime.TryParse(e.Value.ToString(), out date) && !date.Equals(DateTime.MinValue) ? date.ToShortDateString() : "Not Set";
                    e.FormattingApplied = true;
                }
                else if (grid.Columns[e.ColumnIndex].Name.Equals(nameof(Task.Status)))
                {
                    var status = (e.Value is Enum) ? (Status)e.Value : Status.None;
                    e.Value = status.GetEnumDescription();
                    e.FormattingApplied = true;
                }
            }
        }

        private void OnRowContextMenuStripNeeded(object sender, DataGridViewRowContextMenuStripNeededEventArgs e)
        {
            if (sender is DataGridView)
            {
                var grid = sender as DataGridView;
                if (!grid.SelectedRows.Contains(grid.Rows[e.RowIndex]))
                    grid.Rows[e.RowIndex].Selected = true;
                e.ContextMenuStrip = contextMenuStrip1;
            }
        }
    }
}

Open in new window

Form1.Designer.cs -
namespace EE_Q29078537
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            this.bindingSource1 = new System.Windows.Forms.BindingSource(this.components);
            this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.viewDetailToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).BeginInit();
            this.contextMenuStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // dataGridView1
            // 
            this.dataGridView1.AllowUserToAddRows = false;
            this.dataGridView1.AllowUserToDeleteRows = false;
            this.dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
            this.dataGridView1.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.dataGridView1.Location = new System.Drawing.Point(0, 0);
            this.dataGridView1.MultiSelect = false;
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
            this.dataGridView1.Size = new System.Drawing.Size(516, 223);
            this.dataGridView1.TabIndex = 0;
            this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.OnCellFormatting);
            this.dataGridView1.RowContextMenuStripNeeded += new System.Windows.Forms.DataGridViewRowContextMenuStripNeededEventHandler(this.OnRowContextMenuStripNeeded);
            // 
            // contextMenuStrip1
            // 
            this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.viewDetailToolStripMenuItem});
            this.contextMenuStrip1.Name = "contextMenuStrip1";
            this.contextMenuStrip1.Size = new System.Drawing.Size(133, 26);
            // 
            // viewDetailToolStripMenuItem
            // 
            this.viewDetailToolStripMenuItem.Name = "viewDetailToolStripMenuItem";
            this.viewDetailToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.viewDetailToolStripMenuItem.Text = "View Detail";
            this.viewDetailToolStripMenuItem.Click += new System.EventHandler(this.OnClick);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(516, 223);
            this.Controls.Add(this.dataGridView1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.OnLoad);
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).EndInit();
            this.contextMenuStrip1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.DataGridView dataGridView1;
        private System.Windows.Forms.BindingSource bindingSource1;
        private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
        private System.Windows.Forms.ToolStripMenuItem viewDetailToolStripMenuItem;
    }
}

Open in new window

DynamicForm.cs -
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;

namespace EE_Q29078537
{
    // Derived from a dynamic form implementation authored by James Allen - https://codeoverload.wordpress.com/2010/12/24/dynamic-forms-in-c/
    public partial class DynamicForm<T> : Form
    {
        List<Control> controls;
        public T DataItem { get; private set; }

        public DynamicForm(string title, T item)
        {
            InitializeComponent();
            InitializeForm(title);
            InitializeControls(item);
            DataItem = item;

            // We need to ensure that the required fields are clearly marked, so that
            // the user can see which controls they must complete in order for the OK
            // button to become enabled.
            ValidateControls();
        }

        private void InitializeForm(string title)
        {
            Text = title;
            MaximizeBox = false;
            MinimizeBox = false;
            ShowIcon = false;
            ShowInTaskbar = false;
            FormBorderStyle = FormBorderStyle.FixedDialog;
            StartPosition = FormStartPosition.CenterScreen;
            errorProvider.BlinkStyle = ErrorBlinkStyle.NeverBlink;
            controls = new List<Control>();
        }

        private void InitializeControls(T item)
        {
            PropertyInfo[] properties = item.GetType().GetProperties();

            // Create layout table
            tableLayoutPanel1.RowCount = properties.Length;

            // For each property
            int rowNumber = 0;
            foreach (var property in properties)
            {
                Control ctrl = ControlFactory<T>.CreateControl(item, property);
                if (ctrl != null)
                {
                    // Get custom attributes
                    object[] attributes = property.GetCustomAttributes(true);
                    ctrl = ApplyAttributes(ctrl, attributes);

                    // Disable the control if property read only
                    if (!property.CanWrite)
                        ctrl.Enabled = false;

                    // Set the tab index
                    //ctrl.TabIndex = controls.Count + 1;

                    // Build label
                    if (ctrl.Visible)
                    {
                        ControlTag tag = (ControlTag)ctrl.Tag;
                        Label label = ControlFactory<T>.CreateLabel(property.Name);
                        if (!string.IsNullOrEmpty(tag.CustomLabel))
                            label.Text = tag.CustomLabel;
                        tableLayoutPanel1.Controls.Add(label, 0, rowNumber);
                        tableLayoutPanel1.Controls.Add(ctrl, 1, rowNumber);
                        controls.Add(ctrl);
                    }
                }
                rowNumber++;
            }

            // Resize the form
            this.Width = tableLayoutPanel1.Width + 40;
            this.Height = tableLayoutPanel1.Height + 90;
        }

        /// <summary>
        /// Applies the settings from the custom attributes to the control.
        /// </summary>
        /// <param name="ctrl">A control bound to property</param>
        /// <param name="attributes">Custom attributes for the property</param>
        /// <returns></returns>
        private Control ApplyAttributes(Control ctrl, object[] attributes)
        {
            ControlTag tag = (ControlTag)ctrl.Tag;
            NumericSettingsAttribute attrRange = null;
            DisplaySettingsAttribute attrDisplay = null;
            RequiredFieldAttribute attrRequired = null;
            foreach (object attribute in attributes)
            {
                if (attribute is NumericSettingsAttribute)
                    attrRange = (NumericSettingsAttribute)attribute;
                else if (attribute is DisplaySettingsAttribute)
                    attrDisplay = (DisplaySettingsAttribute)attribute;
                else if (attribute is RequiredFieldAttribute)
                    attrRequired = (RequiredFieldAttribute)attribute;
            }

            // Attach LostFocus handler for input validation
            ctrl.LostFocus += OnLostFocus;

            // Range Attribute
            if (attrRange != null)
            {
                // todo
            }

            // Display Attribute
            if (attrDisplay != null)
            {
                tag.CustomLabel = attrDisplay.Label;
                ctrl.Enabled = !attrDisplay.ReadOnly;
                ctrl.Visible = attrDisplay.Visible;
                if (attrDisplay.Width > 0)
                    ctrl.Width = attrDisplay.Width;
            }

            // Required Field Attribute
            if (attrRequired != null)
            {
                if (string.IsNullOrEmpty(attrRequired.Message))
                    tag.ErrorMessage = "Required";
                else
                    tag.ErrorMessage = attrRequired.Message;
            }
            return ctrl;
        }

        private void OnLostFocus(object sender, EventArgs e)
        {
            // TODO: It would be better to validate just this control and update the
            // OK button accordingly, instead of validating every control on the
            // form.
            ValidateControls();
        }

        private bool ValidateControl(Control control)
        {
            bool isValid = true;

            // Validation currently limited to TextBoxes only
            TextBox txt = control as TextBox;
            if (txt != null)
            {
                // If the textbox is empty, show a warning
                ControlTag tag = (ControlTag)txt.Tag;
                if (tag.IsRequired && string.IsNullOrEmpty(txt.Text))
                {
                    errorProvider.SetError(txt, tag.ErrorMessage);
                    isValid = false;
                }
                else
                    errorProvider.SetError(txt, string.Empty);
            }
            return isValid;
        }

        /// <summary>
        /// Returns false if any controls are invalid.
        /// </summary>
        /// <returns></returns>
        private void ValidateControls()
        {
            bool isValid = true;
            foreach (Control control in controls)
            {
                if (!ValidateControl(control))
                    isValid = false;
            }
            btnOk.Enabled = isValid;
        }
    }
}

Open in new window

DynamicForm.Designer.cs -
namespace EE_Q29078537
{
    partial class DynamicForm<T>
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.btnCancel = new System.Windows.Forms.Button();
            this.btnOk = new System.Windows.Forms.Button();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
            ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
            this.SuspendLayout();
            // 
            // btnCancel
            // 
            this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.btnCancel.Location = new System.Drawing.Point(101, 45);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(75, 23);
            this.btnCancel.TabIndex = 3;
            this.btnCancel.Text = "Cancel";
            this.btnCancel.UseVisualStyleBackColor = true;
            // 
            // btnOk
            // 
            this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.btnOk.Location = new System.Drawing.Point(20, 45);
            this.btnOk.Name = "btnOk";
            this.btnOk.Size = new System.Drawing.Size(75, 23);
            this.btnOk.TabIndex = 2;
            this.btnOk.Text = "OK";
            this.btnOk.UseVisualStyleBackColor = true;
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.AutoSize = true;
            this.tableLayoutPanel1.ColumnCount = 2;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 1;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
            this.tableLayoutPanel1.Size = new System.Drawing.Size(164, 20);
            this.tableLayoutPanel1.TabIndex = 4;
            // 
            // errorProvider
            // 
            this.errorProvider.ContainerControl = this;
            // 
            // DynamicForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(188, 80);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnOk);
            this.Name = "DynamicForm";
            this.Text = "DynamicForm";
            ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button btnCancel;
        private System.Windows.Forms.Button btnOk;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private System.Windows.Forms.ErrorProvider errorProvider;
    }
}

Open in new window

SupportingObjects.cs -
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EE_Q29078537
{
    internal class ControlFactory<T>
    {
        internal static Control CreateControl(T item, PropertyInfo property)
        {
            Control ctrl = null;
            Type type = property.PropertyType;

            // The control depends on the property type
            if (type == typeof(string))
            {
                ctrl = new TextBox();
                TextBox textbox = ctrl as TextBox;
                textbox.Name = $"tbx{property.Name}";
                textbox.Text = (string)property.GetValue(item, null);
                textbox.Margin = new Padding(3, 3, 16, 0);
            }
            else if (type == typeof(char))
            {
                ctrl = new TextBox();
                TextBox textbox = ctrl as TextBox;
                textbox.MaxLength = 1;
                textbox.Name = $"tbx{property.Name}";
                textbox.Width = 20;
                textbox.Text = Convert.ToString(property.GetValue(item, null));
                textbox.Margin = new Padding(3, 3, 16, 0);
            }
            else if (type == typeof(int))
            {
                ctrl = new NumericUpDown();
                NumericUpDown numeric = ctrl as NumericUpDown;
                numeric.Name = $"nud{property.Name}";
                numeric.Value = Convert.ToDecimal(property.GetValue(item, null));
            }
            else if (type == typeof(decimal))
            {
                ctrl = new NumericUpDown();
                NumericUpDown numeric = ctrl as NumericUpDown;
                numeric.DecimalPlaces = 2;
                numeric.Name = $"nud{property.Name}";
                numeric.Value = Convert.ToDecimal(property.GetValue(item, null));
            }
            else if (type == typeof(bool))
            {
                ctrl = new CheckBox();
                CheckBox checkbox = ctrl as CheckBox;
                checkbox.Name = $"cbx{property.Name}";
                checkbox.Checked = Convert.ToBoolean(property.GetValue(item, null));
            }
            else if (type.BaseType == typeof(Enum))
            {
                ctrl = new ComboBox();
                ComboBox dropdown = ctrl as ComboBox;
                dropdown.DropDownStyle = ComboBoxStyle.DropDownList;
                dropdown.Name = $"ddl{property.Name}";
                var values = Enum.GetValues(type);
                var ds = new List<dynamic>();
                foreach (var value in Enum.GetValues(type))
                    ds.Add(new { Display = ((Enum)value).GetEnumDescription(), Value = value });
                dropdown.DataSource = ds;
                dropdown.DisplayMember = "Display";
                dropdown.ValueMember = "Value";
            }
            else if (type == typeof(Person))
            {
                ctrl = new ComboBox();
                ComboBox dropdown = ctrl as ComboBox;
                dropdown.DropDownStyle = ComboBoxStyle.DropDownList;
                dropdown.Name = $"ddl{property.Name}";
                dropdown.DataSource = (from technician in Globals.Technicians select new { Display = technician.ToString(), Value = technician }).ToList();
                dropdown.DisplayMember = "Display";
                dropdown.ValueMember = "Value";
                if (property.GetValue(item, null) != null)
                    dropdown.SelectedValue = (property.GetValue(item, null) as Person);
            }
            else if (type == typeof(DateTime))
            {
                ctrl = new DateTimePicker();
                DateTimePicker date = ctrl as DateTimePicker;
                DateTime dateValue = Convert.ToDateTime(property.GetValue(item, null));
                date.Name = $"dtp{property.Name}";
                if (dateValue < date.MinDate)
                    dateValue = date.MinDate;
                if (dateValue > date.MaxDate)
                    dateValue = date.MaxDate;
                date.Value = dateValue;
            }
            if (ctrl != null)
            {
                if (ctrl is ComboBox)
                    ctrl.DataBindings.Add(new Binding("SelectedValue", item, property.Name, true, DataSourceUpdateMode.OnPropertyChanged));
                else
                    ctrl.DataBindings.Add(new Binding("Text", item, property.Name, true, DataSourceUpdateMode.OnPropertyChanged));

                ControlTag tag = new ControlTag();
                tag.PropertyName = property.Name;
                tag.PropertyType = property.PropertyType;
                ctrl.Tag = tag;
            }
            return ctrl;
        }

        /// <summary>
        /// Creates a new instance of the Label control using the specified text value.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        internal static Label CreateLabel(string text)
        {
            Label label = new Label();
            label.Name = string.Format("lbl{0}", text);
            label.Text = GetLabel(text) + ":";
            label.AutoSize = true;
            label.Margin = new Padding(3, 6, 6, 0);
            return label;
        }

        /// <summary>
        /// Returns a friendly label from the supplied name. For example, the
        /// string "firstName" would be returned as "First Name".
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private static string GetLabel(string text)
        {
            bool isFirst = true;
            StringBuilder sb = new StringBuilder();
            foreach (char c in text.ToCharArray())
            {
                if (isFirst)
                {
                    sb.Append(Char.ToUpper(c));
                    isFirst = false;
                }
                else
                {
                    if (Char.IsUpper(c))
                        sb.Append(' ');
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
    }

    internal class ControlTag
    {
        public string CustomLabel { get; set; }
        public string ErrorMessage { get; set; }
        public string PropertyName { get; set; }
        public Type PropertyType { get; set; }

        public bool IsRequired
        {
            get { return !string.IsNullOrEmpty(ErrorMessage); }
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class DisplaySettingsAttribute : Attribute
    {
        public string Label { get; set; }
        public bool ReadOnly { get; set; }
        public bool Visible { get; set; }
        public int Width { get; set; }

        public DisplaySettingsAttribute()
        {
            Visible = true;
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class NumericSettingsAttribute : Attribute
    {
        public float MinValue { get; set; }
        public float MaxValue { get; set; }
        public int DecimalPlaces { get; set; }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class RequiredFieldAttribute : Attribute
    {
        public string Message { get; set; }
    }

    static class Globals
    {
        public static BindingList<Person> Technicians { get; set; }
        public static BindingList<Task> Tasks { get; set; }

        static Globals()
        {
            Technicians = new BindingList<Person>();
            Tasks = new BindingList<Task>();
        }
    }

    static class Extensions
    {
        public static R GetAttributeValue<T, R>(this Enum source, Func<T, R> expression) where T : Attribute
        {
            T attribute =
                source
                    .GetType()
                    .GetMember(source.ToString())
                    .Where(member => member.MemberType == MemberTypes.Field)
                    .FirstOrDefault()
                    .GetCustomAttributes(typeof(T), false)
                    .Cast<T>()
                    .SingleOrDefault();
            return (attribute == null) ? default(R) : expression(attribute);
        }

        public static string GetEnumDescription(this Enum value)
        {
            var field = value.GetType().GetField(value.ToString());
            var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
            return (attributes.Length > 0) ? attributes.First().Description : value.ToString();
        }
    }

    class Task
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Person AssignedTo { get; set; }
        public DateTime AssignedOn { get; set; }
        public DateTime CompletedOn { get; set; }
        public Status Status { get; set; }
    }

    enum Status : int
    {
        [Description("None")] None = 0,
        [Description("Unassigned")] Unassigned,
        [Description("Assigned")] Assigned,
        [Description("In Progress")] InProgress,
        [Description("In Research")] InResearch,
        [Description("Completed")] Completed
    }

    enum JobRole : int
    {
        Manager,
        Engineer
    }

    class Person
    {
        public int ID { get; set; }

        [DisplaySettings(Width = 200)]
        public string FirstName { get; set; }

        [DisplaySettings(Width = 200)]
        public string LastName { get; set; }

        public DateTime DateOfBirth { get; set; }
        public JobRole Role { get; set; }
        public override string ToString()
        {
            return $"{FirstName}{(!string.IsNullOrWhiteSpace(LastName) ? $" {LastName}" : string.Empty)}";
        }
    }
}

Open in new window

Produces the following output -
Initial load -Capture.PNGRight-clicking a row -Capture.PNGShowing the detail form (dynamically generated) -Capture.PNGAs you update the fields in the form, the associated row cells are also updated -Capture.PNG
One part that I did not implement here is the cancellation portion.  Ideally, you would have a copy of your data that is passed in, if the user cancels the changes, then all you need to do is overwrite the dataitem with the copy of the data.

-saige-
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
IntelOneAuthor Commented:
I should have clarified, the RID is the RunID (alphanumeric) which is not the record ID.
0
louisfrCommented:
Does that RunID have a meaning? Does changing it makes sense?
I'm asking because sometimes the right answer to the user demands is: why would you want to do that?
0
IntelOneAuthor Commented:
I will work through this code and see how i can adapt to this project. Thanks so much for all your time on this, you went way over what I expected. Appreciate it! :-)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.