Technical
Technical

Reputation: 154

C# DataGridView and Input Form

I have a DataGridView binded with data from a database. I need to create a Form, which has input fields for data from a single grid row. The Form has 30+ input controls - TextBoxes, Checkboxes and NumericUpDowns.

For now I went with this approach:

  1. Retrieve current row from the DataGridView and load values from the cells to class instance

  2. Pass the instance to the form and manually fill the input controls

  3. Update the database from the form, update the DataGridView

I want to improve some things:

  1. Is there any way to quickly fill all input controls from a class instance?
  2. Is there any way to determine which input controls have changed their values besides manually subscribing every control on an event handler?
  3. Is there any way to improve this whole thing, e.g. do something more efficiently?

Upvotes: 1

Views: 1958

Answers (2)

Avo Nappo
Avo Nappo

Reputation: 630

Re: value changed indicators. Not sure if there is a really elegant way to do that. Firstly, if I had to, I would change the background (or foreground) color rather than font boldness. Setting font to bold would change the width of the content and that is usually quite annoying. Then, I would add handlers to the TextChanged events (or ValueChange events, for controls that are not text-based). You dont need to write custom handlers to each and every edit control - in the event handler, you get the object sender parameter that points to the control object. Then you can get the field name binded to that control with something like this:

private void stringbox_TextChanged(object sender, EventArgs e)
{
    Control ctrl = (Control)sender;
    string fieldName = ctrl.DataBindings[0].BindingMemberInfo.BindingMember;
    if ((string)view[0].Row[fieldName] != ctrl.Text) ctrl.BackColor = Color.Pink;
}

That way you will only need to add a TextChanged handler once (per edit contol class), not one per every edit box you have.

Upvotes: 0

Avo Nappo
Avo Nappo

Reputation: 630

If you are already passing in a DataRow, then you could instead pass in the DataTable and something that identifies the row in that table. And maybe optionally an adapter, if you want to commit the changes immediately on form exit. Then you can create a DataView of that table. And bind each edit control to a field in that view. Something like this:

public partial class EditForm : Form
{

    DataRow row = null;
    DataView view;
    SqlDataAdapter adapter;

    public EditForm(SqlDataAdapter adapter, DataTable table, int rowId)
    {
        InitializeComponent();

        this.adapter = adapter;

        view = table.DefaultView;
        view.RowFilter = $"ID = {rowId}";
        if (view.Count == 0) throw new Exception("no such row");
        DataRowView dvr = view[0];
        row = dvr.Row;

        datebox.DataBindings.Add(new Binding("Value", view, "DATE"));
        stringbox.DataBindings.Add(new Binding("Text", view, "O_STRING"));

        this.FormClosing += EditForm_FormClosing;
    }

    private void EditForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (row.RowState == DataRowState.Modified) adapter.Update(new DataRow[] { row });
    }
}

The above assuming that your table has key column named ID and fields DATE and O_STRING.

This will save you the trouble of creating an intermediate custom class instance, based on that row, moving values to and from various objects and automatically sets the RowStatae in the original table.

Upvotes: 2

Related Questions