Avoiding to freeze UI while loading - c#

I've this class, it works fine
public partial class Home : UserControl
{
public ObservableCollection<Activity> DataGridRows { get; set; }// = new ObservableCollection<Activity>();
public Home()
{
InitializeComponent();
DataContext = this;
this.Init();
}
private void Init()
{
DataGridRows = new ObservableCollection<Activity>();
refreshGrid(null, null);
}
private void refreshGrid(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
startRefresh(); //<-- very long operation!
}));
}
}
My problem is that while calling startRefresh() the whole program is freezed, i can't click on other buttons or perform other operations until startRefresh is finished. However i want to run it on background.
Note that i can't use the Task object with the TaskScheduler.FromCurrentSynchronizationContext() method because startRefresh performs edit operations on DataGridRows and i get this exception:
System.NotSupportedException : This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

You need to move the heavy data fetching and processing off the UI thread. When the data is ready, update the UI from the UI thread. If you are using .NET 4.0 or later, the Task Parallel Library makes this sort of operation MUCH easier.
NOTE: I am making the assumption that startRefresh() both fetches data and updates the UI. You will make like much easier on yourself if the data retrieval and UI update are in separate methods.
See this answer for more detail:
Avoiding the window (WPF) to freeze while using TPL

I think you can use awaitable delegate command
public ICommand MyCommand { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MyCommand = new AwaitableDelegateCommand(refreshGrid);
}
private async Task refreshGrid()
{
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
Thread.Sleep(10000);
}));
}
You can have a look at http://jake.ginnivan.net/awaitable-delegatecommand/ for awaitable delegate command

Related

Consumer/producer locking GUI thread

I made a sample Consumer/Producer thread application, so I can learn how to properly use it.
I want it to allow for one thread to send commands to the GUI thread, to update the GUI with content.
I have it working, but there's one small issue. The GUI thread is my consumer thread, so I have it always checking for new commands (using a while loop). The issue is that because of this while loop, the GUI never displays because it's always stuck in the while loop. Note that the string Queue will eventually be replaced with a more complex object (one that holds data & command type).
I'm not sure how else to allow the GUI thread to consume commands without interrupting GUI functionality. Am I doing something wrong?
Here's my Form1.cs code (the form only contains 1 RichTextBox for showing output called OutputBox).
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
namespace MultiThreading
{
class ThreadCommandQueue
{
public static ThreadCommandQueue instance = new ThreadCommandQueue();
private Queue<string> m_queue;
private Object m_lock;
public static ThreadCommandQueue GetInstance()
{
return instance;
}
private ThreadCommandQueue()
{
m_queue = new Queue<string>();
m_lock = new Object();
}
public void Add(
string data_to_add)
{
lock (m_lock)
{
m_queue.Enqueue(data_to_add);
}
}
public string Get()
{
lock (m_lock)
{
if (m_queue.Count > 0)
{
return m_queue.Dequeue();
}
return null;
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void PopulateThreadCommandQueue()
{
int i = 0;
while(true)
{
ThreadCommandQueue.GetInstance().Add("Item #: " + i.ToString());
i++;
}
}
private void Form1_Load(object sender, EventArgs e)
{
// Create the Command Queue....
ThreadCommandQueue.GetInstance();
// Create a Testing Producer Thread
Thread ProducerThread = new Thread(PopulateThreadCommandQueue);
ProducerThread.Start();
// The GUI thread is the Consumer, so keep checking the CommandQueue for data...
while(true)
{
string latest = ThreadCommandQueue.GetInstance().Get();
if(latest != null)
{
OutputBox.Text += latest + "\n";
}
}
}
}
}
Use ConcurrentQueue. So no locking is required to add and get from Queue.
Also you in real time you will not receive commands continuously from UI thread (while loop). If you have such scenario use a separate thread to receive outcome.
Then from the receivers thread you can update UI using Invoke command, as below.
//This method called from receiver thread
public void UpdateForm(Data d)
{
if(this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() => this.UpdateFormUI(r)));
}
else
{
this.UpdateFormUI(data)
}
}
public void UpdateFormUI(Data d)
{
//Does actual ui update
}
Okay, the problem here is that you need to poll the message loop all the time to make GUI working and you also need to poll your IPC command queue all the time to make your commands working and you need this polling to happen at the same time on the same thread.
There are multiple ways to resolve this, but the easiest would be to process the message loop and when there is nothing to process, do your IPC command queue processing. For WinForms application this would be something like:
public Form1()
{
InitializeComponent();
Application.Idle += (sender, eargs) => ProcessCommands();
}
private void ProcessCommands()
{
while(true)
{
string latest = ThreadCommandQueue.GetInstance().Get();
if(string.IsNullOrEmpty(latest)) return;
OutputBox.Text += latest + "\n";
}
}

