In windows form on click on Next I need to display other form and start some processing. Am coding in .Net C#
The problem is before the form is complete visible, the method gets triggered and the processing starts and the UI looks like it crashed. Processing started even before controls are loaded. and once processing is done all controls are visible.
The actual output must be all controls should be loaded and then processing must start.
I need to call the method to start processing after the form (user control) is visible and is loaded completely.
I tried this and this, but no luck.
Added code:
private void FeatureRemovalControl_Load(object sender, EventArgs e)
{
pictureBox2.Image = Properties.Resources.line;
prgbar.Value = 0;
//code to load images and some other stuff
StratProcess();
}
You're calling StartProcess (which seems to block until it's finished) from your UI thread. Because WinForms repaints occur on that thread, nothing is painted, and it appears that your process has hung. You should look at using a BackgroundWorker, or other way to call StartProcess asynchronously.
Best way, if you ask me, would be to start processing asynchronously, so that you maintain full control of the UI and process at the same time.
http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.71).aspx
Try calling that method at the end of the FormLoad event, the control should have finished loading by then. If it hasn't that you may need perform some checks and possibly create a custom event that fires when you're happy that it is ready.
Another solution is to have a button that the user must press to trigger the processing, which they will only be able to click once everything has loaded
EDIT: The reason it look's like it's happening is because you're starting the process in one of the control's load method, which I assume is not the last control to load, so it's starts processing before the other controls are given a chance to load. Make StratProcess method public and call it in the FormLoad method of the parent form instead, like so:
private void ParentForm_Load(object sender, EventArgs e)
{
FeatureRemovalControl.StratProcess(); // Should it be called StartProcess instead?
}
Beware though this is still doing the processing on the UI thread, so the screen may appear to 'hang' whilst this is happening so I advise you move it to a background thread as others have suggested.
Related
I have a FileSystemWatcher that is waiting for files to appear in a folder, which then triggers an insert to an ObservableCollection. When the form is open on the screen the update happens successfully. But when the form is minimized I get a cross-thread exception? I know I can just check for context, but I want to know why this behaviour is happening so I can ensure it doesn't come up again in other places.
I've tried this with a new blank winform app, and it errors 100% of the time whether it is minimised or not, so I'm unsure what I'm doing wrong or how to diagnose the issue. The main app is thousands of lines long, with many custom libraries so I can't post all of it here.
Called by the watcher
private void CheckForFiles(object source, FileSystemEventArgs e)
{
WaitingFiles.Add(e.FullPath);
}
Which triggers this
private async void WaitingFiles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CurrentProcessStatusLabel.Text = "Checking download for despatch file";
}
When I minimize the window I run the following code;
Hide();
SysTrayIcon.Visible = true;
Unless a SynchronizingObject is set, events are raised on a background thread, and background threads are not allowed to update the UI. I'm not sure why it works unless the application is minimized, but a good start is to ensure you are using correct synchronization to avoid issues like this.
If you want to update the UI you should either set the SynchronizingObject to one of the controls in your UI, or manually schedule any UI updates to the UI thread.
Understanding that I can use the Forms.Shown event to be notified when a form fully is shown for the very first time, I found no way yet to get something similar later in the form's lifetime.
I.e. consider adding/removing controls or resizing the form.
I thought about registering for the Application.Idle event and somehow connect this to my form but I'm unsure whether this is the right way to go.
In addition I thought about subclassing the LayoutEngine class to gain access to some events/overrides. Again this doesn't look the right way to go for me.
So my question is:
When programmatically adding/removing/resizing a control when a form is already visible, how can I detect when the form is fully finished layouting and drawing again?
I.e. consider adding/removing controls or resizing the form
No, you never want to do this when the user can see the window. There are two basic places where you want to write code like this before the user can see you monkeying with the window, actually seeing it changing size, moving around and generally flickering madly because of multiple Paint events.
First and foremost is your class constructor. That's where you initialize the object in .NET, adding or removing controls belongs there.
Secondary is the Load event. An event that's generally very poorly understood and gets used way to often in Winforms. Induced by it being the default event for the Form class, an historical accident that goes back to early versions of Visual Basic where it was the event to use to initialize the form. That is no longer appropriate, .NET class objects should be initialized in their constructor.
Load, or rather OnLoad(), is appropriate when your code needs to know the Location or Size of the window. Everything that needs to happen to the native window is done by then. It has a valid Handle property, Windows picked a Location, it got rescaled if necessary to accommodate the machine's DPI setting, its Size property is now valid, reflecting scaling and any user preferences, layout was performed so every control is in its expected place. The only thing that didn't happen yet is that the Paint event wasn't fired yet. So the window is not yet visible to the user. That happens very soon after Load completes.
So sizing or moving the window is typically appropriate in OnLoad, given that you now know the real size and location. Do beware DPI rescaling, never hard-code a size.
If you need code run some fixed time after the window is displayed then a Timer is appropriate.
I'm taking a guess here, but the form's OnPaint event might do the trick. You can just override the base OnPaint handler, call the parent function, and put any logic you have after that.
You could use separate events call for each of the events that call a common function
I.E. ResizeEnd for resizing, ControlAdded and ControlRemoved for the controls.
private void Form1_ResizeEnd(object sender, EventArgs e)
{
DoWork()
}
private void Form1_ControlAdded(object sender, ControlEventArgs e)
{
DoWork()
}
private void Form1_ControlRemoved(object sender, ControlEventArgs e)
{
DoWork()
}
private void DoWork()
{
//What needs to be done for each of them
}
Im not sure about the timing of these calls though.
On a button press I call a background worker to change the colour of some text, and in the DoWork method it creates a new object and executes one of its methods. Here is the code:
private void StartProcessButton_Click(object sender, EventArgs e)
{
FirstTimeBackgroundWorker.RunWorkerAsync(GenKeyLabel.ForeColor = Color.DodgerBlue);
}
And the DoWork method...
private void FirstTimeBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
KeyFile k = new KeyFile();
k.CreateDummyFile(DLetter);
}
[The CreateDummyFile effectively does a bunch of file processing, such as copying,deleting and creating files (up to 4.0MB). Throughout I call the ReportProgress method and change some GUI elements on the form, however as it does it in one chunk I cant see the constant progress or the GUI elements change]
Now it does what its supposed to inside the CreateDummyFile method, however it executes it as if it wasn't in another thread (like when you press a button to do something and the form would freeze, and then just show the final result). Any ideas why this is? What I am doing wrong?
There is a lot of code in that class that gets executed so I can't just place it inside the DoWork method.
EDIT: Removed all of the ReportProgress's and it still only changes one of my GUI elements. (All I do is change the font.forecolour. Apart from this I am changing the text on a status strip...
You need to add the FirstTimeBackgroundWorker_DoWork as a handler of DoWork event on BackgroundWorker:
FirstTimeBackgroundWorker.DoWork += FirstTimeBackgroundWorker_DoWork;
Also make sure if you're calling back the UI to update it to do so on the UI thread, not from the background one.
Hi
I'm opening a form like this from my main form when the user makes a selection of a menu item.
private void commToolStripMenuItem_Click(object sender, EventArgs e)
{
Command_Form Command_Form1 = new Command_Form();
Command_Form1.ShowDialog();
// Command_Form1.Dispose(); this didn't help
}
Inside the form "Command_Form1"
I close it like this when the user clicks on the close button
private void Close_button_Click(object sender, EventArgs e)
{
this.Close(); //I get the exception here
}
This process works fine once but on the second closing of the form
(Which I hope is a completely different/new instance of the form) I get the error in the title of this post.
This is the output in the debug window.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
All the topics that list this error go on about not trying to do anything to a form that has not been displayed but this happens when I click on a button in the form.
It would seem to me that pretty much ensures the form has been displayed if I'm able to click its button.
The other posts I've found that list this type of error go on about making thread safe calls so I tried this as an experiment but it didn't make any difference.
private void Close_button_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
CloseCallback d = new CloseCallback(Close_button_Click);
Invoke(d, new object[] { sender, e });
}
else
{
this.Close();
I have multiple threads in my application but they are created by the controls I'm using not by me explicitly.
I am passing data from a serial port to/from the form by Marshling the received/sent data via a delegate[s].
It makes sense that the serial port would run on a different thread than the form but why would a button click on a form be in a diffrent thread than the form????
The whole thread thing is very confuzing
How do I figure out what threads originated where and what is going on in the threads that I didn't create explicitly?
Why would I need to invoke the form's close method via a delegate?
Heck is there anything I can do in this multi threading environment that is thread safe How in do I know if what I'm doing is unsafe/safe if I don't know what/where/why/who/when is creating threads?
My guess is your close() call is not throwing that exception, but something that happens after close(). Have you stepped into the code with the debugger to see when it is fired?
As to when you need to invoke...There is only one thread allowed to make changes and access dynamic properties on the GUI, call it the GUI thread. The GUI thread is responsible for updating layout, firing events like buttons, etc. If you ever access the GUI from another thread (like a timer event) you need to use invoke() to queue your function to be run on the GUI thread. BeginInvoke will also queue the function but is asynchronous (will only queue the function to be run on GUI thread, but will not wait for it to finish).
Close_button_click will only be called by your gui thread when the button click event fires(unless you explicitly call it somewhere else in your code behind, not recommended!), so invokeRequired=false in your code above, and the invoke code is never executed.
I'm running a data import, using a Windows form to kick off the import and show progress.
I've got this whole thing so nice and user friendly, with major and minor progress bars and everything... but just one problem... the form refresh keeps going AWOL.
I have a call to Form.Refresh() every time I update my labels/progress bars, and it usually starts off working. But if ever I need to break into debug mode, just to hand-hold the import a bit, the Refresh() call stops working, and sometimes even if I'm running without Debug mode, at some unpredictable point the same thing happens: the labels and progress bars do not get updated, and if you hide the form and reopen it, the form does not repaint at all - it just shows up entirely white.
Why, oh why, does Form.Refresh() stop working, and how can I fix this?
It sounds as if the import runs on the UI thread, which means that this thread is blocked, preventing the form from repainting itself. A better approach would be to use a BackgroundWorker component, do the import in the DoWork event handler and use the ProgressChanged to update the UI.
Example:
private void StartImport()
{
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.RunWorkerAsync();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// do some work simulating a lenghy process which occasionally
// reports progress with data back to the caller
for (int i = 0; i < 100; i++)
{
Thread.Sleep(200);
backgroundWorker.ReportProgress(i, "Item No " + i.ToString());
}
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox.Items.Add(e.UserState.ToString());
}
Using this approach you will typically not need to call Refresh to force a repaint of the form.
You may want to change your code into using BeginUpdate and EndUpdate, like so:
Control.BeginUpdate();
// Do something to the control, e.g. add items or whatnot
Control.EndUpdate();
This way Refresh shouldn't be necessary.
AFAIK constantly calling Refresh is really a hack and should be avoiding, as it stresses the CPU quite a bit (it has to refresh everything instead of just the things which are changed).
Edit: If the form starts being white, it seems the drawing code is not been called at all, which indicates it's somewhat not responding.
I'd check the code for anything that can deadlock or otherwisely hang.
You could use observer pattern..in short if anything changes in model observer pattern will make sure that change is visible on form..
google it for some examples..
Depending on what .NET framework you're using, you can use the Task.Run approach:
private void btnShowProgress_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = i; }));
}
});
}
Task.Run info
Using invoke with controls
The solution may not be the best practice but it definitely works for small applications.
In Form1 create a bool to check to see if the form is closed.
public bool formclosed = false
Then in Form2 on the Form Closing Event Handler add
formclosed = true
also in the Form2 after
InitializeComponent();
add
formclosed = false;
In Form1 create a timer.
In the timer1.Tick event handler say
private void timer1_Tick(object sender, EventArgs e)
{
if(formclosed == true)
{
Application.Restart();
}
}
This will restart the application and refresh everything ... I also had my text saved to the Properties.Settings.Default so everytime the application started the default settings would show.
I created an initial version of a Progress control using a BackgroundWorker. The Progress control computed and displayed nice things like Estimated Duration, Estimated Time to Completion. Each statistic was displayed by custom control based on a Label control. The Progress control worked in my production code.
Then I made some changes to the Progress control. I switched from basing my custom statistics controls from Label to Panel. I successfully ran all my tests (using NUnit). Then, I created a Test Windows Forms app. The Progress control successfully worked in the Test Windows Forms app.
When I ran my production Windows app with the updated Progress control it didn't display the statistics.
I tried Thread.Sleep(N), Form.Refresh(). None of those worked to update the statistics.
I eventually called Control.Invalidate() on each Statistic control. The Invalidate caused OnPaint to be called and the control happily updated the display with its specific statistic.
For Each Stat_Obj As Control in Statistics_Controls
Stat_Obj.Invalidate()
Next
You might need to give the window time to redraw itself. I understand you're doing the import in a loop and the loop is running on the main UI thread? Try adding this line to the loop:
Application.DoEvents();