Run NSAlert app-modal without blocking application - c#

I have an application hosting a finder extension. This extension requests data from the host application.
While running a worker thread, i need to ask the user for feedback via the NSAlert modal. Invoking the main thread and displaying the modal blocks the entire application, thus preventing the finder extension from requesting data.
My application structure is as follows:
1 application thread
1 thread for listening to a CFMessagePort
1 thread for running a file watcher
1 thread performing background work
as well as a running finder extension as client application.
Creating the worker thread
myWorker = new BackgroundWorker();
myWorker.WorkerSupportsCancellation = true;
myWorker.DoWork += new DoWorkEventHandler(delegate(Object o, DoWorkEventArgs args) {
// Time consuming task
// which may require user feedback
});
Inside the worker, invoke an event that is later handled in the os-specific part of the application.
System.Threading.AutoResetEvent waitHandle = new System.Threading.AutoResetEvent(false);
controller.ConflictOccured ? .Invoke(this, new ConflictEventArgs {
Type = ConflictType.LocalFolderDeleted, OnResolve = (userChoice) => {
// Do something else
waitHandle.Set();
}
}
});
// Wait until conflict event has been handled
waitHandle.WaitOne();
Handling the event, running the modal.
private void HandleMyConflict(object sender, ConflictEventArgs eventArgs) {
// Invoking the UI thread
NSApplication.SharedApplication.BeginInvokeOnMainThread(() => {
var conflictAlert = new NSAlert() {
AlertStyle = NSAlertStyle.Warning, MessageText = "Foo", InformativeText = "Bar",
};
conflictAlert.AddButton("Foo");
conflictAlert.AddButton("Bar");
// Run the actual modal - this blocks everything
nint userResponse = conflictAlert.RunModal();
// Determine the user choice
// Resolve event by invoking callback
eventArgs.OnResolve(choice);
});
}
I would expect the modal to be blocking the main application thread since its handling the event, but not the watcher and listener thread. The latter being in an entirely different ThreadPool.

Related

In a C# class library, closing a WPF LogWatcher Window in its own thread, stops the main thread

I wrote a C# COM exposed class library. I call its exposed methods from third party software.
I wrote a WPF dialog to watch the log entries as the business logic is performed.
The WPF dialog is started on a separate thread, prior to starting the actual method generating log entries. This is working pretty good.
The issue I have is that if I close the logwatcher window before to the main method DoTheActualWorkAsync() is finished, the DoTheActualWorkAsync() method stops running and the methods seems deadlocked.
Any ideas how to solve this? Why does closing a WPF window in a separate thread freeze the main thread?
This is my code:
// Exposed Method
public void DoALotOfWorkWrapper()
{
// Start WPF LogWatchWindow on new thread
var logWatcherThread = this.GetLogWatcherThread();
// Peform a lot of business logic, block for procedure to be ready
DoTheActualWorkAsync().Wait();
// Join logwatcher thread so method doesn't finish before window is closed
WaitForLogWatcherThread(logWatcherThread);
}
private Thread GetLogWatcherThread()
{
var signalEvent = new ManualResetEvent(false);
var logWatchWindowThread = new Thread(() =>
{
var vm = new LogWatchWindowViewModel();
var win = new LogWatchWindow
{
DataContext = vm
};
win.Show();
vm.HookupLog4NetEvents(win);
win.Closed += (sender2, e2) =>
{
vm.DetachLog4NetEvents();
win.Dispatcher.InvokeShutdown();
};
signalEvent.Set();
Dispatcher.Run();
});
logWatchWindowThread.SetApartmentState(ApartmentState.STA);
logWatchWindowThread.Start();
// Block to wait for Window and its viewmodel to be ready for displaying new log4net entries
signalEvent.WaitOne();
signalEvent.Reset();
return logWatchWindowThread;
}
private static void WaitForLogWatcherThread(Thread logWatcherThread)
{
logWatcherThread.Join();
}

WinForm UI responds slowly during background thread is running

I've designed a long-time process as below structure:
void OnButtonClick() {
object initState = 0;
Thread thread = new Thread(new ParameterizedThreadStart(DoLongTimeProcess));
thread.IsBackground = true;
thread.Start(initState);
}
void DoLongTimeProcess(object initState) {
// ... Do something
InvokeAsyncMethod(CallbackFunction);
}
void CallbackFunction() {
object newState = AnotherFunction(); // Don't care, just a function to create state
// A 'if' logic to call function again, ex: if (newState is int)
DoLongTimeProcess(newState);
}
My knowdledge, the thread has ended at InvokeAsyncMethod on the first call. Therefore, I cannot control priority of methods called later. I don't want to create another thread in CallbackFunction because it already belongs to another thread.
During above process is running, the UI seems slow to repond, for example, user want to cancel the process. How to make the UI response faster?

