Update data information on the screen/display - c#

I want my application updates the screen information while the code is executing. I tried with this.LayoutRoot.UpdateLayout(); but it didn't work and I don't understand why. Can anyone help me?
The application receive a button propriety from a button clicked, then use that to a several things in a Model class, and then I want it show a message to the user. after that i want it continue executing more things (AI)... :S
public void showMsgFromModel(string player, string msg)
{
if(player!="")
txNomeMsg.Text = player + ":";
else
txNomeMsg.Text = player;
txMsg.Text = msg;
this.LayoutRoot.UpdateLayout();
System.Threading.Thread.Sleep(1500);
}

You're trying to do some processing from the UI thread, which is therefore unable to update the interface.
Use a background worker to execute long-running tasks, and use the dispatcher when you need to update the UI:
var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
Thread.Sleep(1500); // Some processing
Dispatcher.BeginInvoke(() => txMsg.Text = "Hello"); // Update the UI
Thread.Sleep(1500); // More processing
};
worker.RunWorkerAsync();

Related

Progress Bar with Label — Unable to Update Label in ProgressChanged

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.
You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

Try to write from the background worker to a textbox in the GUI

I've spend now over 2 hours trying to resolve this issue and it would be awesome if someone could help me .. :)
Basically, what I try to to is an application which queries a database with LINQ and webservices and retrieves informationen.
From the result I extract few informations and write them to a .CSV file.
Everything works perfectly, except the logging.
Since I dont want that my UI frezzes, I've implemented a background worker:
I hand my logger textbox over to the background worker, that I write the progress from my static webservice methods.
using TextBoxAsAlias = System.Windows.Controls.TextBox;
Here I write for the first time to the logger textbox, which works absolutely perfect.
private void btnExecute_Click_1(object sender, RoutedEventArgs e)
{
// Register background worker
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
// Flush the content of the logger textbox
txtboxLogger.Clear();
txtboxLogger.AppendText("#########################" + Environment.NewLine);
txtboxLogger.AppendText("# Logger - " + DateTime.Now.ToString("T") + "#" +
txtboxLogger.AppendText("#########################" + Environment.NewLine + Environment.NewLine);
worker.RunWorkerAsync(new Object[] {txtboxLogger });
}
Here is where I get the problem:
As you can see I try again to log some text in the DoWork method.
The problem is, that the whole text will just be logged when the worker_DoWork has finished.
So as result I wait for 5minutes and nothing happens in the logger textbox and as soon as it finished, everything gets written at once.
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Object[] parameter = (Object[])e.Argument;
TextBoxAsAlias textboxLogger = (TextBoxAsAlias)parameter[0];
textboxLogger.Dispatcher.InvokeAsync((Action)(() =>
{
txtboxLogger.AppendText(DateTime.Now.ToString("T") + " - Start processing ... " + Environment.NewLine);
if (isAutoSelection)
{
// Execute Webservice with auto selection
RandomDoWorkMethod(null, context, credential, dateStart, textboxLogger);
}
else
{
// Read în the Numbers + hand it over to the webservice for further computing
RandomDoWorkMethod(ReadInputFile(), context, credential, dateStart, textboxLogger);
}
}));
}
Does anyone know how I can write immediately during the background Worker to the log file and not just at the end?
I make further use of the following code in my other methods, but the result is still the same, because all of them are in the worker_DoWork thread.
textboxLogger.Dispatcher.InvokeAsync((Action)(() =>
{
))};
Thanks in advance for your help.
Regards
George
Use SynchronizationContext of your UI thread.
Global variable:
private System.Threading.SynchronizationContext _uiSyncContext;
In constructor:
this._uiSyncContext = System.Threading.SynchronizationContext.Current;
Working with UI elements in worker_DoWork method:
this._uiSyncContext.Post(
delegate(object state)
{
txtboxLogger.AppendText(state as string);
},
"Your Text"
);
You can also use Send (Synchronous) method instead of Post (Asynchronous).
Using the dispatcher like that pushes all the work back to the UI, you should only use the dispatcher to refresh the text every now and then, never push RandomDoWorkMethod using the dispatcher.
Also consider using binding (also see notes on BackgroundWorker and ProgressChanged).
This sounds like a standard scenario for using the ProgressChanged event.
A BackgroundWorker thread cannot directly communicate with the UI thread, but it does expose an event that can. Here is how you set this up:
In your "Register background worker" section, add the following lines:
worker.WorkerReportsProgress = true;
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
You need to define the handler somewhere inside your class to look something like this:
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 0)
{
string message = DateTime.Now.ToString("T") + " - Start processing ... ";
txtboxLogger.AppendText(message + Environment.NewLine);
}
}
Notice, we're assuming a progress of 0 means we've just started our process. This value needs to be sent inside the DoWork event handler. So your DoWork method will now look like this:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(0);
if (isAutoSelection)
{
// Execute Webservice with auto selection
RandomDoWorkMethod(null, context, credential, dateStart, textboxLogger);
}
else
{
// Read în the Numbers + hand it over to the webservice for further computing
RandomDoWorkMethod(ReadInputFile(), context, credential, dateStart, textboxLogger);
}
}

