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);
}
}
}
}
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);
}
Related
I'm trying to make a simple clock program with threads. In Form1 I want to create a new thread that's in ThreadProc(), take time, and sent back to Form1 send() method that will replace the text in label.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ThreadProc tP = new ThreadProc();
Thread t = new Thread(tP.threadP);
t.Start();
}
public void send(Object obj)
{
string time;
try
{
time = (string)obj;
}
catch (InvalidCastException)
{
time = " ";
}
if (label1.InvokeRequired)
this.Invoke((MethodInvoker)delegate () { label1.Text = time; });
else
label1.Text = time;
}
}
internal class ThreadProc
{
public void threadP()
{
Form1 form1 = new Form1();
DateTime dateTime;
do
{
dateTime = DateTime.Now;
string time = dateTime.ToString();
Thread t = new Thread(() => form1.send(time));
t.Start();
Thread.Sleep(500);
} while (true);
}
}
It ends up in infinite loop of Form1() -> threadP() -> Form1()... but without
Form1 form1 = new Form1();
I can't call send() in
Thread t = new Thread(() => form1.send(time));
The threadP() has to be in their own class and send() has to be in Form1 class.
Pass a reference to your form to the thread. You can then access it from there.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ThreadProc tP = new ThreadProc();
Thread t = new Thread(tP.threadP);
// Automatically end thread when application ends
t.IsBackground = true;
// Pass reference to this form to thread
t.Start(this);
}
public void Send(string time)
{
if (Visible) // Check form wasn't closed
{
if (label1.InvokeRequired)
this.Invoke((MethodInvoker)delegate () { label1.Text = time; });
else
label1.Text = time;
}
}
}
internal class ThreadProc
{
public void threadP(object data)
{
// Get Form1 reference from start parameter
Form1 form1 = data as Form1;
DateTime dateTime;
do
{
dateTime = DateTime.Now;
string time = dateTime.ToString();
form1.Send(time);
Thread.Sleep(500);
} while (true);
}
}
Note that there are better designs to re-use the thread like passing an interface instead of the concrete Form1 class. But this is just a simple example.
When a Form and a background thread are so closely bound together, you usually use a BackgroundWorker instead of threads.
public static void Monitor0()
{
bool ToMineOrNot = Backend.ToMineOrNot;
while (ToMineOrNot)
{
Form1 temp = new Form1();
Form1.NonStaticDelegate = new Action(temp.setHashRate);
Form1.NonStaticDelegate();
Backend.hps = 0;
Thread.Sleep(1000);
}
if (ToMineOrNot == false)
{
}
}
public void setHashRate()
{
hashrate.Text = Backend.hps.ToString();
}
I have to get the Static Void to call the non Static Void, i have to have Monitor0 Static because it has to be Run in a Thread, and setHashRate() has to be Non-static to edit the label (It's a Windows form):
Thread thread = new Thread(Monitor0);
thread.Start();
Does anybody know how i could Do that? and I cant just do this because the label won't be changed:
Form1 form = new Form();
form.label1.Text = "text"
I'm trying to figure out why my progressbar isn't updating. I'm trying to update from my program class.
I specifically want to update it from my program class, because I'm going to perform a task and feed it an update value.
The for loop is just for testing.
Program.cs
namespace CacheMonitor
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 myform = new Form1();
Application.Run(myform);
for (int i = 0; i < 100; i++)
{
myform.UpdateProgress(i);
}
}
}
}
Form.cs
namespace CacheMonitor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdateProgress(int newValue)
{
progressBar1.Value = newValue;
}
}
}
I have a windows form application which is supposed to show a splash screen with a label field that I want to update as the main form (called welcome.cs) loads in the background. The splash screen shows & hides just fine, but the label doesn't update.
I've done a lot of research but haven't quite found the solution.
Program.cs
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (new SingleGlobalInstance(1000))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen splashscreen = new SplashScreen();
splashscreen.ShowSplashScreen();
Welcome welcome = new Welcome(splashscreen); //Takes some time to load
splashscreen.CloseForm();
Application.Run(welcome);
}
}
Splashscreen.cs
public partial class SplashScreen : Form
{
//Delegate for cross thread call to close
private delegate void CloseDelegate();
private delegate void UpdateStatusDelegate(string status);
private static SplashScreen splashScreen;
private Thread thread = null;
public SplashScreen()
{
InitializeComponent();
}
public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashScreen != null)
return;
thread = new Thread(ShowForm);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
static private void ShowForm()
{
splashScreen = new SplashScreen();
Application.Run(splashScreen);
}
public void CloseForm()
{
splashScreen.Invoke(new CloseDelegate(CloseFormInternal));
}
static private void CloseFormInternal()
{
splashScreen.Close();
}
public void UpdateStatus(string status)
{
splashScreen.Invoke(new UpdateStatusDelegate(UpdateStatusInternal), status);
}
private void UpdateStatusInternal (string status)
{
if (splashScreen != null && splashScreen.IsHandleCreated)
{
lblStatus.Text = status;
}
}
}
Welcome.cs
public Welcome(Splashscreen splashscreen)
{
InitializeComponent();
//Code to log the user into the system
splashScreen.UpdateStatus("Logging in...");
//my expectation is that UpdateStatus call will update the label displayed on the splash screen but it doesn't.
//Do more stuff.....
}
Does it have something to do with multi-threading or is it because im creating a new instance of splashscreen in welcome.cs before calling UpdateStatus? How would I get around this?
You could do the following
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
// Creates the Splash
splash = new FrmSplash();
//Opens the Splash in a new Thread, this way any gifs, progress bars, lablels changes will work because the main thread isnt blocked
var t = Task.Factory.StartNew(() =>
{
splash.ShowDialog();
});
while (!splash.Created) // wait the splash screen form load process
System.Threading.Thread.Sleep(300);
UpdateSplashMessage("Loading the program... Please wait");
// Some slow initialization code.
// ...
//Close splash screen
CloseSplash();
Application.Run(args);
}
static void CloseSplash()
{
splash.Invoke(new MethodInvoker(() =>
{
splash.Close(); // Closes the splash that is running in the other thread
}));
}
static void UpdateSplashMessage(string msg)
{
splash.Invoke(new MethodInvoker(() =>
{
splash.AtualizarMensagem(msg);
}));
}
Note that you will need to create a method called AtualizarMensagem(string str) in your splash screen form, like this
public void AtualizarMensagem(string novaMsg)
{
lblCarregando.Text = novaMsg;
}
I have this code in my "useful snnipets" folder, it always works for me.
Hope this helps.
I have a method that creates a bound with a windows form and also sents to that window form a parameter. I would like to create a thread to that window form. How can I do it?
public Send_to_windowForm(string name_form, string value_for_labellBox)
{
thread t = new thread(new threadStart (form(value_for_labelBox)).Start();
Application.Run(new form(value_for_labelBox));
form.text=name_form;
}
it's not working. I don't think it's correct. How can I do it? And one more question: how can i handleall the form application that the thread opens? I would like to handle all the forms because in a specific moment i want to add a new value to one of the forms opened on the desktop.
THX
You need to do something like this:
namespace UIThreadMarshalling {
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var tt = new ThreadTest();
ThreadStart ts = new ThreadStart(tt.StartUiThread);
Thread t = new Thread(ts);
t.Name = "UI Thread";
t.Start();
Thread.Sleep(new TimeSpan(0, 0, 10));
}
}
public class ThreadTest {
Form _form;
public ThreadTest() {
}
public void StartUiThread()
{
using (Form1 _form = new Form1())
{
Application.Run(_form);
}
}
}
}