How to force control to immediately update itself in Android? - c#

I have a situation when I need to redraw control immediately. Here is one basic example:
editText.setText("some text");
// some code to redraw control immediately.
Thread.sleep(10000);
Normally, control will be updated when this method exits, at the event processing stack frame, after 10 minutes late. Is there some way to process this event out of order, in top of the current stack. In C# I would achieve this by calling Application.DoEvents().
I know that this is normally achieving by doing long execution async, but in simple case like this it would be unnecessary complication.

Is there some way to process this event out of order, in top of the current stack
No, for the simple reason that you are tying up the main application thread, which processes the events.

Related

Is there a way to not have a WPF MainWindow method terminating to avoid threading?

I'm currently working on a WPF project that is trying to continuously update a listbox based on a network streamed source.
It is my understanding that a initializeComponent() method will only actually display the WPF window once the MainWindow() method has terminated. However I am trying to have a while(true) loop inside it in order to continuously listen for update signals from a server and update the listbox with appropriate values.
Each individual method is working fine, it's just that it doesn't open the WPF window in its current form due to the while loop. I am trying to avoid using a background update thread in order to update the list box because I am aware that I would have to implement functionality in order to pass the thread "ownership" of the listbox to the thread and I'm not 100% sure of how to do this.
Is there a workaround, or better yet, is there something obvious that I'm missing to achieve my required functionality?
The code is as follows:
public MainWindow()
{
TcpClient client = new TcpClient();
client.Connect(serverAddress, port);
NetworkStream stream = client.GetStream();
numberOfPumps = 0; //initialize as 0 on startup.
handshake(stream);
InitializeComponent();
updatePumpList(stream);
updateListBox();
while(true)
{
updatePumpList(stream);
updateListBox();
}
}
The updateListBox() method is simply adding items to the listbox from a dictionary.
private void updateListBox()
{
foreach(KeyValuePair<string, PumpItem> kvp in pumpDict)
{
pumpListBox.Items.Add(kvp.Key + ": " + kvp.Value.state);
}
}
Unfortunately, you just can't do it the way you want to. In a Windows application, you must let the main (UI) thread run. Any looping you do on that thread will hang the whole application until it's done. If the window's up, that looks like the window is frozen (because it is). If it's not up yet, it looks like a wait cursor, forever. No way around that. The thread has to be left alone to process input, update the window, etc. Even manually pumping the message loop (anybody remember MFC?) is a poor expedient. Windows applications work best if you leave the main thread to do its thing as the designers intended.
We do a lot of stuff on the main thread of course, but it's quick stuff that hands control back before the user notices any latency. Synchronous Internet access is never quick enough, and a polling loop that lasts for the lifetime of your process is out of the question.
You've got two options here, both of them pretty anodyne in practice.
You could use a DispatchTimer, with asynchronous internet access.
The other is the worker thread you're trying to avoid. They're not that bad. Just keep a reference to the Thread object around to abort on program shutdown, and the thread has to "invoke into" the UI thread when it does anything that'll touch any UI (including setting any property that will raise a PropertyChanged event).
It's not a big deal at all:
Action act = () => Status = newStatus;
App.Current.Dispatcher.Invoke(act);
Have you considered setting up an onLoad event handler for your WPF window which will trigger once the WPF window is displayed? The event handler could then run your while loop accordingly.
Alternatively, you could have a timer primed to fire an event a few moments after the end of the constructor, allowing the window to display and then the while loop to begin.

Executing Code behind before Xaml Rendering - How to influence the sequence

