I have a WPF application that connects and disconnects to WiFi.
It takes around 2 seconds to complete the process. During that time, I want to show a simple waiting message on the top of my current window "Connecting.." or "Disconnecting...", which closes as soon is the process is complete.
What should I use?
MessageBox is not working because a) it has a button, and b) I can't just close it at my own will through code (I think).
When the Wi-Fi starts to connect, use:
MyWindow popup = new MyWindow();
popup.ShowDialog();
Where MyWindow is a small form containing information.
And when the Wi-Fi is connected, use:
popup.Close();
ShowDialog() prevents user interaction with the parent form.
You should do the long-running task that takes two seconds to complete on a background thread and show and close the window on the dispatcher thread, e.g.:
Window window = new Window()
{
WindowStyle = WindowStyle.None,
Content = new TextBlock { Text = "working..." }
};
Task.Run(() =>
{
//do something that might take a while here...
System.Threading.Thread.Sleep(2000);
}).ContinueWith(task => window.Close(),
CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
window.ShowDialog(); //Call .Show() instead if you don't want to block here until the task has finished.
For this sort of thing I use a busy "throbber" or "spinner" which is shown in a panel on top of the window. That just animates to show something is going on.
I bind the visibility of that to an "IsBusy" property in my window's viewmodel.
In a base viewmodel that inherits from, because this is a common requirement.
When I set Isbusy true, the spinner appears and animates.
When I set it False, the spinner disappears.
I also have an area where I show messages. These are animated so they appear and then fade out after a few seconds.
It's a bit much code to paste into a SO answer, but there's working code uses this approach here:
https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204
Throbber is the usercontrol to look at.
This usually goes in a panel covers where the user would edit stuff - so they can't change data as it's being updated.
I also use IsBusy in the CanExecute predicate of commands to disable buttons etc as processing is done. Because notification isn't necessarilly instant I check inside all commands and exit if IsBusy is true. Commands set IsBusy true whilst they run.
For completeness and probably not directly relevant to Bella's question.
In some situations I want to just show the user that processing is happening but allow them to continue working. This happens a fair bit in the Map and Scenario editors of the game I'm working on.
For example, when you mouse over a commander there's a Spatial A* best route calculation happens between a commander his subordinates that can take in the order of 1 to 12 seconds. I don't want to block the user but I want to show something is going on in this instance.
I have a second mechanism with a less intrusive throbber I use for this. That appears next to my user notification message.
Related
I have 2 windows. The main form and the Loading form. In the main form you send a request which will be executed. Because this takes some time, I made the Loading-form with a progress bar so that the user knows the program is working.
What I want to: The Loading-form should open itself when the process ist started and close itself when it's finished.
At the moment I have code that looks something like this:
Loading.Show();
Loading.MakeAStep(); //used for progressing the progress bar
//program is working
//finishes
Loading.Visible = false;
Loading.ResetProgress(); //Sets the value of the progress bar to 0
My problem is: The window with the progress bar opens, but there is also a label which shows "please wait". When the form opens, the progress bar works perfectly, but the label is just a hole (it really is you can look through it). When I use instead of visible = false form.Close, it works just fine with the label but I get an error when I try to start a progress in the same session.
What I want/need: Either a solution to the hole-problem, or an effective way to open and close a form several times during one session.
(Posted the solution on behalf of the question author).
The answer is in the comments: The UI blocks and I needed to Update the form with Loading.Update(); I put that between Show and MakeAStep.
As already mentioned by others, the problem is that you run your long running process in the UI thread. To avoid this, you should improve how the loading form receives the task and works on it:
The loading form should get the thing to run as a Task (maybe by a method Run(Task task). After getting this task the loading form can attach another action to it, what shall happen when the task is finished by using .ContinueWith() and simply closes itself when it reaches that point. After that it will Start() the task and call ShowDialog() on itself.
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.
Pretty new to DevExpress, my company is stuck using 9.3
I've got this very small snippet of code:
wait = new DevExpress.Utils.WaitDialogForm("Please wait...", "Performing SVN check");
wait.Visible = false;
wait.ShowDialog();
ParseSVNResults(CheckSVN());
wait.Close();
My WaitDialog displays, but the code never continues. I put a breakpoint on ParseSVNResults and when I run the code it gets to that line.
It works properly if I just call Show() instead of ShowDialog(), but that gives poor behavior should the user click outside of the Wait form. The application "whites out" like it's stopped responding and the mouse changes into that little rotating circle icon. Also the hour glass that the dialog form shows doesn't rotate. Stupid minor detail, but it looks like the whole application crashed to end users.
ShowDialog, by design, "blocks" the code until you close the dialog. That is the entire purpose.
The reason that Show() is causing everything to white out is that your work is happening in the UI thread. The proper way to handle this would be to move your work (ParseSVNResults) into a background thread, via something like BackgroundWorker or a Task.
In a WPF 4 app, I have a very big user control full of controls which takes something like 4s to initialize on a fast machine. During this time the application is of course not responsive at all.
Is there a way to show an animation in the main window while this control is initialized?
I understand that I cannot create it on another thread. But is there maybe a way to create it with a lower priority from the Dispatcher so that I could show a spinning wheel or so on the main window which would still spin?
(The only solution I can think of right now would be to break the user control into further pieces and load them only when needed. But this will take a lot of development time to change.)
Update1
To be more clear: It is a simple WPF window using tab pages. When a new tab page is opened I'm initializing the user control which holds the controls for this tab page. One of these user controls is so full of controls that it takes 4s until the new tab page will be shown.
So I thought showing a spinning wheel would be better than having a blocked application.
I think you are going to have to break this user control up into pieces. What you can do is use a BackgroundWorker to coordinate the 'building' of this user control. Each time the DoWork event is fired, use Dispatcher.BeginInvoke to create an add the next control to your UI. This technique is described in the following blog post:
http://loekvandenouweland.com/index.php/2010/12/wp7-add-user-controls-graphics-in-background-thread/
This will allow you to show an animation during loading.
Why can't you initialize it on another thread? I see two scenarios:
Initialization is slow for non-WPF reasons that be preloaded/precomputed on another thread before entering the core WPF initialization.
WPF itself is consuming 4 seconds of CPU time (though, that's really WTF levels of CPU time...). If so, you can start another STA thread with it's own message pump that can display an independant UI (e.g: a spinning wheel) until the primary thread is done loading.
You can create "dialogs" which implicitly create a new Dispatcher and run on a background thread, or you can explicitly create your own Dispatcher (=message pump).
I use the following method to do so:
public static Dispatcher StartNewDispatcher(ThreadPriority cpuPriority = ThreadPriority.Normal) {
using (var sem = new SemaphoreSlim(0)) {
Dispatcher retval = null;
var winThread = new Thread(() => {
retval = Dispatcher.CurrentDispatcher;
sem.Release();
Dispatcher.Run();
}) { IsBackground = true, Priority = cpuPriority };
winThread.SetApartmentState(ApartmentState.STA);
winThread.Start();
sem.Wait();
return retval;
}
}
This gives you real multi-threaded UI; but it also means you can't databind or in any other way directly communicate between the two UI's: after all, WPF objects have thread-affinity.
Before you go this route, verify there aren't any slow components you can preload using a profiler: option 1 (preload heavy stuff before WPF init) is simpler and cleaner.
A solution could be to move the slow part (loading data? that doesn't belong IN the control) to another thread as you mentioned.
Or use a VirtualizingStackPanel to lazy load what is needed.
Could you elaborate on the reason for the delay?
I am working on a C# WinForms app, and I want it to be able to pop open a non-modal dialog which will get redrawn and be interactive at the same time as the main window/form.
The main window is using a SerialPort and displaying data transfer counts, which are continually increasing via a SerialDataReceivedEventHandler.
I can use ShowDialog() which seems to work in a modal fashion, but the main window data counters freeze while the dialog is in use, and I think eventually the serial buffers are overrun.
I think I want to use Show(), but if I do this the dialog appears on screen half-drawn, then is not drawn or interactive any more (gets trashed if I drag another window across it). It stays onscreen until I close the main app window.
Perhaps I should be starting another thread or, likely, am just doing something wrong. I don't usually do C# or Windows programming (maybe you can tell.)
Edit after comments (thanks, commenters):
I think maybe most things are getting run under whatever thread the serial receive event handler gets called under. When starting up my app I create a class to handle the serial, which includes:
com.DataReceived += new SerialDataReceivedEventHandler(SerialRxHandler);
The only code that I have written to care about threads is some functions to update the counters and log listbox, which I found had to be wrapped with some InvokeRequired to stop me getting complaints about thread switching:
delegate void SetCountDelegate(TextBox tb, int count);
internal void SetCount(TextBox tb, int count) {
// thread switch magic
if (InvokeRequired) {
BeginInvoke(new SetCountDelegate(SetCount), new object[] { tb, count });
} else {
tb.Text = String.Format("{0}", count);
}
}
So maybe I shouldn't be trying to Show() a form on this thread. Another InvokeRequired block, or should I be doing things completely differently?
Suppose I'll answer myself..
Prompted by commenters and a bit of thinking, I did an Invoke() to switch to the UI thread before trying to create and Show() my child dialog. That worked.