How to create an application global Progress Window without blocking running threads? - c#

We have a project that have a couple years development invested into and it now need a progress window because some process are very long. We deal with 3D CAD, advanced engineering, Simulations Third Party DLL, Web services, WCF services.
Now i am trying to add a generic process window you can start in 1 place and end anywhere else. that's not complicated per say. simple static class that open a window on MyClass.Open(string Message) and a MyClass.Close() to hide it once the heavy duty code completed. This works perfectly except the heavy code block the Thread.
The project is a mix of bunch of projects but this part is 99% WPF and i am fairly new to WPF threading as they don't behave like winforms. In winforms thread i create that open other forms are not block by main app heavy work. In WPF the copy pasted code the form is frozen.
I tried Thread and Background Worker without success. then to make sure it wasn't my window the issue or my static class i tried opening the window manually with a Form.Show(); and a Form.Close(); in one of the heavy work function an the form still showed and close properly but yet still frozen. then i removed the Form.Close(); and it started to move right after the work completed. Now i find very strange that the main thread when working at full capacity freeze other threads. I have an actual Exact copy in winform for that progress window except that instead being a Form with label i have a WPF window with a label in a grid.
I tried doing it the right way which is creating a thread for the heavy function but compiler now give me 2,404 errors. I would have at least a week of work just o try if something works and i'd rather not as changing the whole project like so would take at least a year and half and we mostly have 2-3 weeks to complete this so i am looking or any solution that might help finishing that. I can be very dirty. as long as it works.
thank you very much.
Edit :
#Baldrick requested more detail about thread.
I went very simple for thread.
public static class CWaitingMessage
{
private static frmWaiting window = new frmWaiting();
private static Thread t = null;
private static string Message = "";
public static void Open(string sMessage)
{
Message = sMessage;
t = new Thread(new ThreadStart(RunForm));
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
}
private static void RunForm()
{
try
{
window = new frmWaiting();
window.UpdateText(Message);
window.Show();
System.Windows.Threading.Dispatcher.Run();
}
catch
{
window.Close();
}
}
public static void Close()
{
if (t != null)
{
t.Abort("Completed");
}
}
}
If i use System.Windows.Threading.Dispatcher.Run(); instead while (ShowForm) { } the form isn't frozen but my thread disappear by that i mean it never reach f.Close();... ever.
Edit #2 :
Managed to make the abort of the thread using the dispatcher.Run but now i have a bigger issue. the program never stop. the thread start, then i call the abort with a special catch on thread exception and on abort i call a method in the window to save the amount of time it's been opened and close the form. I checked and the file is created so the abort does work but if Dispatche.Run is calle in a thread that is aboded it seems to live on his own. now i have created a monster that never stop running. i close my app completely an visual studio still see it as running.
I am very close to forget about a solution in WPF and go for a winform form to show that message as i know it works perfectly in winforms without that freeze up annoyance.
Edit #3 : Updated my current class i use. I have checked my old exampled and background worker i was only doing a normal form load then running a background worker to loop. the form was in a the same thread as the main code which is completely normal as Background worker are MTA and therefore block UI thread. for UI i need Threads which are STA.
I cannot call dispatcher.Invoke to test it. i couldn't find the assembly for it. my compiler and intellisense don't like it by default :)