Update progress bar in another form while task is running

**Ultimately I am going to have four tasks running concurrently and have another form that contains four progress bars. I would like for each progress bar to update as it's work task is completing.
Here's what I'm trying to do for starters.
I have a form that has some buttons on it. When I click one I'm creating a new task to do some work.
public partial class MyMainForm : Form
{
private void btn_doWork_Click(object sender, EventArgs e)
{
Task task = new Task(RunComparisons);
task.Start();
}
private void RunComparisons()
{
int progressBarValue = 0;
MyProgressBarForm pBar = new MyProgressBarForm(maxValue, "some text");
pBar.ShowDialog();
foreach(string s in nodeCollection)
{
//do some work here
progressBarValue++;
pBar.updateProgressBar(progressBarValue, "some new text");
}
pBar.BeginInvoke(new Action(() => pBar.Close()));
}
}
In another class that contains a form with a progress bar:
public partial class MyProgressBarForm : Form
{
public MyProgressBarForm(int maxValue, string textToDisplay)
{
InitializeComponent();
MyProgressBarControl.Maximum = maxValue;
myLabel.Text = textToDisplay;
}
public void updateProgressBar(int progress, string updatedTextToDisplay)
{
MyProgressBarForm.BeginInvoke(
new Action(() =>
{
MyProgressBarControl.Value = progress;
myLabel.Text = updatedTextToDisplay;
}));
}
When I click the doWork button the progress bar form displays but doesn't update. It just sits there and hangs. If I comment out the pBar.ShowDialog(); then the progress bar form doesn't display but the work to be done is run to completion perfectly.
I had this working perfectly when I was creating my own threads but I read about Tasks and now I'm trying to get this to run that way. Where did I go wrong?
The TPL adds the IProgress interface for updating the UI with the progress of a long running non-UI operation.
All you need to do is create a Progress instance in your UI with instructions on how to update it with progress, and then pass it to your worker which can report progress through it.
public partial class MyMainForm : System.Windows.Forms.Form
{
private async void btn_doWork_Click(object sender, EventArgs e)
{
MyProgressBarForm progressForm = new MyProgressBarForm();
progressForm.Show();
Progress<string> progress = new Progress<string>();
progress.ProgressChanged += (_, text) =>
progressForm.updateProgressBar(text);
await Task.Run(() => RunComparisons(progress));
progressForm.Close();
}
private void RunComparisons(IProgress<string> progress)
{
foreach (var s in nodeCollection)
{
Process(s);
progress.Report("hello world");
}
}
}
public partial class MyProgressBarForm : System.Windows.Forms.Form
{
public void updateProgressBar(string updatedTextToDisplay)
{
MyProgressBarControl.Value++;
myLabel.Text = updatedTextToDisplay;
}
}
This lets the Progress Form handle displaying progress to the UI, the working code to only handle doing the work, the main form to simply create the progress form, start the work, and close the form when done, and it leaves all of the work of keeping track of progress and marhsaling through the UI thread to Progress. It also avoids having multiple UI thread; your current approach of creating and manipulating UI components from non-UI threads creates a number of problems that complicates the code and makes it harder to maintain.
Create your progress bar form on the main UI thread of the parent form, then call the Show() method on the object in your button click event.
Here's an example with 2 bars:
//In parent form ...
private MyProgressBarForm progressBarForm = new MyProgressBarForm();
private void button1_Click(object sender, EventArgs e)
{
progressBarForm.Show();
Task task = new Task(RunComparisons);
task.Start();
}
private void RunComparisons()
{
for (int i = 1; i < 100; i++)
{
System.Threading.Thread.Sleep(50);
progressBarForm.UpdateProgressBar(1, i);
}
}
//In MyProgressBarForm ...
public void UpdateProgressBar(int index, int value)
{
this.Invoke((MethodInvoker) delegate{
if (index == 1)
{
progressBar1.Value = value;
}
else
{
progressBar2.Value = value;
}
});
}
.ShowDialog is a blocking call; execution won't continue until the dialog returns a result. You should probably look in to a BackgroundWorker to process the work on another thread and update the dialog.

Communication between threads via delegates?

I am looking for a solution for interthread communication.
Thread A is the main thread of a windows app. I starts a Thread B that is working independant of thread a, they do not share code. But thread A has to get some feedback about status of thread b. I try to solve this with a delegate.
I am very sorry, I forgot to add that I have to work on .net 3.5, c#, WEC7
It is important that the code
public void OnMyEvent(string foo)
{
MessageBox.Show(foo);
}
is executed in context of thread a, how can I achieve this
public partial class Form1 : Form
{
//...
public void StartThread(Object obj)
{
new ClassForSecondThread(obj as Parameters);
}
private void button1_Click(object sender, EventArgs e)
{
//ParameterizedThreadStart threadstart = new ParameterizedThreadStart(startThread);
ParameterizedThreadStart threadstart = new ParameterizedThreadStart(StartThread);
Thread thread = new Thread(threadstart);
Parameters parameters = new Parameters(){MyEventHandler = OnMyEvent};
thread.Start(parameters);
}
public void OnMyEvent(string foo)
{
MessageBox.Show(foo);
}
}
//This code is executed in Thread B
public class ClassForSecondThread
{
public ClassForSecondThread(Parameters parameters)
{
if (parameters == null)
return;
MyEventhandler += parameters.MyEventHandler;
DoWork();
}
private void DoWork()
{
//DoSomething
if (MyEventhandler != null)
MyEventhandler.DynamicInvoke("Hello World");// I think this should be executed async, in Thread A
Thread.Sleep(10000);
if (MyEventhandler != null)
MyEventhandler.DynamicInvoke("Hello World again"); // I think this should be executed async, in Thread A
}
public event MyEventHandler MyEventhandler;
}
public class Parameters
{
public MyEventHandler MyEventHandler;
}
public delegate void MyEventHandler(string foo);
As you want to call the MessageBox on the main UI thread, you can achieve what you want using Control.Invoke.
Invoke((MethodInvoker)(() => MessageBox.Show(foo)));
The Invoke method can be called directly on the Form and you won't be in the context of Thread B within the delegate - the code will run on the same thread as the Form.
EDIT:
OP question: if I understood Control.Invoke correctly, it always acts in the context of a control?
Although the Invoke method uses a Control (in this case the form) to get a handle to the UI thread it is running on, the code within the delegate is not specific to the UI. If you want to add more statements and expand it to include more stuff, just do this:
string t = "hello"; //declared in the form
//Thread B context - Invoke called
Invoke((MethodInvoker)(() =>
{
//Back to the UI thread of the Form here == thread A
MessageBox.Show(foo);
t = "dd";
}));
Also, if you are updating things in a multi threaded environment where the data is accessible to more than one thread, then you will need to investigate sychronization - applying locks to data etc.
For what it is worth you can simplify your code considerably by using the new async and await keywords in C# 5.0.
public class Form1 : Form
{
private async void button1_Click(object sender, EventArgs args)
{
OnMyEvent("Hello World");
await Task.Run(
() =>
{
// This stuff runs on a worker thread.
Thread.Sleep(10000);
});
OnMyEvent("Hello World again");
}
private void OnMyEvent(string foo)
{
Message.Show(foo);
}
}
In the code above OnMyEvent is executed on the UI thread in both cases. The first call be executed before the task starts and the second call will be executed after the task completes.

Windows forms, loops and threading

I want to make server application. In the beginning it should make thread for organizing every connection and write logs in Listbox. I have problem because i don't know where can i make new thread which would have access to Form1.Listbox1. This is what i tried:
public class ServerLoop
{
Form1 form1;
public ServerLoop(Form1 f)
{
form1 = f;
}
public void loop()
{
form1.addConsoleMessage("test");
}
}
And Form1 class:
public partial class Form1 : Form
{
public Thread tServerLoop;
public ServerLoop serverLoop;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
console.Items.Clear();
players.Items.Clear();
players.Items.Add("Witaj w serwerze");
addConsoleMessage("test");
serverLoop = new ServerLoop(this);
tServerLoop = new Thread(serverLoop.loop);
tServerLoop.Start();
}
private void connectButton_Click(object sender, EventArgs e)
{
}
public void addConsoleMessage(String msg)
{
console.Items.Add(msg);
}
}
Anyone knows what can i do to acheive this?
Well, you could use Invoke to marshal a delegate back onto the UI thread where that ListBox can be safely accessed.
public void loop()
{
form1.Invoke(new Action(
() =>
{
form1.addConsoleMessage("test");
}));
}
But alas, this option is inferior. Actually, these marshaling techniques are generally terrible. Do not get me wrong. There is a time and place for Invoke (and the like), but this, like many situations, is not one of them.
The code is ugly because you have to sprinkle Invoke calls all over the place.
It forces you into a design where the UI thread and worker thread are tightly coupled.
The worker thread is dictating the update frequency of the UI.
It is inefficient.
It can flood the UI message queue (at least it could with BeginInvoke).
The worker thread has to wait for a response from the UI thread before it can proceed (it will with Invoke anyway).
So how would I solve this problem? Well, with the boring old System.Windows.Forms.Timer and the fancy new ConcurrentQueue<T> of course.
public partial class Form1 : Form
{
private ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
console.Items.Clear();
console.Items.Add("test");
players.Items.Clear();
players.Items.Add("Witaj w serwerze");
Task.Factory.StartNew(
() =>
{
while (GetSomeCondition())
{
string value = GetSomeValue();
queue.Enqueue(value);
}
});
}
private void YourTimer_Tick(object sender, EventArgs e)
{
string value;
while (queue.TryDequeue(out value)
{
console.Items.Add(value);
}
}
}
So what do we have now.
It looks elegant.
Our background task knows only about a queue. The tight coupling has been broken.
The UI thread is now dictating the update frequency...the way it should be.
It is a lot more efficient.
There is no chance that the UI message queue will get flooded.
And finally, the worker can speed along merrily completely unware of what the UI thread is doing.
This solution is not completely devoid of disadvantages though. Now that we have our worker thread speeding along it is possible that it produces more items for the queue then what the UI thread can consume. It would not typically be a problem, but there are techniques for dealing with that.