How to set text to a TextBlock from a backgroundworker

I want to show in realtime (updating every 1s for example) some temperatures to my program's interface.
To do this I believe I'm going to need to run some code in a background worker so the main program doesn't get blocked. My question here is if it's possible to set the text of a TextBlock from a background worker and if yes, how to do it.
This is the basic idea:
backgroundworker
{
while(true)
{
//reading and updating temperatures
//.....
}
}
BackgroundWorker has built in support for reporting the current progress of the work, which sounds like it's exactly what you're doing:
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (s, args) =>
{
while (true)
{
Thread.Sleep(1000);//placehodler for real work
worker.ReportProgress(0, "Still working");
}
};
worker.ProgressChanged += (s, args) =>
{
textBox1.Text = args.UserState as string;
};
worker.RunWorkerAsync();
By leveraging the built in support you allow the background worker to handle marshaling to the UI thread. (It will ensure that all of the events besides DoWork run in the UI thread.)
This also has the advantage of separating the UI logic from the business logic, rather than embedding code for manipulating the UI all throughout code doing business work.

C# XAML GUI Freezes Because of API Function

I have a program that has stock quotes pushed to me via an API. The program also has a front end, made in XAML, that freezes while this program is running (i.e. processing the information that the API is sending me). I've tried using Dispatcher.Invoke and/or BackgroundWorker and have read up on threading plenty, but can't get it to unfreeze. Perhaps I'm just doing something wrong. I've attached the relevant code here. Was hoping someone could help.
private void QuoteUpdate(QuoteInfo info)
{
BackgroundWorker bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate.WorkerSupportsCancellation = true;
bwQuoteUpdate.DoWork += bwQuoteUpdate_DoWork;
bwQuoteUpdate.RunWorkerAsync(info);
}
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
QuoteInfo info = e.Argument as QuoteInfo;
//logical functions and work are here
}));
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error in QuoteUpdate: " + ex.Message, "Exception Thrown");
}
}
Although you’re creating a BackgroundWorker with the intention of executing your long-running task on a background thread, you’re still dispatching all your processing back onto the UI thread.
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
}));
}
What you need to do is first perform your calculations on the background thread, but do not update any UI components; rather, store all your results in local variables. Then, once that’s done, use the Dispatcher.Invoke to dispatch control back to the UI thread, and use the results stored in your local variables to update your UI.
For example:
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
QuoteInfo info = e.Argument as QuoteInfo;
string result = PerformLongRunningProcessing(info);
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
this.resultTextBox.Text = result;
}));
}
Yes, you are doing something wrong. The computation should be done in thread alone add only UI changes should be done in Dispatcher.Invoke.
And if you use DataBinding through INotifyPropertyChange, then drop the Dispatcher.Invoke completly, because marshaling the changes to UI thread is done automaticaly.
Try
Dispatcher.BeginInvoke(...)

Pause execution of a method without locking GUI. C#