Adding text to RichTextBox Async #C / WPF

What I am trying to achieve is to add text after every operation to a RichTextBox.
The problem is, that these operations take some time and instead of viewing the appended text after every operation finishes, I view them all at the end of the routine.
Semi-Pseudo code:
RichTextBox richTextBox = new RichTextBox()
if (Operation1())
{
richTextBox.AppendText("Operation1 finished");
if (Operation2())
{
richTextBox.AppendText("Operation2 finished");
if (Operation3())
{
richTextBox.AppendText("Operation3 finished");
}
}
}
The problem is that I view the appended text of operation 1 & 2 after the operation 3 is finished.
I read somewhere that I need to use something called BackgroundWorker???
Using BackgroundWorker, you would just put the background work into DoWork, and the update into RunWorkerCompleted:
var bw1 = new BackgroundWorker();
var bw2 = new BackgroundWorker();
var bw3 = new BackgroundWorker();
bw1.DoWork += (sender, args) => args.Result = Operation1();
bw2.DoWork += (sender, args) => args.Result = Operation2();
bw3.DoWork += (sender, args) => args.Result = Operation2();
bw1.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation1 ended\n");
bw2.RunWorkerAsync();
}
};
bw2.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation2 ended\n");
bw3.RunWorkerAsync();
}
};
bw3.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation3 ended\n");
}
};
bw1.RunWorkerAsync();
You'll notice that this runs afoul of "DRY". You could always consider abstracting the tasks for each step using something like:
var operations = new Func<bool>[] { Operation1, Operation2, Operation3, };
var workers = new BackgroundWorker[operations.Length];
for (int i = 0; i < operations.Length; i++)
{
int locali = i; // avoid modified closure
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => args.Result = operations[locali]();
bw.RunWorkerCompleted += (sender, args) =>
{
txt.Text = string.Format("Operation{0} ended\n", locali+1);
if (locali < operations.Length - 1)
workers[locali + 1].RunWorkerAsync();
};
workers[locali] = bw;
}
workers[0].RunWorkerAsync();
You could do the above 3 times, or use ReportProgress to run all tasks in one background thread, and periodically report progress.
The way that WPF (and most other UI frameworks work) is that there is a UI thread, which handles all the UI events (such as button clicking) and UI drawing.
The UI can't draw things if it's busy doing other things. What's happening is this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Now, the UI can't redraw or perform any other updates until your click handler function finishes.
Your Operation1 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation2 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation3 function finishes, and you append to the RichTextBox
Your function finishes, and now the UI thread is free, and it can finally process the updates and redraw itself.
This is why you see a pause and then all 3 updates together.
What you need to do is make the code that takes a long time run on a different thread so that the UI thread can remain free to redraw and update when you'd like it to. This sample program works for me - it requires .NET 4.5 to compile and run
using System.Threading.Tasks;
...
// note we need to declare the method async as well
public async void Button1_Click(object sender, EventArgs args)
{
if (await Task.Run(new Func<bool>(Operation1)))
{
richTextBox.AppendText("Operation1 finished");
if (await Task.Run(new Func<bool>(Operation2)))
{
richTextBox.AppendText("Operation2 finished");
if (await Task.Run(new Func<bool>(Operation3)))
{
richTextBox.AppendText("Operation3 finished");
}
}
}
}
What happens here is that we use the C# magical async feature, and the order of operations goes like this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Instead of calling Operation1 directly, we pass it to Task.Run. This helper function will run your Operation1 method on a thread pool thread.
We use the magic await keyword to wait for the thread pool to finish executing operation1. What this does behind the scenes is something morally equivalent to this:
suspend the current function - and thus free up the UI thread to re-draw itself
resume when the thing we're waiting for completes
Because we're running the long operations in the thread pool now, the UI thread can draw it's updates when it wants to, and you'll see the messages get added as you'd expect.
There are some potential drawbacks to this though:
Because your Operation1 method is Not running on the UI thread, if it needs to access any UI related data (for example, if it wants to read some text from a textbox, etc), it can no longer do this. You have to do all the UI stuff first, and pass it as a parameter to the Operation1 method
It's generally not a good idea to put things that take a long time (more than say 100ms) into the thread pool, as the thread pool can be used for other things (like network operations, etc) and often needs to have some free capacity for this. If your app is just a simple GUI app though, this is unlikely to affect you.
If it is a problem for you, you can use the await Task.Factory.StartNew<bool>(_ => Operation1(), null, TaskCreationOptions.LongRunning))) instead and each task will run in it's own thread and not use the thread pool any more. It's a bit uglier though :-)

