简体   繁体   中英

How can I transfer text from a DataGridView in one Form to a private TextBox in another Form?

[Summary]
I have an Invoice form with several private TextBoxes in my program. When the user changes the text of those TextBoxes, the search Form appears.
I would like to transfer some values from my DataGridView (which is linked to my database) in the search Form to the TextBoxes in the Invoice Form (when I press Enter for example).

[Description]
In my Invoice form I have merchandise code TextBox and merchandise name TextBox. When the user changes the text of those TextBoxes, the search Form appears and the entered text will be transferred to the TextBox of searchform .
The searchform only has a TextBox and a DataGridView. When something is entered in the TextBox of the search Form, the DataGridView will search and show the results. The DataGridView has Columns that match the Invoice Form's TextBoxes.

Now I want to manage to do this: when I enter on searchform , the information of the current row of the DataGridView is transferred to the corresponding TextBoxes in the Invoice Form.
merchandise code Column to merchandise code TextBox and merchandise name Column to merchandise name TextBox.

I could illustrate this in the following code:
(I know how to get the values of selected row in DataGridView my question is just the title...)

if (e.KeyCode == Keys.Enter)
{
    SqlCommand sqlcmd = new SqlCommand("SELECT ID FROM X WHERE ID=" +
                        dataGridView1.CurrentRow.Cells[0].Value + "", sqlcon);
    SqlDataReader sqldr = sqlcmd.ExecuteReader();
    while (sqldr.Read())
    {
        InvoiceForm.CodeTextBox = sqldr[codecolumn].Tostring
        InvoiceForm.NameTextBox = sqldr[Namecolumn].Tostring
        InvoiceForm.BlahTextBox = sqldr[Blahcolumn].Tostring                               
    }
}

I have tried the above code but its says:

codeTextBox is private... not able to do so because of protection level...

Let's assume I don't want to change the protection level of those TextBoxes to public.

Without implementing an Interface, there are two simple methods to reference an existing Form class from another class.

Passing the reference of the caller class ( this ) in the constructor of the callee:

var f2 = new Form2(this);
f2.Show();

Using the Owner property of the callee ( Form2 ). The Owner is set using the Show(Owner) or ShowDialog(Owner) methods. this is the instance of the caller:

var f2 = new Form2();
f2.Show(this);

You could also have a public Property in the callee ( Form2 ), used to set the current caller ( this ):

var f2 = new Form2();
f2.MyCaller = this;
f2.Show();

Pretty much useless since the two former methods already achieve the same result using the standard features. There are other means, of course, but pretty much overkill in this context.

Here, I'm using the Owner property to access the instance of the Form class that instantiated your Search Form class. The example uses a public method of the caller class (your InvoiceForm ), which the callee (your Search Form) uses to pass back the values a user selected.

Using Form.Show(this) also implies that the Form shown will be parented (not to be confused with the Parent property, though) with the Form that showed it and will stay on top of it.
You could also use the ShowDialog(this) method, if it's preferable in your case. The Form will be shown as modal dialog in this case.

I'm making two examples using this public method:

  1. A public method with a class parameter, which contains all the values that can be set in the InvoiceForm controls. This is probably the preferred method to pass these values, because it can be more easily extended and re-used in different contexts.
  2. A public method with string parameters, corresponding to the TextBoxes values to set

Public method with a class parameter :
Note that this.Owner is InvoiceForm frm is used to identify the current Owner .
The UpdateMyControls class is the container used to transfer specific values. The SearchForm could act differently if the Owner was a different one.
This is somewhat simplified, but you can use this selection to re-use the SearchForm with different callers, having different results for each Owner .

Note : The class used to transfer the values/references, could be passed in the contructor of SearchForm , possibly using a well-known contract (an Interface), which defines the values and their types. Too broad to describe here, but you should consider exploring this possibility.

public partial class InvoiceForm : Form
{
    public class UpdateMyControls 
    {
        public string CodeText { get; set; }
        public string NameText { get; set; }
        public string BlahText { get; set; }
    }

    private void btnSearch_Click(object sender, EventArgs e)
    {
        var searcher = new SearchForm();
        searcher.Show(this);
    }

    public void UpdateControls(UpdateMyControls allValues)
    {
        this.CodeTextBox.Text = allValues.CodeText;
        this.NameTextBox.Text = allValues.NameText;
        this.BlahTextBox.Text = allValues.BlahText;
    }
}

public partial class SearchForm : Form
{
    private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter) {
            if (this.Owner is InvoiceForm frm) {
                InvoiceForm.UpdateMyControls updateClass = new InvoiceForm.UpdateMyControls();

                updateClass.CodeText = sqldr[codecolumn].ToString();
                updateClass.NameText = sqldr[Namecolumn].ToString();
                updateClass.BlahText = sqldr[Blahcolumn].ToString();
                frm.UpdateControls(updateClass);
                this.Close();
            }
        }
    }
}

Public method with multiple parameters :

public partial class InvoiceForm : Form
{
    private void btnSearch_Click(object sender, EventArgs e)
    {
        var searcher = new SearchForm();
        searcher.Show(this);
    }

    public void UpdateControls(string Code, string Name, string Blah)
    {
        this.CodeTextBox.Text = Code;
        this.NameTextBox.Text = Name;
        this.BlahTextBox.Text = Blah;
    }
}

public partial class SearchForm : Form
{
    private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter) {
            string CodeValue = sqldr[codecolumn].ToString()
            string NameValue = sqldr[Namecolumn].Tostring
            string BlahValue = sqldr[Blahcolumn].Tostring 

            if (this.Owner is InvoiceForm frm) {
                frm.UpdateControls(CodeValue, NameValue, BlahValue);
                this.Close();
            }
        }
    }
}

Effectively, you should avoid direct access to properties of a control in a form as it make caller code directly dependant on the UI of the form. For example, if you want to change the text box for a combo box or maybe a third party text box, you might have to make change at multiple locations.

In simple cases when the property are essentially independant, I would generally add properties to the class that would hide implementation details from the caller.

public string Code
{
    get { return CodeTextBox.Text; }
    set { CodeTextBox.Text = value; }
}

If the controls are related, then using a function might be preferable

public void SetData(string code, string name, string blah)
{
    CodeTextBox.Text = code;
    NameTextBox.Text = name;
    BlahTextBox.Text = blah;
}

If you do that, you might keep the properties but make then read-only so that the function is used when desired.

But if you reload the data from the database, then you might consider moving the code to the InvoiceForm and have a function similar to:

void LoadFromDatabase(int itemId)
{
    var sqlcmd = new SqlCommand("SELECT ID FROM X WHERE ID=" +
                        itemId.ToString(), sqlcon);
    var sqldr = sqlcmd.ExecuteReader();
    if (sqldr.Read())
    {
        InvoiceForm.CodeTextBox = sqldr[codecolumn].Tostring();
        InvoiceForm.NameTextBox = sqldr[Namecolumn].Tostring();
        InvoiceForm.BlahTextBox = sqldr[Blahcolumn].Tostring();                               
    }
}

By the way, notice that I have replace the while loop with an if condition. Obviously the query should return a single item as otherwise it would mean that the database contains duplicate values. If the data is bogus, why prefer last returned item to first one?

I have also added the missing (); after ToString calls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM