Reputation: 438
I'm creating an application where it starts a FormSelect() asking the user to select a item in the list, when user press the "Select" button it will create a new instance of MainForm(), hide the FormSelect and show the MainForm, but it's not working, I'm getting an exception with all tests I made.
Here are some codes:
static class Program {
public static MainForm mainForm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormSelect());
}
}
Nothing special on my FormSelect, so here is the button click where it start the MainForm:
private async void btnSelect_Click(object sender, EventArgs e) {
loadingSpiner.Visible = true;
btnSelect.Enabled = false;
int index = listProcess.SelectedIndex;
await startMainForm(index);
this.Hide();
Program.mainForm.Show();
}
private async Task startMainForm(int index) {
await Task.Run(() => {
Program.mainForm = new MainForm(runningProcess[index]);
});
}
As you can see above, I'm using a Task to start the MainForm so it does not freeze my UI and my "loading spiner"
But when it try to use the .Show() I get a cross-thread exception, so I tried to Invoke that action using:
this.Invoke(new MethodInvoker(delegate () { Program.mainForm.Show(); }));
But using the method above I get an exception saying:
An exception of type 'System.ComponentModel.Win32Exception' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Error creating window handle.
If I remove the "await Task.Run()..." in the startMainForm method everything works fine, but it blocks totally the FormSelect UI.
How can I avoid this problem? Thank you!
Upvotes: 1
Views: 1708
Reputation: 70652
You can't do it that way.
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to say for sure what the best solution for your scenario is. But UI objects must be created and used only in the UI thread. You can't create your MainForm
instance in a worker thread.
Because your code example is incomplete, it's not clear why you are trying to create the MainForm
instance using Task.Run()
in the first place. All that code does is call a constructor, and constructors should not be time-consuming operations anyway.
If you have some time-consuming initialization to perform, you should abstract that out of the MainForm
class, putting it into some other class that can be passed to MainForm
once it's been fully initialized. Then you can initialize that other class with e.g. a call to Task.Run()
(but still not in a constructor…create the object and then if you need to asynchronously call a method that initializes it, do that).
E.g.:
private async void btnSelect_Click(object sender, EventArgs e) {
loadingSpiner.Visible = true;
btnSelect.Enabled = false;
int index = listProcess.SelectedIndex;
MainFormData data = await startMainForm(index);
Program.mainForm = new MainForm(data);
this.Hide();
Program.mainForm.Show();
}
private async Task<MainFormData> startMainForm(int index) {
await Task.Run(() => {
MainFormData data = new MainFormData();
data.Initialize(runningProcess[index]);
return data;
});
}
class MainFormData
{
public MainFormData() { ... }
// You don't say what "runningProcess[index]" is, so placeholder here...
public void Initialize(object o) { ... }
}
Something like that. Adjust to suit your needs, of course.
Of course, that other class must initialize only non-UI aspects. I.e. the data underlying your UI. Since UI objects generally aren't time-consuming to create, I assume that's feasible in your case. If you feel it's the initialization of the UI itself that is time-consuming, then you should be asking for help figuring out what that is the case and fixing that problem. Of course, again without a good MCVE here, there's nothing I can comment on specifically with respect to that.
Upvotes: 1