ProgressBar in Subthread

I need to write a WPF-Assembly with only a progressbar and a cancel button. This gui has to run in a subthread, so that a program calling the Assembly (sending progress values and checking the cancel state) won't be blocked. I know how to do this the other way around via Backgroundworker, but not how to run a gui in a subthread and communicate between the two threads. Any help appreciated.
Class that starts the gui thread:
public ProgressBar()
{
StartProgressWindowThread(0);
}
private void StartProgressWindowThread(int numberProgressBars)
{
progressThread = new Thread(new ParameterizedThreadStart(ThreadStartPoint));
progressThread.IsBackground = true;
progressThread.SetApartmentState(ApartmentState.STA);
progressThread.Start(numberProgressBars);
}
private void ThreadStartPoint(object args)
{
progressBarWindow = new ProgressBarWindow(args);
progressBarWindow.OnCancel += new EventHandler(CancelAction);
progressBarWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
Update:
At the moment I am sending messages from the calling class to the gui thread via NamedPipes and then invoke the sent values. The cancel button on the gui is connected through a delegate. This works fine, but I am not sure if the NamedPipes are the right choice.
You can use Invoke:
Invoke(new Action<object>((args) =>
{
// update gui from a different thread
}), e.Argument);

How to construct a new WPF form from a different thread in c#

I'm having trouble with the concept of threads and how to use them.
I'm trying to code a fairly basic chat program (as part of a larger program) and it currently works like this:
The 'NetworkSession' class receives the input from the server on a separate thread in a loop. If it receives input that indicates it should open a new chat window it constructs a new WPF class (ChatWindow) and displays it.
Originally I got the error that "The calling thread must be STA, because many UI components require this.". So i set the thread to be STA but now of course the WPF form is unusable because its running on the same thread as the blocking loop.
So my question is how do I create a new instance of a WPF form from within another thread.
I've seen alot of discussion about this but it tends to deal with running a delegate from a form that has already been constructed.
Here is some code.
while (Connected) //this loop is running on its own thread
{
Resp = srReceiver.ReadLine();
if (Resp.StartsWith("PING")) SendToServer("PONG");
if (Resp.StartsWith("CHAT FROM"))
{
String[] split = Resp.Split(' ');
Console.WriteLine("Incoming Chat from {0}", split[2]);
bool found = false;
if (Chats.Count != 0)
{
foreach (ChatWindow cw in Chats)
{
if (cw.User == split[2])
{
found = true;
cw.AddLine(cw.User, split[3]); // a function that adds a line to the current chat
}
}
}
if (!found)
{
ChatWindow temp = new ChatWindow(split[2], split[3]);
Chats.Add(temp); //this is a collection with T = ChatWindow
temp.Show();
}
}
}
If you're constructing NetworkSession from your UI Thread, you can snag a reference to the current Dispatcher that can manipulate the UI later.
NetworkSession.cs
private Dispatcher _dispatcher;
public NetworkSession()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
//any thread can call this method
public void DoStuff()
{
Action action = () =>
{
ChatWindow temp = new ChatWindow(split[2], split[3]);
Chats.Add(temp);
temp.Show();
};
_dispatcher.BeginInvoke(action);
}
The code below, which I took from here worked for me:
public static void StartChatWindow()
{
Thread thread = new Thread(() =>
{
ChatWindow chatWindow = new ChatWindow();
chatWindow.Chat(); // Do your stuff here, may pass some parameters
chatWindow.Closed += (sender2, e2) =>
// Close the message pump when the window closed
chatWindow.Dispatcher.InvokeShutdown();
// Run the message pump
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
What you really need to do is construct the window/form on your main UI thread. You probably need to define a delegate that you can call from your network thread and that delegate should have a method attached that will call this.Dispatcher.BeginInvoke() -> inside which you will construct your window.
The this.Dispatcher.BeginInvoke() call is necessary to execute code on the UI thread, otherwise even with a delegate, code would be executed on the network thread.
Both the delegate and the method for creating a new chat window should probably be attached to the MainWindow...

Categories