The Thread class is not used so much these days as there are now much simpler ways of creating asynchronous methods. For example, please take a look at the Task Class page on MSDN for examples on how to easily create asynchronous methods in WPF.
If you are using .NET 4.5, you can also use the new async and await keywords for even easier asynchronous methods. Please see the await (C# Reference) page on MSDN for a detailed description of this new functionality with code examples.
Having said that, using a BackgroundWorker may actually be your best bet for implementing ProgressBar update functionality with its easy access to update the UI on the UI thread. Please take a look at the BackgroundWorker Class page on MSDN for a full example.
If you need specific help with something, please edit your question in order to add the relevant code.

Found it. after 3 days of work.
For people wondering how to open and close a thread in WPF without freezing he main thread when the new form have animation and no process work.
Code :
public static class CWaitingMessage
{
private static event Action CloseWindow = delegate { };
public static void Open(string sMessage)
{
Thread t = new Thread(delegate()
{
frmWaiting window = new frmWaiting(sMessage);
CloseWindow += () => window.Dispatcher.BeginInvoke(new ThreadStart(() => window.Close()));
window.Closed += (sender2, e2) => Window.Dispatcher.InvokeShutdown();
window.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
public static void Close()
{
CloseWindow();
}
}
Note that no error handling and multiple window opening is supported. this is simple the skeleton working and ready to use. I know at least 2 handful of people that will be very interested in this piece of code.
If you required process also on that window background worker works perfectly in the window. This is very useful for mouse over over a grid being filled asynchronously and while you mouse over you can still open secondary window with another DataGrid and heavy work without issue.

Related

Different results when passing a MetroFramework.Forms.MetroForm as Argument to a Method [duplicate]

UPDATE: Just to summarize what my question has boiled down to:
I was hoping that constructing .NET forms and controls did NOT create any window handles -- hoping that process was delayed until Form.Show/Form.ShowDialog
Can anyone confirm or deny whether that is true?
I've got a large WinForms form with tab control, many many controls on the form, that pauses while loading for a couple seconds. I've narrowed it down to the designer generated code in InitializeComponent, rather than any of my logic in the constructor or OnLoad.
I'm well aware that I can't be trying to interact with the UI on any thread other than the main UI thread, but what I'd like to do is to have the application pre-load this form (run the constructor) in the background, so it's ready for display on the UI thread instantly as soon as the user wants to open it. However, when constructing in a background thread, on this line in the designer:
this.cmbComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
I'm getting the error
Current thread must be set to single
thread apartment (STA) mode before OLE
calls can be made. Ensure that your
Main function has STAThreadAttribute
marked on it.
Now this is halfway down the designer file, which gives me hope that in general this strategy will work. But this particular line seems to be trying to instantly kick off some kind of OLE call.
Any ideas?
EDIT:
I think I'm not making myself clear here. The delay seems to take place during the construction of a bazillion controls during the designer-generated code.
My hope was that all this initialization code took place without actually trying to touch any real Win32 window objects since the form hasn't actually been shown yet.
The fact that I can set (for example) Label texts and positions from this background thread gave me hope that this was true. However it may not be true for all properties.
While it is not possible to create a form on one thread, and display it using another thread, it is certainly possible to create a form in a non main GUI thread. The current accepted answer seems to say this is not possible.
Windows Forms enforces the Single Threaded Apartment model. In summary this means that there can only be one Window message loop per thread and vice versa. Also, if for example threadA wants to interact with the message loop of threadB, it must marshal the call through mechanisms such as BeginInvoke.
However, if you create a new thread and provide it with it's own message loop, that thread will happily process events independently until it is told to end the message loop.
So to demonstrate, below is Windows Forms code for creating and displaying a form on a non GUI thread:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
ThreadStart ts = new ThreadStart(OpenForm);
Thread t = new Thread(ts);
t.IsBackground=false;
t.Start();
}
private void OpenForm()
{
Form2 f2 = new Form2();
f2.ShowDialog();
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString() ;
}
}
The OpenForm method runs in a new thread and creates an instance of Form2.
Form2 is actually given it's own separate message loop by calling ShowDialog(). If you were to call Show() instead, no message loop would be provided and Form2 would close immediately.
Also, if you try accessing Form1 within OpenForm() (such as using 'this') you will receive a runtime error as you are trying to do cross-thread UI access.
The t.IsBackground=false sets the thread as a foreground thread. We need a foreground thread because background threads are killed immediately when the main form is closed without first calling FormClosing or FormClosed events.
Apart from these points, Form2 can now be used just like any other form. You'll notice that Form1 is still happily running as usual with it's own message lopp. This means you can click on the button and create multiple instances of Form2, each with their own separate message loop and thread.
You do need to be careful about cross Form access which is now actually cross-thread. You also need to ensure that you handle closing of the main form to ensure any non main thread forms are closed correctly.
I think your understanding is a little off. Controls must be touched from the thread that created them, not the main UI thread. You could have numerous UI threads in a application, each with its own set of controls. Thus creating a control on a different thread will not allow you to work with it from the main thread without marshalling all of the calls over using Invoke or BeginInvoke.
EDIT
Some references for multiple UI threads:
MSDN on Message Loops
MSDN social discussion
Multiple threads in WPF
The answer is no.
If you create a window handle on any thread other than the GUI thread you can never show it.
Edit: It is completely possible to create Forms and controls and
display them in a thread other than the main GUI thread. Of course if
you do this you can only access the multi threaded GUI from the thread
that created it, but it is possible. – Ashley Henderson
You need to perform any heavy lifting on a bg thread and then load the data into you GUI widget
In general, properties of the form need to be accessed from the same thread running the message loop. That means, in order to construct the form on another thread, you would need to marshal any calls to actually set properties using BeginInvoke. This is true for property sets from the constructor, too, if they end up generating a message that needs to be processed (as is happening to you now).
Even if you get that to work, what does it buy you? It will be a bit slower, not faster, overall.
Perhaps just show a splash screen while this form is loading?
Alternatively, review why your form takes so long to construct in the first place. It's not common for this to take seconds.
I believe it is possible to add the components created on the non-UI thread to the main UI, I've done it.
So there are 2 threads, 'NewCompThread' and 'MainThread'.
You spin off NewCompThread and it creates components for you - all ready to be displayed on the MainUI (created on MainThread).
But ... you WILL get an exception if you try something like this on NewCompThread:
ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread;
But you can add this:
if (ComponentCreatedOnMainThread.InvokeRequired) {
ComponentCreatedOnMainThread.Invoke(appropriate delegate...);
} else {
ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread;
}
And it will work. I've done it.
The strange thing (to me) is that then the ComponentCreatedOnNewCompTHread 'thinks' it was created on the MainThread.
If you do the following from the NewCompThread:
ComponentCreatedOnNewCompTHread.InvokeRequired
it will return TRUE, and you'll need to create a delegate and use Invoke to get back to the MainThread.
Creating a control in a background thread is possible but only on an STA thread.
I created an extension method in order to use this with the async/await pattern
private async void treeview1_AfterSelect(object sender, TreeViewEventArgs e)
{
var control = await CreateControlAsync(e.Node);
if (e.Node.Equals(treeview1.SelectedNode)
{
panel1.Controls.Clear();
panel1.Controls.Add(control);
}
else
{
control.Dispose();
}
}
private async Control CreateControlAsync(TreeNode node)
{
return await Task.Factory.StartNew(() => CreateControl(node), ApartmentState.STA);
}
private Control CreateControl(TreeNode node)
{
// return some control which takes some time to create
}
This is the extension method. Task does not allow to set the apartment so I use a thread internally.
public static Task<T> StartNew<T>(this TaskFactory t, Func<T> func, ApartmentState state)
{
var tcs = new TaskCompletionSource<T>();
var thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.IsBackground = true;
thread.SetApartmentState(state);
thread.Start();
return tcs.Task;
}

WPF Recreation window in different STA thread

I need to create window with loading gif when my main window is rendering. I have read some articles and make a decision that for this purposes i need to create new thread. I did it like in this article
As a result I have something like that:
LoadingDialog _loadingDlg;
Thread loadingThread;
public void ShowLoading()
{
loadingThread = new Thread(new ThreadStart(loadingThreadWork));
loadingThread.SetApartmentState(ApartmentState.STA);
loadingThread.Start();
}
private void loadingThreadWork()
{
_loadingDlg = new LoadingDialog();
_loadingDlg.Show();
System.Windows.Threading.Dispatcher.Run();
}
public void HideLoading()
{
_loadingDlg.Dispatcher.InvokeShutdown();
}
First time when I call ShowLoading() and then HideLoading() everything works like I want. But when I call ShowLoading() at the second time I get an exception at
_loadingDlg.Show();
with message The calling thread cannot access this object because a different thread owns it.
How can this be? _loadingDlg is created in the previous line, and in the same thread.
In the loadingThreadWork you're creating the control, before the first run it's a null, so in first time you succeed. However, you're creating the dialog in a different thread, which is marked as an owner for the control.
At the next time you're calling the loadingThreadWork, control isn't null, and ant change for it from a different thread (and it is a different thread, because you're creating it again) leads to the exception you've got.
As you're using WPF, you probably should switch from threads to async operations, which are much more readable, supportable and predictable than your current solution.

Thread Starts lags UI WPF

Well, I've been struggling with this for a while and I've been unable to find a solution.
I'm developing one of those cashier apps you see in the supermarket where everything works fast as hell. The cashiers know the program so good they just type at the speed of light so the UI must be incredibly responsive.
Anyway, as I've only coded in WPF I'm using it, if someone says WinForms is faster I'll surely learn it.
This is my code, it's very simple as I'm testing performance
public partial class MainWindow : Window
{
Thread t;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
t = new Thread(HandleThread);
t.Start();
}
private void HandleThread()
{
Thread.Sleep(3000);
}
}
Whenever the button is clicked, a new thread is launched as I plan on connecting to a server in the new thread.
My problem is the method button_Click is VERY SLOW. It takes about 1 second to return so the button looks like it's stuck and the application look sluggish. I've tried threads, BackgroundWorker, changed the framework to 4.0 and tried Tasks. NOTHING. Whatever I do to start some kind of background work the method takes forever.
I need to check the username and password on a remote server, how can I accomplish it without hurting the UI?
You should turn off the IntelliTrace (How-To).
On the Tools menu, click Options.
In the Options dialog box, expand the IntelliTrace node and then click General.
Clear the Enable IntelliTrace check box.
Click OK.
Do you need to spawn a new thread every time you click your button? Is this thread long-lived? Do you really want to allocate 1 meg of stack space (on 64 bit OSes) for this every time you click this button? Really sure you don't want to use the TPL's Task and CancellationTokenSource instead?
In your case, you should really not create a thread every time you click, what you want is start a long-running async task and check the result.
public void OnClick(object src,EventArgs args)
{
var login = tbLogin.Text;// assuming non MVVM coding here
var pwd= tbPass.Text;
Task.Factory.StartNew(()=>{
return _myWebService.CheckAuth(login,pwd); // your login stuff here
}).ContinueWith(wsTask=>{
if(!wsTask.IsCompleted){ // handle errors / cancel }
DisplayLoginState(ws.Result);
}, TaskScheduler.FromCurrentSynchronizationContext()); // this runs on the UI Thread
}

C# Getting to original thread to set textbox values

I've got my main form Form1 running the main bulk of my program.
I have a separate thread started to perform an algorithm.
When I run the method from the new thread, method MyAlgorithm() I get the error
InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on."
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
This is the method that I want to run contained in Form1, the main class in my application.
// Reset the results values
public void ShowResults()
{
while (true)
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
}
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
I'm invoking my thread like this...
// Set the algorithm method up in it's own thread
Thread thread = new Thread(new ThreadStart(MyAlgorithm));
// Run the algorithm
thread.Start();
How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?
In Windows Forms, you'd either use Control.Invoke/BeginInvoke or use a BackgroundWorker and perform the update in the progress event handler.
In WPF you'd use Dispatcher.Invoke/BeginInvoke.
In C# 5 and .NET 4.5 you'll be able to use async methods which should make a lot of this much simpler...
I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.
If the "threaded method" is just an instance method of the Form, then you've already got the this reference. If it's not, you'll need to provide that information - ideally as an ISynchronizeInvoke to avoid a direct dependency on Windows Forms if you can express the "update" part separately. (That interface is somewhat deprecated these days, in favour of synchronization contexts, but it still works perfectly well.)
Have a look at Control.Invoke():
public void ShowResults()
{
while (true)
{
Thread.Sleep(1000); // don't spam the UI thread
if (this.InvokeRequired)
{
this.Invoke((Action)UpdateGui);
}
else
{
UpdateGui();
}
}
}
private void UpdateGui()
{
loopsNum.Text = Convert.ToString(resultLoopsNum);
nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
You can use:
myform.Invoke(ShowResults);
There's other options here too:
Alternately use a System.Forms.Timer to call ShowResults periodically. Or another option would be not to use another thread to do the operation; do it in the GUI thread and call Application.DoEvents() from within the operation when you want to let the GUI update.
The first option is nice because it keeps you from accidentally flooding the GUI with Invoke requests, and the second option is nice because it's all on the GUI thread and allows you to have fine-grain control over when things get displayed on the GUI.

DirectShow/WPF Threading issue

I am writing an app using WPF and DirectShow and have run into a sticky issue. My application utilizes DS through static methods Start() and Stop() in a static class written using DirectShowNet (a C# wrapper class for DS). I have a Windows Forms panel in my WPF window (via a WindowsFormsHost object) that I need the graph to render to. Here is the general flow of the app: The Start() method builds the graph and starts it; I pass the handle of my windows form panel and render to it using the IVideoWindow interface. Start() returns and the graph runs in the background. At some point, Stop() is called; this method stops the graph and destroys it.
Everything works fine as long as I call Start() and Stop() from the same thread. However, I will need to call them from different threads in my app. When this is the case, I get an exception in the part of code that destroys the graph (specifically, when I am attempting to enumerate the filters). I discovered that I need to use a Multithreaded Apartment when working with DirectShow. This is easy with a Windows Forms app; I just throw a [MTAThread] on my main method and everything works.
For my WPF app, this is apparently not an option. My workaround has been to launch new MTA threads when I need to call Start() and Stop(). This gets rid of the exception, but has introduced another problem. When the Start() method returns, the video disappears from the render panel. If I put a Sleep at the end of the Start() method, the video will be visible until the Sleep ends. In addition, I have verified that the graph continues to run after the video disappears. Does anyone have any advice as to how to proceed? Thanks.
Kevin
Which exception is thrown? I'm guessing something along the likes of: "The calling thread cannot access this object because a different thread owns it."
When this is the case, use a correct dispatcher to do your calls, as explained here.
FYI, Windows Forms doesn't support a MTAThread main thread. If it worked, then you just got lucky.
I believe you should be able to invoke DS objects from STA threads just fine - though I'm not that familiar with DS, it sounds like you're using windowless mode and it seems to me that it would work best with STA.
In that case, why not always call Start/Stop from your main thread? If another thread needs to tell the main thread to stop or start, then just have it queue a task to a TaskScheduler.FromCurrentSynchronizationContext to run it on the main thread.
Ok, so I've encountered a problem not too dissimilar before, but not with WPF, so take the following (very hacky) suggestion with a pinch of salt.
The following method basically creates an entirely separate application thread to run directshow commands in, but tells direct show to use the handle of the windows forms control hosted in your WPF application.
So, first we need a dummy WinForms form that we can use to invoke calls on, but that is never going to get rendered:
/// <summary>
/// Just a dummy invisible form.
/// </summary>
private class DummyForm : Form
{
protected override void SetVisibleCore(bool value)
{
//just override here, make sure that the form will never become visible
if (!IsHandleCreated)
{
CreateHandle();
}
value = false;
base.SetVisibleCore(value);
}
}
Next step is to create a thread that we can put a message loop on:
//this will need to be a class level variable, since all the directshow
//calls will get invoked on this form
DummyForm dumbForm;
Thread separateThread;
private void CreateDummyForm()
{
ManualResetEvent reset = new ManualResetEvent(false);
//create our thread
separateThread = new Thread((ThreadStart)
delegate
{
//we need a dummy form to invoke on
dumbForm = new DummyForm();
//signal the calling method that it can continue
reset.Set();
//now kick off the message loop
Application.Run(dumbForm);
});
//set the apartment state of this new thread to MTA
separateThread.SetApartmentState(ApartmentState.MTA);
separateThread.IsBackground = true;
separateThread.Start();
//we need to wait for the windowing thread to have initialised before we can
//say that initialisation is finished
reset.WaitOne();
//wait for the form handle to be created, since this won't happen until the form
//loads inside Application.Run
while (!dumbForm.IsHandleCreated)
{
Thread.Sleep(0);
}
}
So, once the dummy form (and its thread) have been created, you can invoke calls on the MTA
application thread like so:
/// <summary>
/// Blank delegate, used to represent any Invoke or BeginInvoke target method.
/// </summary>
public delegate void InvokeHandler();
//i'm assuming here that DSComponent is a class that all your directshow
//code is in, and externalControl is the WinForms control you have embedded in
//your application.
dumbForm.Invoke(new InvokeHandler(delegate
{
//maybe something like this?
DSComponent.Start(externalControl);
}));
//and to stop it...
dumbForm.Invoke(new InvokeHandler(delegate
{
DSComponent.Stop();
}));
Then, when you're all done with the Directshow stuff, shutdown your separate application thread like so:
//to end the separate thread and application loop,
//just close your invisible form
dumbForm.Close();
Advantage of this approach is that you neatly sandbox directshow into a separate thread. Disadvantage is the context switch of the Invoke calls, plus the overhead of having another application thread. You may have some fun shoehorning this into your current architecture, but it should help.
Let me know how you get on, I am intrigued as to how well this works.

Categories

Resources