I am trying to optimize the loading times for my WPF prism application. The loading is basically a loop of using reflection to create instances of UI elements and then adding them to the main window (the shell) in a tab control.
Since we are limited to using a single thread to create all the objects, what would be the optimal way to speed up loading / create a better user experience?
These are the options I have so far:
Use lazy loading. Only load the tab when the user first clicks on it. But that would have a 4-5 second delay opening the first time as it gets initialized on demand.
Cache all the reflection calls. I actually did that, but it did not speed anything up at all. Most of the time occurs during the rendering of the controls...
?
Any suggestions would be greatly appreciated for this tricky problem.
You're pretty much stuck as you can only load objects on the main thread, so I don't think you'll make it load any faster.
What you can do is distract the user: I have an animated splash screen that take about 10 seconds to work its way through the animation sequence. This serves a number of purposes:
It shows the user motion - so they have a visual cue that something is going on
It distracts them and fills the space taken by the initial load
To ensure smooth animation you need to create a second dispatcher. Here's how I do it:
public class AppEntry : Application
{
private static ManualResetEvent _resetSplashCreated;
internal static Thread SplashThread { get; set; }
internal static SplashWindow SplashWindow { get; set; }
private static void ShowSplash()
{
SplashWindow = new SplashWindow();
SplashWindow.Show();
_resetSplashCreated.Set();
Dispatcher.Run();
}
[STAThread]
public static void Main()
{
_resetSplashCreated = new ManualResetEvent(false);
SplashThread = new Thread(ShowSplash);
SplashThread.SetApartmentState(ApartmentState.STA);
SplashThread.IsBackground = true;
SplashThread.Name = "Splash Screen";
SplashThread.Start();
_resetSplashCreated.WaitOne();
var app = new App();
app.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(app_DispatcherUnhandledException);
app.InitializeComponent();
app.Run();
}
static void app_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
// MessageBox.Show(e.Exception.StackTrace);
}
}
I set the AppEntry class as my Startup Object in the Project Properties/Application tab.
I close my splash screen at the end of my OnStartup method in App:
AppEntry.SplashWindow.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => AppEntry.SplashWindow.Close()));
Is this faster? No
Does the user think it's faster? Yes
Sometimes, if you can't give them speed, you can give them activity. It's a nice placebo.
As you mentioned, You cannot Multithread if your objects are DependencyObjects. Kent Boogart discusses this. That's why you must leverage INotifyPropertyChanged and do POCO objects to hold your data. That way you can multithread to obtain the data and then bind these to your UI. Another drawback of using DependencyObjects is that you're tying your application too much to the WPF framework (DependencyObject being a class defined in the System.Windows namespace in a WPF assembly (don't remember if PresentationCore or PresentationFramework)). If refactoring is not an option, you will have to consider a solution like the one LastCoder proposed. Be aware that you will be able to do very little multithreading (if any at all), therefore your application is not going to be very responsive all the time.
I would implement a timer that loads a few controls or tabs every tick (iteration). The timer will run on the same thread as the UI (control messages for it will be queued up on the Windows Message Loop). Once all of the work is done you can kill the timer.
The timer interval and the number of controls to load per tick will boil down to use-testing; try something like 100ms and 2 controls a tick that will give you ~20 controls a second, so if you had 10 tabs with 15 controls each it would take ~8seconds, but the UI shouldn't lock up as bad.
The best answer to speeding up the loading is to simply hide the container while the visual tree is being constructed.
This prevents the screen from constantly needing to update itself.
When all the elements have been added to the visual tree, then setting the container visibility to visible renders the tab container once.
We also implemented some simple lazy-rendering to the tab control items.
Net result: loading times from 2 minutes down to about 20 seconds.
Related
I have an WPF application using the MVVM design property. There is a long running process to populate a grid that I would like to show a busy cursor for. I have the part changing the cursor working. The problem I have is that the cursor is bound to a bool called IsBusy. When the method is called that runs for awhile I set IsBusy to true and when the method is complete I set it back to false.
public void LongProcedure(){
IsBusy=true;
... long running code here
IsBusy=false;
} `
I know the cursor binding to IsBusy is working because I tested it out with another procedure that does
IsBusy=!IsBusy;
And that does toggle the cursor. The problem I am having is that the view doesn't appear to refresh while the code in the method is running. Is there a way to force the view to refesh after I set IsBusy to true?
In every windowed application, there is a single thread that is dedicated to working with the visual representation of the application. This thread is the UI thread. When your code runs in response to a window event (e.g., the Loaded event or a user clicking on a button), it is running on this UI thread. While your code runs, the UI thread is occupied performing your work, and cannot update the visual representation of the window. In extreme cases (i.e., your work takes a perceptible amount of time to complete), your window will appear locked up.
If you are performing long-running work using UI thread, then you are not doing it right. You should either use async-await, or use Task.Run() in order to perform your work on a background thread.
If LongProcedure() is IO-bound (like waiting for data from somewhere), then you should use async-await. If it is CPU-bound (a lot of data processing/calculations), then you should use Task.Run()
Async-await:
public async Task LongProcedure()
{
IsBusy = true;
await GetDataFromSomewhereAsync();
IsBusy = false;
}
public async void CallingMethod()
{
......
await LongProcedure();
}
Task.Run:
public void LongProcedure()
{
IsBusy = true;
... // long running code here
IsBusy = false;
}
public async void CallingMethod()
{
......
await Task.Run((Action) LongProcedure);
}
Please note that the UI cannot be updated directly from a background thread. Typically you will need to marshal your update code onto the UI thread first. There are a number of different ways to do that, and it depends on what kind of windows application you are using, such as windows forms, WPF, etc.
I am a newbie in Windows phone app development. On one of my pages I have a button that does some substantial amount of work. So when I press the button the screen freezes and then resumes when all the work is done. Now I wanted to show some waiting image or page to be shown during this while.
So I created and destroyed an image on top on the page at the start and end of button click action respectively but it still freezes and the screen renders only after all the work is done making it indifferent. I am using Silverlite C#.
You'll want to push the heavy work onto a background thread. What's currently happening is that the work is being done on the UI thread, so it 'freezes' since the UI thread is waiting for the work to be done. There are various ways you can push the work to a background thread. One way would be to use a BackgroundWorker.
A simple example, which doesn't include things like reporting when the work is done, would look like this
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(BW_DoWork);
bw.RunWorkerAsync();
private void BW_DoWork(object sender, DoWorkEventArgs e)
{
//Your heavy work code
}
Important thing to remember when you work on a background thread, is that if you want to change anything on the UI, you need to marshal the data to the UI thread and update it there. You can do that like this:
Deployment.Current.Dispatcher.BeginInvoke(() => {
myTextBlockExample.Text = "Changing the UI";
}
If you tried to change the TextBlock without the Dispatcher.BeginInvoke method, you'll get a Invalid Cross-Thread Access error. If you see that error in WP development, it's likely that you're trying to update a UI element from a non-UI thread.
You can put your code that displays the loading image before calling bw.RunWorkerAsync.
When my MainPage loads in my Windows Phone 7 application, it triggers a popup splash screen that includes a progress bar and a graphic in a usercontrol. After displaying the spash screen I do some work in a BackgroundWorker thread to load some resources while the spash displays. When the loading is done I dismiss the popup. This technique is well documented on WindowsPhoneGeek.
I noticed today that while this is working flawlessly when running in debug under Visual Studio, if I run the same build directly without the debugger connected, the splash screen animation and progress bar never appear and the DoWork() thread takes at least twice as long to execute the same operations. I can see that the MainPage constructor gets called as well as OnNavigatedTo, but the popup still does't display and the default jpg splash image remains on the screen, until the worker thread completes. Then the popup displays for < 1 second and the mainpage displays. Again, this all works flawlessly when debugging through VS 2010.
This is all in the Emulator, I don't have a device yet. I just noticed this today, and coincidently(?) I just updated the environment to 7.1 last night.
// Constructor
public MainPage()
{
InitializeComponent();
IDictionary<string, object> state = Microsoft.Phone.Shell.PhoneApplicationService.Current.State;
if (!state.ContainsKey(STATE_WAS_LOADED))
{
state[STATE_WAS_LOADED] = "LOADED";
this.LayoutRoot.Visibility = System.Windows.Visibility.Collapsed;
_popup = new Popup();
_popup.Child = new NPGSplash();
System.Diagnostics.Debug.WriteLine("{0}: Displaying Splash Popup", DateTime.Now.ToString("ss.ffff"));
_popup.IsOpen = true;
// Asynchronously load the biggest dataset
StartLoadingData();
}
}
private void StartLoadingData()
{
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
_worker.RunWorkerAsync();
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Dispatcher.BeginInvoke(() =>
{
System.Diagnostics.Debug.WriteLine("{0}: Splash RunWorkerCompleted", DateTime.Now.ToString("ss.ffff"));
this.LayoutRoot.Visibility = System.Windows.Visibility.Visible;
this._popup.IsOpen = false;
});
}
EDIT:
I ended up buying a device on ebay this week to ensure that I don't release an app where I can't confirm that it works properly. I can confirm that whatever the problem is, it does NOT occur when running the application on the device. Good news. Still, I can't explain what appears to be an approximately 3-5 second pause in my application after the consrtuctor is called. I even changed my logic in the constructor to set a System.Windows.Threading.DispatcherTimer to fire in 100ms to kick off my logic. When this code executes, the constructor completes, but the timer doesn't tick for 3-5 seconds. Very odd, and only in the simulator when not attached to the debugger.
The problem here would appear to be that you are never attaching the Popup control to the visual tree. To be honest, I've got no idea why this would work with the debugger attached either, but I'm going off what I can interpret from your code.
What I think you need to do is to add the Popup control as an element in the XAML for MainPage so that it is attached to the visual tree.
One other thing I'd say is that it's perhaps worth moving all the data loading code out of the constructor and into either an override for OnNavigatedTo or a handler for the Loaded event. Generally speaking, it's best to keep constructors and short and simple as possible (and yes I appreciate that you're using a background worker to load the data).
I am having a bit of a problem with my UI hanging even though I am using a Dispatcher, and before I get any further I'm wondering if it's the way I'm handling the data retrieval.
Right now I have my main window creating a View and ViewModel. Then, inside of a new Thread (using a Dispatcher) it sets the View.DataContext = ViewModel. A very large ObservableCollection is lazily created when the binding kicks in which is causing the slowdown. However, it seems that some of the other UI items that should be showing up before that slowdow don't actually show up.
private void ButtonClick(Object sender, RoutedEventArgs e)
{
MyView view = new MyView();
MyViewModel vm = new MyViewModel();
TabItem tabItem = new TabItem();
tabItem.Header = "MyView";
tabItem.Content = view;
MyTabCollection.Items.Add(tabItem);
Window working = new Working();
working.Show();
ThreadStart thread = delegate()
{
DispatcherOperation operation = Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(delegate()
{
view.DataContext = vm;
((FrameworkElement)view.Parent).Focus();
working.Close();
}
)
);
};
Thread theThread = new Thread(thread);
theThread.Start();
}
This basically says it's supposed to create a view and a viewmodel, then add the view to the tab collection I have (which means it should show the new tab at the least). And, it should also show a "Working..." window. After that, a separate thread is supposed to link the ViewModel to the view, focus on that tab and close the working window. The problem is that the first portion doesn't show until everything is done; The tab is not displayed and the working window is not shown until after the new Thread actually finishes (which causes the Working window to show/close right away). I'm guessing it might have to do with the way I retrieve the data, but I'm not sure. Here is the way it does it:
Create View
Create ViewModel
Create TabItem with Content set to the View and add the TabItem to the TabCollection.
Create/Show the "Working..." window
Dispatcher: Set the View.DataContext = ViewModel. This event sets off the DataBindings, which in turn grab the ObservableCollection. Since the OC is created Lazily it is now being created (this is the bottleneck). <-- Is this messing up my separate thread/dispatcher?
Dispatcher: Set Focus to the tab
Close the "Working..." window
All your extra thread is doing is marshalling another call back to the dispatcher thread. Presumably you actually want to do work on the extra thread, or there's no point in creating it.
Ideally your extra thread should be fetching all the data appropriately, leaving you only to actually connect it all up in the dispatcher thread. The important thing is to decide which work you need to do on the UI thread and which work you need to do on the background thread.
Obviously your analysis of the problem is correct. Your view model is lazily loading data when it is needed, and this is not happening until the Dispatcher callback, at which point you are back on the UI thread again and everything is locked up.
In my opinion, the solution is to do the threading in the data access layer:
For collections: You can define special collections that return only items that have already been loaded from the upstream data source, then trigger loading of additional items on a separate thread when someone subscribes to INotifyCollectionChanged. When the additional items arreive, fire INotifyCollectionChanged events. When INotifyCollectionChanged is unsubscribed, cancel any pending load.
For totals and the like: Same idea. As data comes in the total increases and events occur (automatically for DependencyProperty or using INotifyPropertyChanged).
In addition, the data layer should have a parallel property to each collection, sum, or other delay-loaded value indicating whether it is fully loaded or not, allowing the UI to gray out sections that aren't fully loaded. It is also convenient to have an overall "loading" flag somewhere that can be used to gray out UI sections when anything at all is loading (easier to write the UI this way).
Note that sometimes an operation must block until the actual data has been retrieved. I think the easiest thing in this case is to provide methods in the data layer to force data to be loaded synchronously.
Your DispatcherPriority is set to Normal - try setting it to Background as this may improve the rendering
Our main WPF window ('Main Window') takes considerable time to create (it is using a ribbon and docking panel similar to Visual Studio). Just like in Blend the first action will always be to either choose an existing project to work on or to create a new project. Therefore we would like to show this 'Select Project' window as quickly as possible and create the heavy 'Main Window' in the background (invisible until the user clicks OK on the 'Select Project' window).
What's the best way to architect this startup scenario, preferably using 2 WPF windows (one for the project selection and one for the main control)?
I'm aware of this sample which demonstrates how to load a Win32 window shortly after application startup, but I'd prefer a WPF-only solution.
Another potential solution is to use multiple WPF UI threads, however lifetime management and window-to-window communication are non-trivial.
Questions:
Will I be able to see a faster startup time when implementing the 2 WPF window solution as compared to simply creating the 'Main Window' and not implement the 'Select Project' window (assuming I can find an implementation that takes care of the window lifetimes)?
Should the 'Select Project' window be created on the main application thread and the 'Main Window' on a background thread (or visa versa)?
If I need to use a Win32 startup window for speed, how would I communicate the user's choice of project name to the main WPF application window?
Does anyone know how Blend actually implements their startup window (which I am trying to mimic here)?
Any implementation pointers would be much appreciated!
Best way to architect this scenario
I would:
Create classes to manage the data for your "Select Project" window (such as MRU lists, templates, etc), making sure they are self-contained and don't require interaction with the main thread.
Store information about the "current" project(s) in the Application object or a singleton object.
Create the "Select Project" window to operate completely independently of the main window.
Set your "Select Project" window's "Open" and "Create" buttons to fire the Application.Open command, and bind the CommandParameter on the button to the property that contains the filename to open (or the template to instantiate).
Add a CommandBinding for the Application.Open command at the window level to take the command parameter and set the current project in the Application object.
In your application's startup code, immediately spawn a thread to create the "Select Project" window, then immediately do {a Dispatcher.Invoke at ApplicationIdle priority followed by a very short Thread.Sleep} several times, then proceed, allowing the main window to be created but make it initially invisible.
Set a trigger in the main window to make it visible once the current project has been set for the first time.
Here is how the Open command handler in the "Select Project" window might update the project path in the Application object:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
((App)Application.Current).CurrentProjectPath = e.Parameter;
}
Here is what the application's startup code would look like:
public override void OnStartup(StartupEventArgs e)
{
new Thread(() => { new SelectProject().Show(); }).Start();
for(int i=0; i<10; i++)
{
Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, () => {});
Thread.Sleep(20);
}
_guts = new MainWindowContent(); // probably a UserControl
_guts.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
_guts.Arrange(new Rect(0, 0, guts.DesiredSize.Width, guts.DesiredSize.Height));
_guts.SetBinding(VisibilityProperty,
new Binding { Source = this, Path="StartupFinished",
Converter = BoolToVisibiltyConverter.Instance });
_mainWindow = new MainWindow { Content = guts };
// Note: Title and other properties of MainWindow would be set in the XAML
}
Note the extra code to make sure the guts get measured and arranged ahead of time.
Here is the code to set the current project path:
public string CurrentProjectPath
{
get { return _currentProjectPath; }
set
{
_currentProjectPath = value;
LoadProject();
_startupFinished = true;
OnPropertyChanged("StartupFinished");
}
}
This code assumes the XAML binds _mainWindow's Visibility property
StartupFinished is a DependencyProperty that the main window's Visibility property is bound to, but it could be done in other ways.
Will you be able to see faster startup time this way?
Yes.
Should Select Project be created on main thread & main on other thread, or vice versa
You should create MainWindow on the main thread so it shares the same Dispatcher with the Application object. That means "Select Project" has to be in the second thread.
If you need to use a Win32 startup window for speed
It is probably probably not necessary. Startup of WPF itself is only a fraction of a second. But if it is necessary, the Application.Current.Dispatcher.Invoke technique described above will work.
Do you know how Blend implements its startup window?
No
Do you have a full working axample that I might look at? I tried to work something up from the code snippets enclosed above but wasn't able to get an example to work...