Closing a window owned by a different thread

I am new to threading. I am using background threads in my WPF Application to talk to the DB and message communication.
One of the view models should open a separate window. Since this should Run as a UI thread, I am doing:
private void OnSelection(SelectionType obj)
{
Thread thread = new Thread(ShowRegionWindow);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private void ShowRegionWindow()
{
var rWindow = new RegionWindow();
rWindow .Show();
rWindow .Closed += (s, e) => System.Windows.Threading.Dispatcher.ExitAllFrames();
System.Windows.Threading.Dispatcher.Run();
}
Now I need to close this window on another message. How do I do that?
Before I go any further, you said you are new to threading and I want to stress that there is probably no good reason for your application to open windows on different threads. It is good that you are using MVVM, but you may not be doing it right. Ideally, all your views and view models would be on the main UI thread. Any worker threads in your model layer need to invoke the UI dispatcher before interacting with a view model. For instance, you might have an update event on a worker thread call a handler on the view model to update the UI. The UI dispatcher should either be invoked immediately before or after that event is invoked. (To be clear though, the model should not know about the view model.)
In fact, you seem to be creating a new Window in a UI event handler which means you should probably just do this:
private void OnSelection(SelectionType obj)
{
var rWindow = new RegionWindow();
rWindow.Show();
}
However, maybe you have a perfectly legitimate reason for doing it the way you are. If so, one way you could close that new window from the calling thread would be to pass in an event. You could do something like this:
private event Action CloseRegionWindows = delegate { }; // won't have to check for null
private void OnSelection(SelectionType obj)
{
Thread thread = new Thread(() => ShowRegionWindow(ref CloseRegionWindows));
...
}
private void ShowRegionWindow(ref Action CloseRegionWindows)
{
var rWindow = new RegionWindow();
rWindow.Show();
CloseRegionWindows += () => rWindow.Dispatcher.BeginInvoke(new ThreadStart(() => rWindow.Close()));
...
}
And then raise that event somewhere:
private void OnClick(object sender, RoutedEventArgs args)
{
CloseRegionWindows();
}
After reading some of your comments again, I think I have a better understanding of the scenario. Here's what you need to do.
First, be sure that one of your ViewModels has a reference to the Model that needs to open and close a window. One way to accomplish that is constructor dependency injection.
public ViewModel(Model model) // or IModel
{
...
Next, you'll need to capture the UI dispatcher in that ViewModel. The best place for this is probably also the ViewModel constructor.
private Dispatcher dispatcher;
public ViewModel(Model model)
{
dispatcher = Dispatcher.CurrentDispatcher;
...
Now create two events in your Model; one to open and one to close the window.
class Model
{
internal event Action OpenWindow = delegate { };
internal event Action CloseWindow = delegate { };
...
And subscribe to them in your ViewModel constructor.
public ViewModel(Model model)
{
dispatcher = Dispatcher.CurrentDispatcher;
model.OpenWindow += OnWindowOpen;
model.CloseWindow += OnWindowClose;
...
}
Now open and close your window with the UI Dispatcher in the ViewModel class;
private Window window;
private void OnWindowOpen()
{
// still on background thread here
dispatcher.BeginInvoke(new ThreadStart(() =>
{
// now we're on the UI thread
window = new Window();
window.Show();
}
}
private void OnWindowClose()
{
dispatcher.BeginInvoke(new ThreadStart(() =>
{
window.Close();
}
}
Finally, raise the OpenWindow and CloseWindow events from your background thread in your Model, just as you would raise any event. Your Model might look something like this:
class Model
{
private Thread worker;
internal event Action OpenWindow = delegate { };
internal event Action CloseWindow = delegate { };
public Model()
{
worker = new Thread(Work);
worker.Start();
}
private void Work()
{
while(true)
{
if (/*whatever*/) OpenWindow();
else if (/*whatever*/) CloseWindow();
}
}
}

Categories