I have a WPF application that calls a dll when user clicks on a button. This dll does very lengthy operations and while it does that, I cannot interact with the MainWindow, e.g scroll down the Datagrid logging some update messages, switch tabs etc.
How can I keep the MainWindow activated while the dll is running ? I thought about a BackgroundWorker that constantly calls the .Activate() method whenever Window.Deactivated occurs, but wouldn't that be terribly resource-consuming and slow down the other dll that already takes a lot of time ?
I'm waiting for suggestions :)
Thank you
It seems like you are running that lengthy operation in the UI thread. Try running it in a separate thread.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(CallLengthyDllMethod);
}
private void CallLengthyDllMethod()
{
Thread.Sleep(10000); // simulating lengthy operations
MessageBox.Show("Done!");
}
Related
With Winform Form Window (FixedDialog with toolbars) providing options to install and uninstall the application. (Windows exe application)
When a user clicks on the button to install/uninstall, the window can neither be moved nor minimized.
i.e. until the event raised for activity is not completed, it's stuck and not able to perform any action on the form window.
Events are added as below which does independent work.
in form1.designer.cs
private void InitializeComponent(string defaultPath)
{
//Other steps
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.InstallButton.Click += new System.EventHandler(this.InstallButton_Click);
this.UnInstallButton.Click += new System.EventHandler(this.UnInstallButton_Click);
}
Eg. The function InstallButton_Click has multiple steps for installation which copies files and does other work which takes around half a minute. During this time it doesn't allow to move or minimize the window.
In form.cs
private void InstallButton_Click(object sender, EventArgs e)
{
//Multiple steps for installation
//takes around 20-30 seconds to complete
}
The issue is similar to what is mentioned here, but don't see an acceptable answer there.
Is there a way to allow the user to minimize or move the window?
Here multiple approaches are available.
As the code involved some steps updating the GUI controls back, couldn't use the solutions directly.
As almost all solutions are based asynchronous principle, it used to throw an error, Cross-thread operation not valid: Control 'InstallButton' accessed from a thread other than the thread it was created on.
To avoid it segregated steps involving GUI control access and executed them sequentially while as remaining independent code was run asynchronously using Task.Run() method.
It has ignorable execution time for GI
//Logic to update control
Task.Run(()=>
{
//Remaining logic
});
I've put a loading message like this on my form :
public void myFunc()
{
lbl_status.Text = "Loading ... Please Wait";
// Some Database Works
lbl_status.Text = "Done";
}
but there is a problem. Some times when I click on the button ( Which does myFunc method ) my application doesn't show the Loading message. It just does the database work than it will show Done message.
I know that sometimes the database work is very fast so Loading message won't show but sometimes it is not that fast, like the fist time I open my app. At that time my application seems to be disabled and no buttons and no textBoxes and ... works and after the database work it will be OK and show Done message and never shows Loading message again!
You can do your db work in background thread:
public void myFunc()
{
lbl_status.Text = "Loading ... Please Wait";
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
}
EDIT: oops DB works should be in the DoWork event handler :)
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Some Database Works
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lbl_status.Text = "Done";
}
Your application, like all Windows GUI application, needs to process the GUI events. It is these events that do things like refresh the labels and make your application seem 'responsive'. See Window Messages. Your winforms application runs the message loop when you call Application.Run:
Begins running a standard application message loop on the current thread
If you block the processing while waiting for database work then it will stop refreshing and it will be unresponsive (not respond to clicks or keyboard). So you have to do your database w/o blocking the main loop. There are several options:
use a BackgroundWorker.
use ThreadPool.QueueUserWorkItem.
use the async database methods like SqlCommand.BeginExecuteReader and completion callbacks
use await methods like SqlCommand.ExecuteReaderAsync
Each method has pros and cons, the easiest to start with is probably the first one. Be aware that from a background thread, or from a completion callback, you must use the Control.Invoke when interacting with the main GUI (ie. when updating the form or any element on it).
Any updates to UI objects must be done on the UI thread.
You should look into Control.Invoke to provide you a way to put your call on the Display thread the Control is on.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invoke%28v=vs.110%29.aspx
for further clarification, this also means that your heavy updates should be done on a background thread.
WorkerThread may be a good solution for you.
When my main form loads I want to do some processing like loading the last client viewed. This extra processing causing the drawing of the form to be pretty ugly. I am doing it on form shown so I would expect that the form would be done painting.
I have used PostMessage... Is there a modern day version of PostMessage? During this processing I would like to set the hourglass cursor.
I think what you are trying to do is background processing. So you can use a BackgroundWorker class. When your form loads, you would start the background worker and when the worker is finished, it will notify your form through the RunWorkerCompleted event. You can even get progress updates from the worker by implementing ProgressChanged event.
How about using Task + ContinueWith. (If you upgrade to .Net 4.5 async/await would be a better choice).
private void Form_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000); //Some long Running Jobs
return "Finished";
})
.ContinueWith(t =>
{
this.Text = t.Result; //Update GUI
},
TaskScheduler.FromCurrentSynchronizationContext());
}
I am not sure why would you want to use PostMessage. If you post a message using PostMessage it will eventually be obtained by GetMessage and unless the message is posted from a different thread whole exercise seems a little futile. Am I missing something here? As for BackgroundWorker please refer to the solution for Label is not set until method is done.
I have the following code, Some times ArchiveFiles() take more time to complete its execution.
When user clicks exit option more than one time from context menu, Application becomes non responding if ArchiveFiles() takes more time. How can I show a wait message when he clicks the exit option again?
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
ArchiveFiles();
Application.Exit();
}
Thanks
Bhaskar
You can use BackgroundWorker.
Using BackgroundWorker, you will be able to execute time consuming tasks such as the one you have on another thread so that the UI doesn't freeze. You will be able to report the progress of that task, then report it accomplishement and executing what ever logic you need after its completion.
Handle the BackgroundWorker.DoWork event to start the operation that performs the potentially time-consuming work.
Handle BackgroundWorker.ProgressChanged event to report the progress of an asynchronous operation to the user.
Finaly, handle BackgroundWorker.RunWorkerCompleted event to implement whatever logic you want to be implemented after the task has been completed.
Refer to the following:
BackgroundWorker Component Overview
C# BackgroundWorker Tutorial
Create a new WaitingForm and put an image control on the form and use the below .gif in that image control which which automatically animate. Then use the code below:
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var wait = new WaitingForm())
{
wait.Show();
ArchiveFiles();
Application.Exit();
}
}
This may not be the best solution but it is the quickest. If you want the form to be a dialog, use wait.ShowDialog(); instead and carry ArchiveFiles(); Application.Exit();
functions inside it (if that is a probability).
You'd have to run ArchiveFiles asynchronously, by either using a thread or task, or making some delegate and using its BeginInvoke method.
You should use then some synchronization object like ManualResetEvent so the main thread don't continue executing, calling Application.Exit(). Or you could use some event to know when the operation finishes and then call Application.Exit() (or call it inside ArchiveFiles), but I find that to be worse.
We have a application written in C#, using WPF.
It has a timer based activation event which leads to some drawing in a DirectX context.
All seems well, until we open child window, and move it around the screen. The timing seems to coincide with the timer getting fired, but at that moment, the entire screen (even other applications) seem to freeze, and the user is unable to click anywhere.
The normal operation resumes from the exact same point where it froze if one presses ALT+TAB key combination. During the frozen state, there is no rise in CPU/memory utilization which leads me to suspect some kind of blocking on the main thread.
Normally, if my application hangs in the middle of some operation, I'd go, press pause from Visual Studio, and see the thread view in the debugger. This gives me sufficient idea on which call is the culprit.
But in this case, if I press ALT+TAB to switch to IDE, my application resumes it's normal execution. If I place my IDE on secondary screen and try to click (without the need to press ALT+TAB), it appears to be frozen as well (As I previously mentioned, entire desktop seems to be frozen to mouse clicks. Mouse movement however is normal)
Any one faced/aware of a similar problem, and on how can I go on debugging it ?
Try using a BackgroundWorker process to run your timer in the background. I was having the exact same issue, and I used the BackgroundWorker and it fixed my issue. Here is some sample code to get you started:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
bw.Dispose();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Do Stuff Here
return;
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Do an action when complete
}
Just wrap that in your timers tick event and everything should function as needed.
The problem is your using a single thread. You need to offload the work to another thread. ie: ThreadPool.QueueUserWorkItem or use the newer Task model, or the Dispatcher.Invoke ...
Just make sure you marshal any UI back to the UI thread when your done or it will GPF on you.