So, I'm working on a C# and .NET Windows Forms project using Visual Studios 2022. The project contains three different forms, but for this forum post, we'll only be talking about the first two forms. The first form is called frmHome and is the default form that displays when the code is ran. When the user clicks the "View" button on frmHome, the second form pops up, being frmViewCollection. The goal is to get the string of the highlighted item in the list of frmHome and have it display in the Collection Name text box on frmViewCollection when the "View" button is clicked on frmHome.

Here's my code for frmHome:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProjectAlexKadyn
{

    public partial class frmHome : Form
    {
        public string collection;
        public frmHome()
        {
            InitializeComponent();
        }

        private void btnView_Click(object sender, EventArgs e)
        {
            collection = lstCollections.Text.ToString();
            frmViewCollection frmViewCollection = new frmViewCollection();
            frmViewCollection.collectionName = collection;
            frmViewCollection.Show(this);
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            
            this.Close();

        }

        private void btnDelete_Click(object sender, EventArgs e)
        {

        }
    }
}

Here's my code for frmViewCollection:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProjectAlexKadyn
{
    public partial class frmViewCollection : Form
    {
        public double totalValue;
        public string collectionName;
        public frmViewCollection()
        {
            InitializeComponent();

            txtCollectionName.Text = collectionName; //Currently doesn't display variable value in txtCollectionName
            totalValue = 0.00;
            txtTotalValue.Text = totalValue.ToString("C", CultureInfo.CurrentCulture);

       
                /*
                 * Oh boy it's about to get slightly hairy...
                 * 
                 * Prerequisites for this part of the code:
                 * 
                 * - We need to start doing the 4D array with all the data of each collection that is saved in the current session
                 * - The 4D array will contain these four variables: Collection name, model of each item, short description of each item, each item's value
                 * - Every time a new collection is saved, it saves the data of the collection to the 4D array and adds collectionName to lstCollections in frmHome
                 * - Alex, if you don't know how 4D arrays work, research them real quick and understanding what I'm trying to do 
                 * should be extremely straight forward with your new found knowledge afterwards 
                 * 
                 * Goal:
                 * 
                 * The goal of this block of code is to set the default total value of the collection in txtTotalValue to the actual value of the collection when btnView is 
                 * clicked in frmHome, that's if the collection is not the default "Add New Collection" and was a previously saved collection with a value greater than zero.
                 * 
                 * How the Code Should Work:
                 * 
                 * 1. Matches collectionName of the class to the collectionName of the 4D array
                 * 2. Indexes through each modelIdentifier dimension of the array of the specific collectionName value
                 * 3. As it's indexing through each modelIdentifier under that collectionName, it grabs the itemValue from the itemValue dimension under that modelIdentifier
                 * 4. Sums up the total of every itemValue and sets totalValue of the class to that summed-up total
                 * 5. Converts totalValue to currency as a string and displays it in txtTotalValue
                 * 6. Put the code in this block and it should do it's thing once btnView is clicked in frmHome
                 */
            

        }

        private void btnAddNewItem_Click(object sender, EventArgs e)
        {
            
            

        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnSaveAsCurrent_Click(object sender, EventArgs e)
        {

        }

        private void btnSaveAsNew_Click(object sender, EventArgs e)
        {

        }
    }
}

In frmHome's code, the code that matters is the btnView_Click event handler. In frmViewCollection's code, please ignore that massive comment instructing my partner on the next steps, but the code that matters happens before that comment. This code should work, and it's not throwing me any errors, but the string isn't being displayed in the Collection Name text box.

Here's images of the actual GUIs of each form:

Image of frmHome's GUI Image of frmViewCollection's GUI

3

There are 3 best solutions below

0
Jie Kong On

To pass a string from the first form to a second form in a C# .NET Windows Forms application, you can follow a straightforward approach by creating a public property or method in the second form that you can access from the first form.

Pass the Value from the First Form

// When you want to show Form2 from Form1
Form2 form2 = new Form2();
form2.PassedValue = "The string you want to pass"; // Set the value here
form2.Show();

Create the public property in the Second Form

public partial class Form2 : Form
{
    public string PassedValue { get; set; }

    public Form2()
    {
        InitializeComponent();
    }

    // Use this method to update the UI when the form is shown or loaded
    private void Form2_Load(object sender, EventArgs e)
    {
        // Assuming you have a TextBox called textBox1 in Form2
        textBox1.Text = PassedValue;
    }
}
0
Slash On

You can add a parameter to the constructor of the second form and then call frmViewCollection(this);.

frmHome code:

private void btnView_Click(object sender, EventArgs e)
{
    collection = lstCollections.Text.ToString();
    frmViewCollection frmViewCollection = new frmViewCollection(this);
    frmViewCollection.collectionName = collection;
    frmViewCollection.Show(this);
}

frmViewCollection code:

namespace ProjectAlexKadyn
{
    public partial class frmViewCollection : Form
    {
        private frmHome home = new();
        public frmViewCollection(frmHome home)
        {
            InitializeComponent();
            
            txtCollectionName.Text = collectionName;
        }
    }
}
0
Harald Coppoolse On

Short answer: initialize FrmViewCollection.CollectionName in the eventhandler for Form.Load

Longer answer: in winforms separated the construction of a form from showing the form. This has the advantages that you can set properties after construction before the form is shown. Don't use public properties in the constructor, use them when the form is about to be shown.

Use visual studio designer to add an event handler for form.load in FrmViewCollection, and initialize FrmViewCollection.TxtCollectionName in this event handler:

void OnFormLoading(object sender, ...)
{
    this.TxtCollectionName.Text = this.CollectionName;

    ... // other initializations just before the form is shown.
}

Best answer: If FrmViewCollection.TxtCollectionName should always show the value of FrmViewCollection.CollectionName, then don't make two variables for this.

// obsolete: public string CollectionName;

public string CollectionName
{
    get => this.TxtCollectionName.Text;
    set => this.TxtCollectionName.Text = value;
}

Usage in FrmHome:

// only if FrmHome.Collection should always have the value of lstCollections.Text
string Collection
{
    get => this.lstCollections.Text;
    set => this.lstCollectionis.Text = value;
}

private void btnView_Click(object sender, EventArgs e)
{
    FrmViewCollection frmViewCollection = new frmViewCollection();
    frmViewCollection.CollectionName = this.collection;
    frmViewCollection.Show(this);
}

Modeless vs Modal Dialog Boxes

You decided to show FrmViewCollection as a modeless dialog box. Modeless dialog boxes are way more difficult to handle than a modal dialog box. Are you sure you need to use a modeless dialog box?

If you are not familiar with modeless dialog boxes and modal dialog boxes, consider to read Modeless and Modal dialog boxes

In a modeless dialog box the operator can switch back to the FrmHome while FrmViewCollection is still visible. The operator can even decide to close FrmHome without closing FrmViewCollection.

Are you sure that you need that the operator can switch back to FrmHome without closing FrmViewCollection? If this is not the usage that you intended, if you want that the operator closes FrmViewcollection before continuing to work in FrmHome, consider to show FrmViewCollection as a modal Form using Form.ShowDialog

private void btnView_Click(object sender, EventArgs e)
{
    using (FrmViewCollection frmViewCollection = new frmViewCollection())
    {
        frmViewCollection.CollectionName = this.Collection;
        DialogResult dlgResult = frmViewCollection.Show(this);
        if (dlgResult == DialogResult.Ok)
        {
            // operator pressed Ok, fetch result data from the form and process it
            // for example, suppose operator changes the collection
            // and provides a file name:
            string fileName = frmViewCollection.FileName;
            this.SaveCollection(fileName, frmViewCollection.CollectionName);
        }
        // else: operator pressed cancel, don't do anything.
    }

While FrmViewCollection is visible, the operator can't switch back to FrmHome. He first has to fill in FrmViewCollection and press Ok, or Cancel, or close the form in any other method. FrmHome gets the opportunity to communicate with FrmViewCollection before it is Disposed.

How to properly use a modeless dialog box

If you really need to switch between FrmHome and FrmViewCollection, so if you really need a modeless dialog box, then there are several problems in your code.

  • You forgot to save a reference to FrmViewCollection. The garbage collector will consider it as garbage
  • If the operator clicks FrmHome.btnView again, a second FrmViewCollection is shown. Do you want this?
  • What happens with FrmViewCollection if the operator switches to FrmHome and decides to close FrmHome? Do you always want to lose all information that the operator entered in FrmViewCollection, or do you want to warn the operator, so he can chose to save information or not?

You see that using a modeless dialog box leads to several problems.

First of all, you need to save that you are showing the dialog box:

private FrmViewCollection DisplayedFrmViewCollection {get; set;} = null;

private void BtnView_Click(object sender, EventArgs e)
{
    this.DisplayedFrmViewCollection = new frmViewCollection();
    this.DisplayedFrmViewCollection.CollectionName = this.collection;
    this.DisplayedFrmViewCollection.Show(this);
}

To prevent that the form is shown twice if the operator clicks the button again, consider to disable this button. Of course you'll have to enable the button again if FrmViewCollection is closed. Thas is also the moment that you can set the pointer to null again.

private void BtnView_Click(object sender, EventArgs e)
{
    this.DisplayFormViewCollection();
}

private void DisplayFrmViewCollection
{
    // inform the operator that the button can't be clicked while FrmViewCollection
    // is displayed
    this.btnView.Enabled = false;
    this.DisplayedFrmViewCollection = new frmViewCollection();

    // make sure you get informed if the form has been closed
    this.DisplayedFrmViewCollection.Closed += this.FrmViewCollectionClosed;

    this.DisplayedFrmViewCollection.CollectionName = this.collection;
    this.DisplayedFrmViewCollection.Show(this);
}

void FrmViewCollectionClosed(object sender, ...)
{
    if (Object.ReferenceEquals(sender, this.DisplayedFrmViewCollection))
    {
        // if needed, get result information from FrmViewCollection
        string fileName = this.DisplayedFrmViewCollection.FileName;
        this.SaveCollection(fileName, this.DisplayedFrmViewCollection.CollectionName);

        // don't need the form anymore. Dispose it and set the pointer to null
        this.DisplayedFrmViewCollection.Dispose();
        this.DisplayedFrmViewCollection= null;
        
        // enable the button again
        this.btnView.Enabled = true;
    }
}

Now what do you want if the operator decides to close FrmHome? Usually FrmHome should ask FrmViewCollection to close, so it can warn the operator that it will be closed and that information is lost if it is not saved. If the operator presses cancel, you shouldn't continue closing.

FrmHome should react on event Form.Closing

private void FormClosing(Object sender, FormClosingEventArgs e)
{
    if (this.DisplayedFrmViewCollection != null)
    {
        // FrmViewCollection is being displayed as modeless
        // ask it to close:
        this.DisplayedFrmViewCollection.Close();

This will lead to the Closing event in FrmViewCollection. The handler will check if something has to be saved, and if so, the operator will be warned that information will be lost. The operator can select Ok, or Cancel.

If the operator chooses Cancel, then the closing process stops. If he chooses Ok, then the closing process continues. Event FrmViewCollection.FormClosed is raised, which will lead to FrmViewCollectionClosed described above. this.DisplayedFrmViewCollection will be set to null.

So if after this.DisplayedFrmViewCollection.Close(); the property is null, we know that the form has been closed normally, and that all data has been processed correctly. FrmHome can continue to close. If this.DisplayedFrmViewCollection is not null, then the Closing process has somewhere been cancelled, and this.DisplayedFrmViewCollection still exists. In that case FrmHome cannot be closed.

private void FormClosing(Object sender, FormClosingEventArgs e)
{
    if (this.DisplayedFrmViewCollection != null)
    {
        this.DisplayedFrmViewCollection.Close();

        // cancel the closing process if FrmViewCollection still exists
        e.Cancel = this.DisplayedFrmViewCollection != null;
    }
}

So you see that code is way more difficult if you decide to create a modeless dialog box. My advice would be: only make a dialog box modeless if the operator really needs to be able to switch back to the main form before answering the dialog box.