I come from an embedded C background and I am working on my first C# application and I have hit a wall on this and my research is not panning out so I thought I would ask here.
Simple app, so far. I have a MainWindow that, among a bunch of other stuff, starts a TCPClient thread on a button click:
public partial class MainWindow : Window
{
....
TCPConnection myCon = new TCPConnection();
....
private void connectButton_Click(object sender, RoutedEventArgs e)
{
networkListBox.Items.Add("Connecting...");
myCon.Connect("localhost", updateNetworkListBox);
}
}
....
public void updateNetworkListBox(string message)
{
networkListBox.Items.Add(message);
}
And in TCPConnection.cs:
public class TCPConnection
{
....
public void Connect(string server, ReportDelegate reportDelegate)
{
this.server = server;
clientThread = new Thread(() => Client(this.server));
clientThread.Start();
reportDelegate("Started client thread...");
}
static void Client(string server)
{
try
{
Int32 port = 25565;
TcpClient client = new TcpClient(server, port);
Byte[] outgoingBytes = new Byte[1024];
string outgoingString = "Hello! I am " + Guid.NewGuid();
outgoingBytes = System.Text.Encoding.ASCII.GetBytes(outgoingString);
NetworkStream stream = client.GetStream();
stream.Write(outgoingBytes, 0, outgoingBytes.Length);
stream.Close();
client.Close();
}
The first thing I would like to do, now that TCP connection works is send a message back to the UI such as "Client thread connecting...", "Client thread connected..." and have it show up in the networkListbox.
Within the Connect() method, I was able to do this by using the delegate but this obviously will not work in the new thread since one is not able to directly access UI controls from another thread.
I have read loads of articles on this and I know that I probably want to use the Dispatcher to to do this. However, almost all of the examples I have seen have created a new thread within the current class and, for example, passed an anonymous method to Dispatcher.Invoke().
One exception to this discussion which advocated using an EventHandler and initializing it in the main window. That seems less than ideal but maybe I am wrong.
Further down, someone else advocated data sharing. Again, that seems less than ideal to me.
Other articles I have read appear to be out of date.
So, I welcome any explanations on how to go about this. It may be that I am just getting hung up syntactically but I suspect that, although I think I am mostly clear on delegates, lambdas, etc., I am probably hung up on what exactly needs to get done.
If you can show how it would be done in this specific example with some explanation, I would greatly appreciate it.
And maybe some specific questions on some points that are a little hazy for me:
1) Can my worker task access on it on its own or must it be provided with the UI's Dispatcher?
2) Should the UI provide a delegate that performs the dispatch or should the dispatch be coded in the worker task, referencing the UI Dispatcher?
Thanks very much.
For your question about providing a sample, if there is a worker class like...
public class Worker
{
public Worker(Action<string>action)
{
Task.Run(() =>
{
int i = 0;
while (true)
{
++i;
Task.Run(() => { action("Current value " + i); });
Task.Run(() =>
{
// doing some work here
});
Thread.Sleep(1000);
}
});
}
}
...which is performing background work on different threads and advising the caller via the delegate. The delegate is a plain vanilla Action that takes a string. Then the View Model should be implemented such that it does not care on which thread the message originated. Here's the corresponding code in the VM...
private readonly SynchronizationContext _context = SynchronizationContext.Current;
private void StartWorker()
{
Worker w = new Worker((s) => _context.Post(delegate { StatusText = s; }, null));
}
This code uses a SynchronizationContext, but could just as easily use a dispatcher. The point being that the responsibility for sync'ing up on the UI thread doesn't belong to a worker. The worker shouldn't care, and similarly the VM is thread-agnostic and posts everything via its SynchronizationContext.
The code for the StatusText property looks like this...
private string _statusText;
public string StatusText
{
[DebuggerStepThrough]
get { return _statusText; }
[DebuggerStepThrough]
set
{
if (value != _statusText)
{
_statusText = value;
OnPropertyChanged("StatusText");
}
}
}
And finally, on the UI, it is presented like this...
<StatusBar DockPanel.Dock="Bottom">
<TextBlock Text="{Binding StatusText}"/>
</StatusBar>
...
So to recap your questions: the worker threads can access it, but they should not have to deal with sync'ing up the UI. That responsibility is the VM's. And VM should be thread-agnostic and sync the UI through the dispatcher or synchronization context or other methods.
Scheduling through the Dispatcher is appropriate if you are manipulating a collection that is the subject of a binding (e.g., an ObservableCollection); otherwise SynchronizationContext is appropriate (it's a bit more light-weight).
just add the delegate and pass a reference to your main form
public partial class MainWindow : Window
{
TCPConnection myCon = new TCPConnection();
private void connectButton_Click(object sender, RoutedEventArgs e)
{
networkListBox.Items.Add("Connecting...");
myCon.Connect("localhost", updateNetworkListBox);
}
public delegate void updateNetworkListBoxDelegate(string message);
public void updateNetworkListBox(string message)
{
if(this.invokeRequired())
{
this.invoke(new updateNetworkListBoxDelegate(updateNetworkListBox), message);
}
else
{
networkListBox.Items.Add(message);
}
}
}
in TCPConnection add a constructor that takes a MainWindow instance
public class TCPConnection
{
//add member to hold instance
private _mainWindow;
//add constructor taking instance
public TCPConnection(MainWindow mw)
{
_mainWindow = mw;
}
public void Connect(string server, ReportDelegate reportDelegate)
{
this.server = server;
clientThread = new Thread(() => Client(this.server));
clientThread.Start();
//reportDelegate("Started client thread...");
//call the method on the UI thread
_mainWindow.updateNetworkListBox("Started client thread...");
}
static void Client(string server)
{
try
{
Int32 port = 25565;
TcpClient client = new TcpClient(server, port);
Byte[] outgoingBytes = new Byte[1024];
string outgoingString = "Hello! I am " + Guid.NewGuid();
outgoingBytes = System.Text.Encoding.ASCII.GetBytes(outgoingString);
NetworkStream stream = client.GetStream();
stream.Write(outgoingBytes, 0, outgoingBytes.Length);
stream.Close();
client.Close();
//call the method ont he ui thread
_mainWindow.updateNetworkListBox("DONE!!")
}
}
}
Related
I am trying to implement a long-running background process, that periodically reports on its progress, to update the UI in a UWP app. How can I accomplish this? I have seen several helpful topics, but none have all of the pieces, and I have been unable to put them all together.
For example, consider a user who picks a very large file, and the app is reading in and/or operating on the data in the file. The user clicks a button, which populates a list stored on the page with data from the file the user picks.
PART 1
The page and button's click event handler look something like this:
public sealed partial class MyPage : Page
{
public List<DataRecord> DataRecords { get; set; }
private DateTime LastUpdate;
public MyPage()
{
this.InitializeComponent();
this.DataRecords = new List<DataRecord>();
this.LastUpdate = DateTime.Now;
// Subscribe to the event handler for updates.
MyStorageWrapper.MyEvent += this.UpdateUI;
}
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
StorageFile pickedFile = // … obtained from FileOpenPicker.
if (pickedFile != null)
{
this.DataRecords = await MyStorageWrapper.GetDataAsync(pickedFile);
}
}
private void UpdateUI(long lineCount)
{
// This time check prevents the UI from updating so frequently
// that it becomes unresponsive as a result.
DateTime now = DateTime.Now;
if ((now - this.LastUpdate).Milliseconds > 3000)
{
// This updates a textblock to display the count, but could also
// update a progress bar or progress ring in here.
this.MessageTextBlock.Text = "Count: " + lineCount;
this.LastUpdate = now;
}
}
}
Inside of the MyStorageWrapper class:
public static class MyStorageWrapper
{
public delegate void MyEventHandler(long lineCount);
public static event MyEventHandler MyEvent;
private static void RaiseMyEvent(long lineCount)
{
// Ensure that something is listening to the event.
if (MyStorageWrapper.MyEvent!= null)
{
// Call the listening event handlers.
MyStorageWrapper.MyEvent(lineCount);
}
}
public static async Task<List<DataRecord>> GetDataAsync(StorageFile file)
{
List<DataRecord> recordsList = new List<DataRecord>();
using (Stream stream = await file.OpenStreamForReadAsync())
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
// Does its parsing here, and constructs a single DataRecord …
recordsList.Add(dataRecord);
// Raises an event.
MyStorageWrapper.RaiseMyEvent(recordsList.Count);
}
}
}
return recordsList;
}
}
The code for the time check I got from following this.
As written, this code makes the app unresponsive with a large file (I tested on a text file on the order of about 8.5 million lines). I thought adding async and await to the GetDataAsync() call would prevent this? Does this not do its work on a thread aside from the UI thread? Through Debug mode in Visual Studio, I have verified the program is progressing as expected... it is just tying up the UI thread, making the app unresponsive (see this page from Microsoft about the UI thread and asynchronous programming).
PART 2
I have successfully implemented before an asynchronous, long-running process that runs on a separate thread AND still updates the UI periodically... but this solution does not allow for the return value - specifically the line from PART 1 that says:
this.DataRecords = await MyStorageWrapper.GetDataAsync(pickedFile);
My previous, successful implementation follows (most of the bodies cut out for brevity). Is there a way to adapt this to allow for return values?
In a Page class:
public sealed partial class MyPage : Page
{
public Generator MyGenerator { get; set; }
public MyPage()
{
this.InitializeComponent();
this.MyGenerator = new Generator();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
this.MyGenerator.ProgressUpdate += async (s, f) => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate ()
{
// Updates UI elements on the page from here.
}
this.MyGenerator.Start();
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
this.MyGenerator.Stop();
}
}
And in the Generator class:
public class Generator
{
private CancellationTokenSource cancellationTokenSource;
public event EventHandler<GeneratorStatus> ProgressUpdate;
public Generator()
{
this.cancellationTokenSource = new CancellationTokenSource();
}
public void Start()
{
Task task = Task.Run(() =>
{
while(true)
{
// Throw an Operation Cancelled exception if the task is cancelled.
this.cancellationTokenSource.Token.ThrowIfCancellationRequested();
// Does stuff here.
// Finally raise the event (assume that 'args' is the correct args and datatypes).
this.ProgressUpdate.Raise(this, new GeneratorStatus(args));
}
}, this.cancellationTokenSource.Token);
}
public void Stop()
{
this.cancellationTokenSource.Cancel();
}
}
Finally, there are two supporting classes for the ProgressUpdate event:
public class GeneratorStatus : EventArgs
{
// This class can contain a handful of properties; only one shown.
public int number { get; private set; }
public GeneratorStatus(int n)
{
this.number = n;
}
}
static class EventExtensions
{
public static void Raise(this EventHandler<GeneratorStatus> theEvent, object sender, GeneratorStatus args)
{
theEvent?.Invoke(sender, args);
}
}
It is key to understand that async/await does not directly say the awaited code will run on a different thread. When you do await GetDataAsync(pickedFile); the execution enters the GetDataAsync method still on the UI thread and continues there until await file.OpenStreamForReadAsync() is reached - and this is the only operation that will actually run asynchronously on a different thread (as file.OpenStreamForReadAsync is actually implemented this way).
However, once OpenStreamForReadAsync is completed (which will be really quick), await makes sure the execution returns to the same thread it started on - which means UI thread. So the actual expensive part of your code (reading the file in while) runs on UI thread.
You could marginally improve this by using reader.ReadLineAsync, but still, you will be returning to UI thread after each await.
ConfigureAwait(false)
The first trick you want to introduce to resolve this problem is ConfigureAwait(false).
Calling this on an asynchronous call tells the runtime that the execution does not have to return to the thread that originally called the asynchronous method - hence this can avoid returning execution to the UI thread. Great place to put it in your case is OpenStreamForReadAsync and ReadLineAsync calls:
public static async Task<List<DataRecord>> GetDataAsync(StorageFile file)
{
List<DataRecord> recordsList = new List<DataRecord>();
using (Stream stream = await file.OpenStreamForReadAsync().ConfigureAwait(false))
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
string line = await reader.ReadLineAsync().ConfigureAwait(false);
// Does its parsing here, and constructs a single DataRecord …
recordsList.Add(dataRecord);
// Raises an event.
MyStorageWrapper.RaiseMyEvent(recordsList.Count);
}
}
}
return recordsList;
}
Dispatcher
Now you freed up your UI thread, but introduced yet another problem with the progress reporting. Because now MyStorageWrapper.RaiseMyEvent(recordsList.Count) runs on a different thread, you cannot update the UI in the UpdateUI method directly, as accessing UI elements from non-UI thread throws synchronization exception. Instead, you must use UI thread Dispatcher to make sure the code runs on the right thread.
In the constructor get reference to the UI thread Dispatcher:
private CoreDispatcher _dispatcher;
public MyPage()
{
this.InitializeComponent();
_dispatcher = Window.Current.Dispatcher;
...
}
Reason to do it ahead is that Window.Current is again accessible only from the UI thread, but the page constructor definitely runs there, so it is the ideal place to use.
Now rewrite UpdateUI as follows
private async void UpdateUI(long lineCount)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// This time check prevents the UI from updating so frequently
// that it becomes unresponsive as a result.
DateTime now = DateTime.Now;
if ((now - this.LastUpdate).Milliseconds > 3000)
{
// This updates a textblock to display the count, but could also
// update a progress bar or progress ring in here.
this.MessageTextBlock.Text = "Count: " + lineCount;
this.LastUpdate = now;
}
});
}
In an effort to learn c#, I'm writing an application that will continuously monitor UDP traffic on a particular port and update a WPF UI text block with received packet information. The following code works (UDP handler class instance d instantiated elsewhere in scope):
public MainWindow()
{
InitializeComponent();
Task.Run(async () =>
{
using (d.receiveClient)
{
while (true)
{
var receivedResults = await d.receiveClient.ReceiveAsync();
byte[] buffer = receivedResults.Buffer;
Console.Write("Receiving Data: ");
Console.WriteLine(buffer[0].ToString());
Dispatcher.BeginInvoke(new Action(delegate
{
MyTextBlock.Text = "Rx Data: " + buffer[0].ToString();
}));
}
}
});
}
While it works, it's certainly doesn't feel idiomatic or correct. I'd like to create a Task<byte[]> that contains the async receive logic in the class that currently contains receiveClient. Problem is it doesn't execute continuously; the task will execute once, then exit. I've tried restarting the task in .ContinueWith(), etc, and while the Task can be restarted, I seem to loose the hooks into the UI. What's the best way to accomplish an event driven, continuous receive that in turn updates WPF UI components in native c# (I'd rather not use WinPcap or its .Net equivalent)?
Rather than try to stick with an always running Task/Thread and passing messages back to the main application, I ultimately opted to use the BeginReceive() and EndReceive() methods with a custom event for handling data parsing. Essentially something like the following (distilled to essentials):
public void Listen()
{
receiveClient.BeginReceive(new AsyncCallback(ProcessIncoming), null);
}
private void ProcessIncoming(IAsyncResult res)
{
byte[] rec_bytes = receiveClient.EndReceive(res, ref rec_ep);
receiveClient.BeginReceive(new AsyncCallback(ProcessIncoming), null);
PacketReceivedEventArgs args = new PacketReceivedEventArgs();
args.IP = rec_ep.Address;
args.Port = rec_ep.Port;
args.Data = rec_bytes;
OnPacketReceived(args);
}
protected virtual void OnPacketReceived(PacketReceivedEventArgs e)
{
EventHandler<PacketReceivedEventArgs> handler = PacketReceived;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<PacketReceivedEventArgs> PacketReceived;
public class PacketReceivedEventArgs : EventArgs
{
public byte[] Data { get; set; }
public IPAddress IP { get; set; }
public int Port { get; set; }
}
In the main app, connecting, listening, and packet handling becomes (where d is an instance of the class implementing the above methods):
d.Connect(someIPString, portInt);
d.PacketReceived += _SomeHandler;
d.Listen();
I want to asynchronously update UI status when doing a long-time task . The program is a console application , however , when I execute the async operations , the UI thread will exit soon after the task begins .
How should I let the UI thread wait when my long-time task finish ?
I simplify my code as below :
public static class Program
{
static void Main()
{
WorkerWrapper wp = new WorkerWrapper();
wp.ProcessData();
}
}
public class WorkerWrapper
{
private RateBar bar;
public void ProcessData()
{
bar = new RateBar();
bar.Show();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
handler.EndInvoke(ar);
}
private void wk_WorkProcess(object sender, PrecentArgs e)
{
if (e.Precent < 100)
{
bar.Precent = e.Precent;
}
}
}
public class Worker
{
public event EventHandler<PrecentArgs> WorkProcess;
public void DoWork()
{
for (int i = 0; i < 100; i++)
{
WorkProcess(this, new PrecentArgs(i));
Thread.Sleep(100);
}
}
}
public class PrecentArgs : EventArgs
{
public int Precent { get; set; }
public PrecentArgs(int precent)
{
Precent = precent;
}
}
public partial class RateBar : Form
{
public int Precent
{
set
{
System.Windows.Forms.MethodInvoker invoker = () => this.progressBar1.Value = value;
if (this.progressBar1.InvokeRequired)
{
this.progressBar1.Invoke(invoker);
}
else
{
invoker();
}
}
}
public RateBar()
{
InitializeComponent();
}
}
However , in method ProcessData() , if I add result.AsyncWaitHandle.WaitOne() in the end to wait my operation to complete , the Form will freeze .
Is there anything wrong with my way to wait the thread to complete ?
Reason that your application exiting before your "background threads" completed is when there are multiple threads application exists soon after there are not any foreground threads. This is explained more in here http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx
You should add proper waiting mechanisms to your background threads to be completed. There are multiple ways of letting other threads know that the thread is complete. Please refer here. How to wait for thread to finish with .NET?
You shouldn't block the UI thread waiting for the result, but rather retrieve the result from EndInvoke. Your deadlock probably occurs because you are using both result.AsyncWaitHandle.WaitOne() and EndInvoke, both will block until the result is available.
In my opinion the best option is to not call result.AsyncWaitHandle.WaitOne() and just retrieve the result in the AsyncCallback
private void AsyncCallback(IAsyncResult ar)
{
Action handler = ar.AsyncState as Action;
var result = handler.EndInvoke(ar);
}
More information here. Also if you are using .net 4.0 or higher, this sort of thing can be done much easier with async/await.
I write down this solution and hope it may helps others with same question .
The key to this problem is to use a new thread to run RateBar's ShowDialog function .
public void ProcessData()
{
new Thread(() => new RateBar().ShowDialog()).Start();
Worker wk = new Worker();
wk.WorkProcess += wk_WorkProcess;
Action handler = new Action(wk.DoWork);
var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
}
I want warn my page that the transmitted data is completed. I create object, add event handler and call new Thread for async transmitted data to server. When data transmitted, and recive from server answer i callback my event, but throw exception 'invalid cross-thread access'.
Why don't run my event handler?
// My page (PhoneApplicationPage)
public partial class PageStart
{
private void btn_Send_Click(object sender, RoutedEventArgs e)
{
TransmitHolder holder = new TransmitHolder();
holder.onCompleted += new TransmitHolder.CompleteHandler(onCompleted);
// transmit async
new Thread(delegate() { Transmitter(holder).Start(); }).Start();
}
private void onCompleted(object sender, byte[] answer)
{
//some code
}
}
public class TransmitHolder
{
public delegate void CompleteHandler(object sender, byte[] answer);
public event CompleteHandler onCompleted;
public void Complete(byte[] answer)
{
if (onCompleted != null)
{
onCompleted(null, answer); // here throw exception `invalid cross-thread access`
}
}
}
public class Transmitter
{
private TransmitHolder holder;
public Transmitter(TransmitHolder holder)
{
this.holder = holder;
}
// send data from server
public void Start()
{
// send data using soket
NetworkManager nm = new NetworkManager();
// method Send execute Connect, Send and Recive data from server
byte[] answer = nm.Send(Encoding.UTF8.GetBytes("hello_word"));
holder.Complette(answer); // notify, send data completed
}
}
On the Windows Phone 7 Platform, all the UI logic should be done on the UI Thread. If you attempt to change the visual tree, or set/get a property of a DependencyObject (all the UI elements are DependencyObject(s) ) on a thread different thant the dedicated UI thread, you will get an Invalid Cross thread exception.
To perform UI logic on the right thread, use the adequate dispatcher.
Deployment.Current.Dispatcher.BeginInvoke(() => { <Put your UI logic here> });
I tried to wrap the dispatcher in a thread. But the result is not what i expect. How can i solve that problem?
public void Start()
{
ThreadStart ts = inner;
Thread wrapper = new Thread(ts);
wrapper.Start();
}
private void inner()
{
_Runner.Dispatcher.Invoke(_Runner.Action, DispatcherPriority.Normal);
}
You have not shown us enough code/explained yourself well enough to be able to provide a good answer, but I'm guessing your action (_Runner.Action) is expensive and slow to execute. If so, that is why your UI is unresponsive. You're essentially telling the Dispatcher to run that expensive operation on the UI thread when what you really want to do is run as much of your operation on the background thread as possible, and then marshal back to the UI thread via the Dispatcher only when necessary.
When you fire an action through/on the dispatcher, that action is called on the UI thread.
My guess is that you are doing the work/processing in the _Runner.Action function and it is tying up the UI thread. You'll have to do the main processing part in the inner() function and then call the Dispatcher for the final update details.
If you absolutely must process on the dispatcher, break your process into smaller pieces and call Dispatcher.BeginInvoke() for each piece so other events can be processed in between your process.
You need to break Runner.Action into two parts - the long running part that does the calculation and the part that updates the GUI.
After you do that you call the long running part in the background thread and use the dispatcher only on the UI update part.
By the way, you should also probably use BeginInvoke and not Invoke.
If the long running part of Runner.Action is updating the GUI than you can't use a background thread to solve your problem - there are solutions for slow GUI operations but they change depending on what exactly you are trying to do.
Here is an example that will let you run WPF applications with multiple UI threads. I believe this will help you. Refer to this http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/
Thread lThread = new Thread(() =>
{
var lWnd = new Window1();
lWnd.Show();
lWnd.Closed += (sender2, e2) => lWnd.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
lThread.SetApartmentState(ApartmentState.STA);
lThread.Start();
Ditto what everyone here has said.
Additionally, you may want to look into using the BackgroundWorker class.
This is what I have started using for background tasks... I have not been using it long, so I don't know if there are bugs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SSA.Utility
{
public class BackgroundTaskManager : IDisposable
{
private System.Windows.Threading.Dispatcher _OwnerDispatcher;
private System.Windows.Threading.Dispatcher _WorkerDispatcher;
private System.Threading.Thread _WorkerThread;
private Boolean _WorkerBusy;
private System.Threading.EventWaitHandle _WorkerStarted = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
public BackgroundTaskManager()
{
_OwnerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerThread = new System.Threading.Thread(new System.Threading.ThreadStart(WorkerStart));
_WorkerThread.Name = "BackgroundTaskManager:" + DateTime.Now.Ticks.ToString();
_WorkerThread.IsBackground = true;
_WorkerThread.Start();
_WorkerStarted.WaitOne();
}
public Boolean IsBusy
{
get { return _WorkerBusy; }
}
public System.Windows.Threading.Dispatcher Dispatcher
{
get {
return _WorkerDispatcher;
}
}
public System.Windows.Threading.Dispatcher OwnerDispatcher
{
get
{
return _OwnerDispatcher;
}
}
private void WorkerStart()
{
_WorkerDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_WorkerDispatcher.Hooks.DispatcherInactive += WorkDone;
_WorkerDispatcher.Hooks.OperationPosted += WorkAdded;
_WorkerStarted.Set();
System.Windows.Threading.Dispatcher.Run();
}
private void WorkAdded(Object sender, System.Windows.Threading.DispatcherHookEventArgs e)
{
_WorkerBusy = true;
}
private void WorkDone(Object sender, EventArgs e)
{
_WorkerBusy = false;
}
public void Dispose()
{
if (_WorkerDispatcher != null)
{
_WorkerDispatcher.InvokeShutdown();
_WorkerDispatcher = null;
}
}
}
}
// Useage (not tested)
private SSA.Utility.BackgroundTaskManager _background = new SSA.Utility.BackgroundTaskManager();
public void LongTaskAsync()
{
_background.Dispatcher.BeginInvoke(new Action(LongTask), null);
}
public void LongTask()
{
System.Threading.Thread.Sleep(10000); // simulate a long task
_background.OwnerDispatcher.BeginInvoke(new Action<STATUSCLASS>(LongTaskUpdate), statusobject);
}
public void LongTaskUpdate(STATUSCLASS statusobject) {
}
Yes. _Runner.Action is the problem. Some long-timed methods used in the Dispatcher block. But solution is "dont use the any thread not related to UI in the dispatcher"