Can I freeze my UI rendering while my form loads? - c#

Is there any way I can pause all UI Update commands in Winforms?
Or I have a slight feeling I'm trying to go about this the completely wrong way, so is there another way around my problem: I basically load a saved state of a control, which loads new controls all over it. However I do some of this in the UI thread, and some of the data loading from another thread, which then fills the UI.
So the effect I have when it is loading is that the user can see a few of the controls appearing in one place, then moving to another place on the form, changing values, etc.
I'd like to get a loading screen instead of this and load the controls in the background. It's quite a large application and its not THAT important so redesigning my code isn't really an option.
Can I simply stop all Update() commands on a control while a method is executing?

You can use the SuspendLayout and ResumeLayout methods to wrap the setup of UI in one operation (without the update of the rendering).
Basically (assuming SomeMethod is in the form class):
private void SomeMethod()
{
this.SuspendLayout();
// all UI setup
this.ResumeLayout();
}

it really depends on your form logic, in general you should not overload the Load or Show method with too much things so that the form can be shown and drawn quickly and always look responsive.
in some cases it could help to use the SuspendLayout and ResumeLayout methods, see here:
Control.SuspendLayout Method

Related

Form load interface

In my form_load I have a mysql connection that loads data in a datagrid, when starting my application it will first try to load the mysql database and after its done it will load the form/interface with all the controls.
This leaves the user the impression nothing is happening, how can I first load the form/interface with all the controls and then my mysql database?
I would say, 3 approaches
1 - Separate Splash screen
2 - On-Form "splash screen" - first thing you do on form.load, is Me.Show(), then show a message, something like "Please wait while data is loaded". Progress can be shown to users as well using Application.DoEvents which will refresh your screen.
3 - Background worker thread. Using it, you can actually give user progress updates. That will leave your form completely responsive. http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
Move your code to Shown event or OnShown overridden method. this will allow your form shown to user before it loads grid.
You should move your "Database calls" to worker threads as well.
protected override void OnShown(EventArgs e)
{
//Delegate the DB calls to worker threads here
//everything will be smooth then..
base.OnShown(e);
}
There are two ways to approach this situation. You need to pick the approach that best suits your needs.
Approach 1: Create a splash screen. A splash screen is a small window that opens up and usually had product info and a message like "Initializing...". You can see this in a lot of applications, such as Microsoft Office. You can see how to make one by looking at this article.
Approach 2: You will need to make your load data happen in a separate thread then your UI. You will need to load your data in the separate thread and bind it to a list of some sort temporarily, then after its done, bind the list to your Datagrid
Use other thread for loading data from mysql. It's loading form first, but not displaying it until code has finished work.
new Thread(new ThreadStart(LoadDataMethodHere)).Start();
You can use Form Shown event.
Alternatively you can use a different thread to connect to database and show data or use a timer.

WinForms: Looking for an easy way to pop up a 'Processing..' panel

I have a WinForms application that, at some point, will do some calculations that take a couple of seconds. During this time, I disable the controls. Additionally I want to display a popup that kind of 'blocks' the application more visibly.
I tried placing a Panel on top of the Form in order to do so, but there I'm somehow not able to add any text to it. I.e. the Labels and ImageBoxes I put there are somehow not displayed or behind the Panel.
Any suggestions how I could achieve this in a quick and easy way? .. I mean, without redesigning the whole Form.
Your problem with the things not showing up in your additional panel is that everything is happening in the same thread, but your calculations are blocking everything from appearing because they are hogging up the thread. You should look into using a backgroundWorker, which is one of the tools in WinForms. You should throw the function call that performs the calculations into the DoWork method of the backgroundWorker, then the form itself can continue to function during the calculations.
See http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx for examples
Create a Dialog Box (windows form) to popup during the processing, then you can have the Dialog Box close once your processing is completed.
By using a Dialog Box not a Message Box, you can still have control over closing the window when your processing is done.
You could create a simple UserControl with just a Label and an ImageBox, maybe with public setter for their values. You can add it to your form setting its Visible property to false. Then you can make it visible during your calculations and go back top invisible in the end.

How do I show an animation while creating user control

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?

Delay loading of combobox when form loads

I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.

Forced Redraw in Silverlight 3 application

I have a function in my silverlight app, that takes one or two seconds to finish. While executing this function, I want to show my "just loading" UC to the user:
private void ComboBoxGranularity_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
WaitScreenObject.Visibility = Visibility.Visible;
OperationThatTakesALittleTime();
WaitScreenObject.Visibility = Visibility.Collapsed;
}
Problem is, that Silverlight doesn't redraw while the function is executed, so my WaitScreen doesn't show up. I tried the trick from this question:
this.Visibility = Visibility.Visible;
but it didn't work. I wanted to avoid the Backgroundworker-Overhead, so is there any possibilty to make the WaitScreenObject visible?
Thanks in advance,
Frank
Your method runs on the UIThread since you mentioned that you are not using a background thread. The thread only redraws the screen when it is not busy with something else, since the queue is filled with the other instructions, and those are considered more important than a redraw.
I tried what Andrew suggested, but I could not find the InvalidateVisual() method on the UIElement. Maybe I was just being daft.
The reason why the linked example did not work for you, is because the other question just dealt with an element not being invalidated because it did not have focus. However, the UIThread was available for a refresh at that time.
I also tested the dispatcher.BeginInvoke() on a delegate, and it did not work either. I am afraid that from my point of view, you might just have to use a separate thread.
I could be wrong because I simulated my "work" by making the thread sleep instead, however, I cannot see what the difference will be.
Hope this helps.
Have you tried invalidating the visual for the root object to try convince it to redraw? (Havn't checked if it would work, just comes to mind as a hack)
Edit : InvaldiateVisual is the method in WPF on the UIelement, in Silverlight you use a diff call, such as InvalidateArrange
myCanvasRoot.InvalidateArrange();
Bit hacky but might convince it to perform the update of the screen.

Categories

Resources