I'm working on a Windows Phone App.
I have a very performance intensive method which takes several seconds until the operation is done.
When the method is called I want to show an animated loading symbol which is defined in the xaml of my view. When the operation is finished it should disappear. I set the loading symbol to visible in the first line of this method.In the last line I set the visibility to collapsed.
The problem is that at first the whole code behind will be executed. Unfortunately nothing is to be seen, because the the visibiliy is set to visible after the code behind operations are executed and in the same moment its set to collapsed.
Has anybody an idea how to solve this problem? Thanks so much in advance.
The problem you have is that you're calling your method on the main (UI) thread. This means that your method blocks the UI from refreshing, and also means that (as you noted) by the time the UI does refresh, you've already hidden the icon again.
What you need to do instead is call your method on a background thread (there are a number of ways to handle this). You will need to push the UI update to the UI thread (using Dispatcher.Invoke), but the rest of your method will run on a separate thread.
You'll also need to use a callback of some kind - maybe a custom event - so that your UI thread knows when the background thread is completed.
Without seeing the code its hard to say for sure but if you use the dispatcher to run you intensive code after the busy indicator is set this would allow the ui thread time to change before running the code.
An example
//This assumes you are binding in xaml to the isbusy and it implements INotifyPropertyChanged
IsBusy = true;
Dispatcher.BeginInvoke(()=>{ //...performance intense here
});
That being Said Dan Puzey is right. You should only run this logic on the UI thread if for some reason your need to. even then be wary of this as it makes for a poor ui experience.
One way you could accomplish this and still have your dispatcher fire off when you need would be to pass a copy of the dispatcher into the background.
ThreadPool.QueueUserWorkItem (d => {
//...performance intense here
Dispatcher dispatcher = d as Dispatcher;
if(dispatcher != null){
dispatcher.BeginInvoke()()=>{//...ui updates here }
}
}, Dispatcher.CurrentDispatcher);//make sure this is called from your UI thread or you may not end up with the correct dispatcher

Why does Refresh() not do what DoEvents() does?

I am trying to understand a certain longstanding concept in Windows Forms re: UI programming; following code is from Chris Sells' Windows Forms Programming book (2nd Ed., 2006):
void ShowProgress(string pi, int totalDigits, int digitsSoFar) {
// Display progress in UI
this.resultsTextBox.Text = pi;
this.calcToolStripProgressBar.Maximum = totalDigits;
this.calcToolStripProgressBar.Value = digitsSoFar;
if( digitsSoFar == totalDigits ) {
// Reset UI
this.calcToolStripStatusLabel.Text = "Ready";
this.calcToolStripProgressBar.Visible = false;
}
// Force UI update to reflect calculation progress
this.Refresh();
}
This method is part of small sample application that has another long-running method which calculates Pi. Each time a cluster of digits are calculated, ShowProgress() is called to update the UI. As explained in the book, this code is the "wrong" way of doing things, and causes the UI to freeze when the application is minimized and then brought into the foreground again, causing the system to ask the application to repaint itself.
What I don't understand: Since this.Refresh() is being called repeatedly, why doesn't it process any system repaint event that is waiting for attention?
And a follow-up question: When I add Application.DoEvents() immediately following this.Refresh(), the freeze-up problem disappears. This is without having to resort to Invoke/BeginInvoke, etc. Any comments?
Basically, the reason for this is the way Windows handles messages - it does this in a synchronous way in an internal message loop.
The point is that there was a message that triggered your code. For example a button click. Your application is in the middle of handling the message. From within this handler, you force the refresh which puts another WM_PAINT in the message queue. When your handler finishes, the message loop will surely pick it up and dispatch, thus repainting the control. But your code is not finished, in fact it loops calling your ShowProgress, causing WM_PAINT being queued forever.
On the other hand, the DoEvents() causes an independent instance of the message loop to fire. It's fired from within your code which means that the call stack looks like this:
outer message loop -> your code -> inner message loop.
The inner message loop processes all pending messages, including the WM_PAINT (thus the control is redrawn) but it is dangerous - as it will dispatch all other pending messages, including button clicks, menu clicks or event closing your application with the X at the top-right corner. Unfortunately, there's no easy way to make the loop to process the WM_PAINT only which means that calling DoEvents() exposes your application to subtle potential problems involving unexpected user activity during the execution of your code which triggers the DoEvents.

Form Visiblity Problem

Form1.button_Click(...) {
// Show a dialog form, which runs a method <CheckBalance()> on it's OnLoad Event.
var modemDialog = new ModemDialog("COM25");
modemDialog.ShowDialog();
// the user can't see this dialog form until the method <CheckBalance()> terminates.
}
Is it possible to show first the dialog then run the specified method?
THanks.
That is correct and expected. Winforms UI is inherently single-threaded. Having a function call like "CheckBalance" in the form load event will prevent the form from showing until the form load event completes. Depending on the duration of the task, you have a number of options available to you:
If it's a fast task, compute it ahead of time before showing the form
If it's something the user may want to initiate, move it to a button on the new form, so it's only calculated on the request of the user
If it's a long running task that takes some time, you'll need to move it off in to another thread. Using a BackgroundWorker is recommended.
OnLoad occurs before the form is shown to allow you to initialise the form and variables and what not, which means it is synchronous. The form will not show until you return from that function.
If you want to asynchronously run the CheckBalance() method, then you can use a few techniques, such as utilising the Threading, ThreadPool or Tasks API to shift that work to a background thread, and returning immediately so that the form is shown.
Here is an example of using a Task to perform the same action, but asynchronously so that the form immediately shows:
Action<object> action = () => { CheckBalance(); };
new Task(action).Start();
Please note that if you access the UI thread, you'll need to beware of thread-safety and invocation.
The simple way to make sure your form is visible before CheckBalance is run is to use this code in the form load handler:
this.BeginInvoke((Action)(() => this.CheckBalance()));
This will push the execution of the CheckBalance method onto the UI thread message pump so will execute after all preceding UI code is complete.
Others are correct though that the UI will still be blocked as CheckBalance executes. You probably want to run it on a background thread to prevent this.

WinForm And Looping

I have a WinForm set up and a process that loops until a button is pressed on the form.
When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?
If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.
If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,
You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.
Don't do that.
Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.
Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)
The correct solution is to do one of the following:
Use a Timer
Use a BackgroundWorker
Use a ThreadPool
Another solution that would work, but is not a good idea is:
Use Application.DoEvents() -- but please don't actually do this
Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.
You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.
The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.
About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.
You can use the form load event to trigger the start of the loop.
So it would handle the event Me.Load
However is it necessary for your loop to be happening inside of the UI?
This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.
However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.
You should run your loop in a background thread using the BackgroundWorker component.
Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.
You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.

Categories

Resources