I'm modifying existing WinForms project. The project has UserControl.
This UserControl has DataSet variable which is set from another part of the program in different thread.
What I want to do is to dynamically add another controls to this control depending on the DataSet.
So, after DataSet is loaded, I'm calling RefreshChildControl function and trying to add my new ChildUserControls to flowLayoutPanel. And that's where the problems begin:). I get the "Cross-thread operation not valid: Control 'ChildUserControl' accessed from a thread other than the thread it was created on" exception. I tried to use if(this.InvokeRequired) and Invoke this method, but it does not help. InvokeRequired on MyUserControl is false.
So, is there any good way of performing such task? Or am I missing something important?
EDIT:
I tried to skip InvokeRequired test and just call this.FindForm().Invoke on this method. I've got "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." exception. And, by the way, when I open another form with this control everything worked fine.
First. The simplest solution is to perform Invoke everytime. Nothing bad will happen.
Second, use SynchronizationContext.
using System.Threading;
public class YourForm
{
SynchronizationContext sync;
public YourForm()
{
sync = SynchronizationContext.Current;
// Any time you need to update controls, call it like this:
sync.Send(UpdateControls);
}
public void UpdateControls()
{
// Access your controls.
}
}
SynchronizationContext will manage all threading issues for you. It checks, whether you call from the same or from the other thread. If from same it will just immediately execute your code. Otherwise it will do Invoke through form's message loop.
If your user control is not immediately visible after you construct it, the handle will not be created on the thread that you think it is created on. It's not the C# object whose thread parent is important, it is the Windows Handle object whose parent is important.
To force a control to be immediately created on the thread that you thought you created it on, then
read out the control.Handle which will force the control to actually be made and assigned a handle.
MyUserControl uc = new MyUserControl(); // the handle is not created here
uc.Visible = false;
IntPtr dummy = uc.Handle; // The control is immediately given a real handle
You can also try to fiddle around with uc.CreateControl, but this won't create the handle if the control is not visible.
Now you can have another thread update your user control even if the user control is not visible.
uc.BeginInvoke((Action)(() => uc.Text = "ha ha"));
If you leave out the dummy = uc.Handle line, you will get an exception that you can't call BeginInvoke on a control that does not have a handle.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.createcontrol(v=vs.90).aspx
Related
I have a WPF project and from the main window i am creating and loading some bunch of user controls, there is some large data i am loading in background and then updating a built-in control throw the dispatcher, that works fine, the problem is that some of the user controls loads a lot of data, for example the very first thing i load in the main area of my main window, what i want is to put a loading label instead, load the main window as fast as possible so the user see this label and run in background the creation of that user control and when is done add it as a child of my main container area on my main window while i remove the loading label, if i follow the same philosophy i run into the same error like when i run a task and then try to update the window without using the dispatcher. i want to be able of create the user control asynchronous then update the main window.
Code:
User Control:
public partial class CustomUserControlGallery : UserControl
{
public CustomUserControlGallery()
{
InitializeComponent();
}
...
}
On the backend class of the main window:
public partial class MainWindow : Window
{
CustomUserControlGallery _customUserControlGallery;
public MainWindow()
{
InitializeComponent();
Task t = new Task({
//Can't use the _customUserControlGallery's Dispatcher because object is uninitialized and this.Dispatcher not working either.
_customUserControlGallery = new CustomUserControlGallery(); //Error Here.
_gridContainer.Dispatcher.Invoke(new Action(() => _gridContainer.Children.Add(_customUserControlGallery)));
_loadingLabel.Visbility = Visibility.Collapse;
});
t.Start();
}
...
}
I don't know how to handle this situation with the thread associated to the user control and the main thread.
Error:
{"The calling thread must be STA, because many UI components require this."}
You're doing this wrong. All controls must be created & operate on the UI Thread. That said, you can use the BackgroundWorker class to load the data.
You typically do this by disabling the control whose data is being loaded in the background or hiding it & displaying a progress indicator in its place. Then, you start your BackgroundWorker. That can communicate how far along it is using the ReportProgress method. Finally, when it's finished running, the RunWorkerCompleted event is fired, and you use that to either enable the control, or to hide the progress indicator & show the control.
Some quick & dirty (untested) code:
Place this in your Initialize() or control constructor:
private BackgroundWorker loadData = new BackgroundWorker();
loadData.DoWork += loadData_DoWork;
loadData.ProgressChanged += loadData_ProgressChanged; // Only do this if you are going to report progress
loadData.WorkerReportsProgress = true;
loadData.WorkerSupportsCancellation = false; // You can set this to true if you provide a Cancel button
loadData.RunWorkerCompleted += loadData_RunWorkerCompleted;
private void DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker worker = sender as BackgroundWorker;
bool done = false;
while ( !done ) {
// If you want to check for cancellation, include this if statement
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// Your code to load the data goes here.
// If you wish to display progress updates, compute how far along you are and call ReportProgress here.
}
}
private void loadData_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
// You code to report the progress goes here.
}
private void loadData_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Your code to do whatever is necessary to put the UI into the completed state goes here.
}
What you are essentially saying (I think) is that Your app becomes sluggish while your control renders a large amount of data.
This is a problem that needs to be solved via virtualisation. You cannot create a control on a background thread, have it render its data behind the scenes and then pop it into existence. You can create controls on separate dispatchers, but they cannot share the same visual and logical tree, so you will not be able to have one as a child of the other.
Virtualisation is what you need to focus on. Depending on the control you can use a variety of virtualisation settings. Try googleing the subject as there is a lot of information on how to achieve this effectively. Most likely you will want to use things like virtualizing stackpanels and container recycling.
You cannot create UI controls with different Dispatchers and use them with each other. It's just not allowed. What you want to do is on your Task you do the heavy lifting work without UI updates and when it is done you push it back to the Dispatcher to update the UI.
In your case, I wouldn't even use Dispatcher.Invoke. Since you are using Task, it has a TaskScheduler.FromCurrentSynchronizationContext() that you can pass in the constructor.
What is the purpose of instantiating controls in a different thread if you're just going to put it back to the Main dispatcher? It's not expensive to do that.
I have an older form that I really don't want to rewrite at this point, so what I'm doing is loading the form and then adding it to a panel in the new UI form. This is working fine, but it's slow. The old form does a lot of loading and gathering of data and it's not very efficient. So as a result larger records take up to 30 seconds to load. As you know, creating the form then "locks up" the main UI for about 30 seconds while it loads the old form. This is the action I'm trying to prevent. I want to load the new form, display a "Loading" gif in the blank panel, and then once the old form is loaded remove the "Loading" image and add the form as a control.
And here starts the problem.
I've tried creating a Background Worker but this causes a STA error (old form has a few threaded data loadings of it's own), and since I can't change the worker to STA I stopped trying.
I've tried to create an Invoke (and BeginInvoke) and while this works, it doesn't really load the old form in the thread. It simply sends it back to the UI thread and does the work there. Again this hangs the UI. I.E.: Not what I want.
I've tried to create a delegate and trigger it as an event in the thread, but I get the same results as below...
I've created a thread, set STA on it, started it and then did a while loop with a DoEvents waiting on it to finish. Of course this all seems to work up to the point of accually adding the form to the panel, and then I get the "Control 'ChartForm' accesses from a thread other than the thread it was created on". In this error 'ChartForm' is the old chart that was loaded in the thread.
I've tried the above method, but I instead used a private static field to hold the creating of the old form, and then adding it to the panel once the thread is completed. This is in the method that created the thread, just after the while loop. Same error.
So, I've used the above method in other places with DataTables and didn't have any issue getting the data back to the main thread for use with DataBinding. I know that this is a little different but I didn't think that it would be this hard to do.
Below is the code that I have tried to use that seems to be the closest to what I want.
private static _ChartForm;
private void LoadPatientChart()
{
ClearMainPanel(); // Removes any loaded ChartForms from Panel
if (_Patient == null) // Test to make sure a patient is loaded
return;
loadingPanel.Visible = true; // Displays the "Loading" gif
Thread thread = new Thread(new ThreadStart(this.GetChartForm));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (thread.ThreadState != ThreadState.Stopped)
Application.DoEvents(); // Keeps the UI active and waits for the form to load
this.ChartPanel.Controls.Add(_ChartForm); // This is where the error is
loadingPanel.Visible = false; // Hide the "Loading" gif
}
private void GetChartForm()
{
ChartForm chartForm = new ChartForm(_Patient.AcctNum.ToString(), false);
chartForm.TopLevel = false;
chartForm.FormBorderStyle = FormBorderStyle.None;
chartForm.Dock = DockStyle.Fill;
chartForm.Visible = true;
_ChartForm = chartForm;
}
It's really not a good idea to create UI controls on any other thread than the UI thread. It is technically possible, but it's difficult to manage, especially if the new thread is a "temporary" one.
What you really need to do is refactor out the work that the ChartForm is doing (on construction it appears?) and do that work on a background thread, and then return it to your UI thread and then create your ChartForm passing in the results of that work. IMHO this is a better design anyways; although it may be a lot of work for you.
I don't think what you want is possible without refactoring this "old form". There is only one UI thread, and all UI elements must be created on that thread to be displayed to the user.
I would suggest refactoring the form to display initially without any data (or maybe with a loading image), and then have the form start a background task using BackgroundWorker to perform the long running tasks that are not UI related (going to a database, etc.) Once the worker is complete, then you can run the code that initializes the Form's data elements. This will keep the UI responsive for as long as possible while the blocking tasks are performed.
I've tried to create an Invoke (and BeginInvoke) and while this works,
it doesn't really load the old form in the thread. It simply sends it
back to the UI thread and does the work there. Again this hangs the
UI. I.E.: Not what I want.
You must update the user interface on the main thread, you do not have any choice, if its still hanging then your doing the calculations in the wrong thread.
I have an windows form application written in C# in which I use a FileSystemWatcher to monitor a folder for new files and then perform some processing on them. My application is designed to run in the system tray and therefore does not instantiate any forms at startup. The problem is that the Created event is firing on a separate thread and when I try to create an instance of a form in the Created event I get an ThreadStateException that states I need to be running in SingleThreadApartment. I think I need to set the FileSystemWatcher.SynchronizingObject property but don't know what to use since there is no main form in my application.
You will have to call Application.Run() in your Main() method to get the Windows Forms synchronziation machinery in place so that FileSystemWatcher can properly marshal the call to the main thread. The problem you'll have then is that the main form will become visible. Fix that by pasting this code into the class:
protected override void SetVisibleCore(bool value) {
if (!this.IsHandleCreated) {
this.CreateHandle();
value = false;
}
base.SetVisibleCore(value);
}
There are no restrictions on what your form looks like if you do this.
You don’t need to pass any forms to Application.Run at all. Then you can save having to mess around with form visibility. Just do this:
var InvokerForm = new Form();
var dummy = InvokerForm.Handle; // force handle creation
Application.Run();
Just one gotcha there - the form handle creation needs to be forced by accessing it once.
The simplest way to do this is to make a hidden form and pass it to Application.Run.
You can then set the SynchronizingObject property to the hidden form.
To make sure it's a hidden form, set the ControlBox and ShowInTaskbar properties to false.
Now unfortunately due to the fact that WinCE Usb Device Arrival / Removal exposes itself via WindowsMessages I have to ensure that a certain (non-UI) component is not created on a background thread. I would like to assert this via an exception but am lacking the code for the assertion.
This component creates a MessageWindow* and uses it to receive usb arrived/removed messages. The issue is if someone creates this component on a background thread (not necessarily; IsBackground = true) when the thread exits the window will be destroyed.
Any ideas?
*as an aside I still don't know why Form doesn't inherit from this class
Update
I think my version 1 wasn't very clear. So this is v2.
When you create a MessageWindow or a Form for that matter on a thread, when that thread exits the Window/Form is destroyed.
My component is creating a "hidden" message window to intercept some important events, ergo I do not wish for it to be destroyed. Therefore I must somehow ensure that the code that creates the form is running on the "Main UI" thread.
If possible i'd like to avoid passing down a reference to the "main" form to this component as it is (architecturally speaking) supposed to be miles away from the UI.
Update
Moving logging question to a separate Q.
Ok, I understand that you don't want for your component to "know" about the main window -- makes sense.
How about this: How about if you make sure that you always instance your component on the main thread? You component will create it's listener window on the constructor's thread.
If you do that, then you just need to make sure that you call the constructor from the main thread. I'm making some assumptions about your code, but I'm guessing that you must have some class in your architecture that knows about both the UI and your component. Create your component there, using a callback, and the main form's InvokeRequired/Invoke methods.
In forms, you use the InvokeRequired property.
Why not create the non-UI component on a background thread and when you go to update any UI component just look to see if invokeRequired then get back on the main thread to actually do the update.
You should have nothing really tying up the main event thread, IMO.
You can use it in this way:
void MyCallback()
{
if (form1.InvokeRequired) { // form1 is any existing gui control
form1.Invoke(new Action<>(MyCallBack));
return;
}
// your logic here
}
Hey there: I had an idea about your problem. This is just a random thought, and I don't know for sure whether it will work (I have not tested, nor even compiled this -- it just hit me):
What if you get the window handle of the main window of your app, then build a Control around it (I'm assuming that you have a gdi-based app, like Winforms)?
this code might not compile, but it's close (it would go into your component -- note that it would make your component require a gdi windows/winform app, as opposed to a console or WPF app).
If you do try it, I'd love to hear whether it worked for you.
using System.Diagnostics;
using System.Windows.Forms;
void Init()
{
// get handle to the main window
intPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
Control mainWindow = Control.FromHandle(mainWindowHandle);
if(mainWindow.InvokeRequired)
mainWindow.Invoke(SetupMessageWindow);
else
SetupMessageWindow();
}
void SetupMessageWindow()
{
// do your thing...
}
Short version: I want to trigger the Form_Load() event without making the form visible. This doesn't work because Show() ignores the current value of the Visible property:
tasksForm.Visible = false;
tasksForm.Show();
Long version: I have a WinForms application with two forms: main and tasks. The main form is always displayed. The user can either click a button to open the tasks form, or click some buttons that just run a task directly without opening the tasks form.
When a user asks to run a task directly, I'd like to just call some public methods on the tasks form without showing it. Unfortunately, the task logic depends on stuff that happens in the Form_Load() event. The only way I can find to trigger Form_Load() is to call Show(). The best I've been able to do is to show the form in the minimized state:
tasksForm.WindowState = FormWindowState.Minimized;
tasksForm.Show();
I suppose the cleanest solution would be to pull the tasks logic out of the tasks form and into a controller class. Then I can use that class from the main form and from the tasks form, and only load the tasks form when I need it visible for the user. However, if it's an easy thing to load the form without displaying it, that would be a smaller change.
Perhaps it should be noted here that you can cause the form's window to be created without showing the form. I think there could be legitimate situations for wanting to do this.
Anyway, good design or not, you can do that like this:
MyForm f = new MyForm();
IntPtr dummy = f.Handle; // forces the form Control to be created
I don't think this will cause Form_Load() to be called, but you will be able to call f.Invoke() at this point (which is what I was trying to do when I stumbled upon this SO question).
It sounds to me like you need to sit down and re-think your approach here. I cannot imagine a single reason your public methods need to be in a form if you are not going to show it. Just make a new class.
I totally agree with Rich B, you need to look at where you are placing your application logic rather than trying to cludge the WinForms mechanisms. All of those operations and data that your Tasks form is exposing should really be in a separate class say some kind of Application Controller or something held by your main form and then used by your tasks form to read and display data when needed but doesn't need a form to be instantiated to exist.
It probably seems a pain to rework it, but you'll be improving the structure of the app and making it more maintainable etc.
From MSDN:
Form.Load
Occurs before a form is displayed for the first time.
Meaning the only thing that would cause the form to load, is when it is displayed.
Form.Show(); and Form.Visible = true; are the exact same thing. Basically, behind the scenes, Show checks for various conditions, then sets Visible to true. So obviously, setting visible to false (which it already is) before showing the form is meaningless.
But let's forget the technicalities. I completely agree with Rich B and Shaun Austin - the logic shouldn't be in that form anyway.
Sometimes this would be useful without it being bad design. Sometimes it could be the start of a migration from native to managed.
If you were migrating a c++ app to .NET for example, you may simply make yourwhole app a child window of the .NET form or panel, and gradually migrate over to the .NET by getting rid of your c++ app menu, status bar, toolbar and mapping teh .NEt ones to your app using platform invoke etc...
Your C++ app may take a while to load, but the .NET form doesn't..in which you may like to hide the .NEt form until your c++ app has initialised itself.
I'd set opacity=0 and visible=false to false after calling show, then when your c++ app loads, then reverse.
If you make the method public, then you could access it directly.... however, there could be some unexpected side effects when you call it. But making it public and calling it directly will not draw the screen or open the form.
Move mandatory initialization code for the form class out of the Load event handler into the constructor. For a Form class, instantiation of an instance (via the constructor), form loading and form visibility are three different things, and don't need to happen at the same time (although they do obviously need to happen in that order).
None of the answers solved the original question, so, add the below, call .Show() to load the form without showing it, then call .ShowForm() to allow it to be visible if you want to after:
private volatile bool _formVisible;
protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(_formVisible);
}
public void ShowForm()
{
_formVisible = true;
if (InvokeRequired)
{
Invoke((Action) Show);
}
else
{
Show();
}
}