I'm working on a card game in C# for a project on my Intro to OOP paper and have got the game working now but am adding "flair" to the GUI.
Currently cards are dealt and appear on the UI instantaneously. I want to have to program pause for a moment after dealing a card before it deals the next.
When a game is started the following code runs to populate the PictureBoxes that represent them (will be a loop eventually):
cardImage1.Image = playDeck.deal().show();
cardImage2.Image = playDeck.deal().show();
cardImage3.Image = playDeck.deal().show();
cardImage4.Image = playDeck.deal().show();
cardImage5.Image = playDeck.deal().show();
...
I have tries using System.Threading.Thread.Sleep(100); between each deal().show() and also inside each of those methods but all it achieves is locking up my GUI until all of the sleeps have processed then display all of the cards at once.
I have also tried using a combination of a timer and while loop but it resulted in the same effect.
What would be the best way of achieving the desired result?
The problem is that any code that you run on the UI will block the UI and freeze the program. When your code is running (even if it's running Thread.Sleep), messages (such as Paint or Click) sent to the UI will not be processed (until control returns to the message loop when you exit your event handler), causing it to freeze.
The best way to do this is to run on a background thread, and then Invoke to the UI thread between sleeps, like this:
//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
//This code runs on a backround thread.
//It will not block the UI.
//However, you can't manipulate the UI from here.
//Instead, call Invoke.
Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
//etc...
});
//The UI thread will continue while the delegate runs in the background.
Alternatively, you could make a timer and show each image in the next timer tick. If you use a timer, all you should do at the beginning is start the timer; don't wait for it or you'll introduce the same problem.
Normally I'd simply recommend a function like this to perform a pause while allowing the UI to be interactive.
private void InteractivePause(TimeSpan length)
{
DateTime start = DateTime.Now;
TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
while(true)
{
System.Windows.Forms.Application.DoEvents();
TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
if (remainingTime > restTime)
{
System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
// Wait an insignificant amount of time so that the
// CPU usage doesn't hit the roof while we wait.
System.Threading.Thread.Sleep(restTime);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
if (remainingTime.Ticks > 0)
System.Threading.Thread.Sleep(remainingTime);
break;
}
}
}
But there seems to be some complication in using such a solution when it is called from within an event handler such as a button click. I think the system wants the button click event handler to return before it will continue processing other events because if I try to click again while the event handler is still running, the button depresses again even though I'm trying to drag the form and not click on the button.
So here's my alternative. Add a timer to the form and create a dealer class to handle dealing with cards by interacting with that timer. Set the Interval property of the timer to match the interval at which you want cards to be dealt. Here's my sample code.
public partial class Form1 : Form
{
CardDealer dealer;
public Form1()
{
InitializeComponent();
dealer = new CardDealer(timer1);
}
private void button1_Click(object sender, EventArgs e)
{
dealer.QueueCard(img1, cardImage1);
dealer.QueueCard(img2, cardImage2);
dealer.QueueCard(img3, cardImage1);
}
}
class CardDealer
{
// A queue of pairs in which the first value represents
// the slot where the card will go, and the second is
// a reference to the image that will appear there.
Queue<KeyValuePair<Label, Image>> cardsToDeal;
System.Windows.Forms.Timer dealTimer;
public CardDealer(System.Windows.Forms.Timer dealTimer)
{
cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
dealTimer.Tick += new EventHandler(dealTimer_Tick);
this.dealTimer = dealTimer;
}
void dealTimer_Tick(object sender, EventArgs e)
{
KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
cardInfo.Key.Image = cardInfo.Value;
if (cardsToDeal.Count <= 0)
dealTimer.Enabled = false;
}
public void QueueCard(Label slot, Image card)
{
cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
dealTimer.Enabled = true;
}
}
The cheap way out would be to loop with calls to Application.DoEvents() but a better alternative would be to set a System.Windows.Forms.Timer which you would stop after the first time it elapses. In either case you'll need some indicator to tell your UI event handlers to ignore input. You could even just use the timer.Enabled property for this purpose if it's simple enough.
I would try puting the code that deals the deck ( and calls Thread.Sleep) in another thread.

Categories

Resources