Search code examples
c#multithreadingwinformssplash-screen

c# communication between splashscreen and mainform into different thread


I've found that example (Communicate between two windows forms in C#) that works fine but not when the two forms are in different thread.

I've got an "System.NullReferenceException" at line 20 in MainForm (this.splashy.LabelText = i;)

The point of this script is to modified the width of the progressBar in the splashscreen when MainForm is doing his job.

Thanks in advance!

Here are the two c# classes and program file:

//Program.cs
using System;
using System.Threading;
using System.Windows.Forms;
namespace GIS
{
    static class Program
    {
        public static SplashScreen splashy = null;
        /// <summary>
        /// Point d'entrée principal de l'application.
       /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Thread splashThread = new Thread(new ThreadStart(
            delegate
            {
                //SplashScreen splashy = new SplashScreen();
                splashy = new SplashScreen();
                Application.Run(splashy);
            }
            ));
            splashThread.SetApartmentState(ApartmentState.STA);
            splashThread.Start();
            //run form - time taking operation
            MainForm mainForm = new MainForm(splashy);
            mainForm.Load += new EventHandler(mainForm_Load);
            Application.Run(mainForm);
        }
        static void mainForm_Load(object sender, EventArgs e)
        {
            //close splash
            if (splashy == null)
            {
                return;
            }
            splashy.Invoke(new Action(splashy.Close));
            splashy.Dispose();
            splashy = null;
        }
    }
}

//SplashScreen.cs
using System;
using System.Windows.Forms;
namespace GIS
{
    public partial class SplashScreen : Form
    {
        public SplashScreen()
        {
            InitializeComponent();
        }
        public int LabelText
        {
            get 
            { 
                return rectangleShape1.Width; 
            }
            set 
            { 
                rectangleShape1.Width = value; 
            }
       }
    }
}

//MainForm.cs
using System.Threading;
using System.Windows.Forms;
namespace GIS
{
    public partial class MainForm : Form
    {
        private SplashScreen splashy = null;
        public MainForm(Form callingForm)
        {
            splashy = callingForm as SplashScreen;
            InitializeComponent();
            doWork();
        }
        private void doWork()
        {
            for(int i = 0; i < 100; i++)
            {
            this.splashy.LabelText = i;
            Thread.Sleep(10);
            }
        }
    }
}

Solution

  • You need to invoke the SplashScreen to change it's value from an other thread like

    this.splashy.Invoke((MethodInvoker) delegate { this.splashy.LabelText = "Requested" + repeats + "Times"; });
    

    Note that the constructor

    splashy = callingForm as SplashScreen;
    

    allows splashy to be null - this causes your current NullReferenceException.

    This problem is locaed in this snipped:

            Thread splashThread = new Thread(new ThreadStart(
            delegate
            {
                splashy = new SplashScreen();
                Application.Run(splashy);
            }
            ));
            splashThread.SetApartmentState(ApartmentState.STA);
            splashThread.Start();
            //run form - time taking operation
            MainForm mainForm = new MainForm(splashy);
    

    The new thread you are start is not fast enought to create the instance of SplashScreen before you pass it. The result - you are passing null.

    Fix it by create the splashy before your thread starts:

            splashy = new SplashScreen();
            Thread splashThread = new Thread(new ThreadStart(
            delegate
            {
                Application.Run(splashy);
            }