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
Related
I'm using the latest version of Caliburn Micro (4.0.173) in an application leveraging IoC.
I do not have a sample project yet has my sandbox is using nugets from Telerik that are not easily findable. But I think a detailed explanation can get the point across:
I have a ProfileEditorViewModel : Conductor<IScreen>.Collection.OneActive resolved by the container (singleton) through the constructor of my ShellViewModel and I use the I use the WindowsManager to open my ProfileEditorViewModel in a different window:
public class ShellViewModel : IShellViewModel{
private readonly IProfileEditorViewModel _profileEditor;
private readonly IWindowManager _windowManager;
public ShellViewModel(IWindowManager windowManager, IProfileEditorViewModel profileEditor){
_windowManager = windowManager;
_profileEditor = profileEditor;
}
//Method triggered by an Action from the View
public void OpenProfileEditor(){
_windowManager.ShowWindowAsync(_profileEditor, null, null);
}
}
When I close the ProfileEditor window (clicking on the top right red cross), it triggers a closing type deactivation, which is coherent with the implementation of a conductor (not being conducted itself) being shutdown. The Screens collection is being wiped and some more cleaning is being done through the Screen.cs implementation of Caliburn.
However, for this application, I'm facing "the view not being unbound" issue (https://github.com/Caliburn-Micro/Caliburn.Micro/issues/361) and next time I open the ProfileEditor, it'll bind a new view (because the Views Collection of my viewmodel was empty) and it create some issue related to UI components used in the view (basically one event from the viewmodel triggers similar actions, on the view(s) side, that all come back to the viewmodel, which create some identification issue)
Reading through the issue 361, I'm currently able to catch the Unloaded event of my ProfileEditor (once closed), and basically clean the DataContext of any view associated to the viewmodel (before Screen cleans the Screens collections). Next time I open the ProfileEditor, it'll bind a new view and it'll be the only one = it works.
However clearing the DataContext may produce some other issue down the road.
What I would like to do, is to avoid clearing the View collection of the ProfileEditorViewModel upon closing. So Caliburn can use this reference the next time it needs to resolve a window/view for the ProfileEditorViewModel (instead of looking for a new one).
Is it possible to intercept the Deactivation / Closing strategy and change the close parameter to false ?
Another solution may be for my ShellView to be a Conductor<>.Collection.AllActive but I can't wrap my head around teh management of a window closing. How to intercept the ProfileEditor windows closing and proceeding with a deactivation of the ProfileEditorViewModel instead of a closing.
I hope it make sense :)
Thank you in advance for your help
Firstly, I am very proficient at WPF and rarely get thrown by things, and rarely ever have to post a question. To be fair, I have only ever used one main Window with all embedded Content so it is really flat so this is the first time I have had to use additional floating Windows.
I have a Window that I raise after assigning it a View Model to its Content properly which duly shows and renders as expected using the data template etc. I hook into the Window.Close event after creation and remove the Close even method when closed. Pseudocode as follows:
Creating the Window:
private void CreateWin()
{
// Just in case a previous instance is open
CloseWin();
_myWin = new Window()
{
Content = new MyViewModel()
};
_myWin.Closed += OnMyWinClosed;
_myWin.Show();
}
When the Window is closed:
private void OnMyWinClosed(object sender, EventArgs e)
{
CloseWin();
}
Cleanind up:
private void CloseWin()
{
if (_myWin == null)
return;
_myWin.Content = null;
_myWin.Closed -= OnMyWinClosed;
_myWin = null;
}
I run the code to create the Window and set the content to the view model and when the I close the Window it soon gets destroyed and all is OK.
Now the issue:
The view model that I am assigning is not necessarily complex but it does a few things, even then, it is probably not relevant but, if I assign an existing view model that is still in memory rather than a new instance of the same view model type (as this code shows), I can create 50 instances of the Window but they do not get destroyed until the application terminates.
My actual window is a subclassed Window so that I can add a breakpoint to the ~ destructor to check the finalisation. I have also called the following just to make sure but no destruction, although a bit over the top:
GC.Collect()
GC.WaitForPendingFinalizers();
GC.Collect()
You will also notice, for extra measure, that I set the Window.Content = null afterward just to really detach it from the Window.
Nothing in the associated view, model, or data template has any events hooking the view model to the UI other than the inbuilt PropertyChanged. However, that should also make no difference, and also the same view model independently set has no issue.
My application went from about 110MB to 420MB when opening all so I could also see the memory creep up. I have a good laptop so is it that it would eventually clear but takes longer for larger objects? If that is the case then that does not make sense.
In summary, it almost seems that ViewModel’s assigned to a Window.Content property will prevent the Window from being garbage collected if the ViewModel is from another source and not created on the fly for the specific Window.
Surely that cannot be right?
Other points:
I have tried setting the Owner and not setting the Owner which makes no difference.
I have created these windows in a loop of 100 and destroying them to see if has anything to do with bigger objects taking a long time, but no change.
I have this:
Shows a waiting animation to 'block' the UI while performs a loading operation in the background.
At the end of the loading I call a method that instances a User Control and displays some data by using Bindings (and ObservableCollection among others)
This User Control gets displayed and user can interact with it, however the ObservableCollection seems to be stuck in another thread as it doesn't allow to add new items to it.
I've tried to update the UI at the Completed event of a BackgroundWorker, using Dispatcher, using DispatchTimer... all of this displays the User Control, but the ObservableCollection stays of out reach for adding.
The code that tries to add items to the collection is inside the UserControl.
The exact error is: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread"
This does not happen if I don't do the loading in the background.
Thank you for any workaround for this.
By the way, trying to add the items using Dispatcher doesn't work either.
In other words, what I would like to do is to create an object in the UI Thread while being in the background... I know this may sounds silly.
You may have to check which Dispatcher you are using? In your case you could be referring to two different dispatchers.
Also why not use thread safe observable collection?
Usually I will create the objects on my UI thread, then populate them with data obtained from a background thread.
For example,
void async LoadButton_Clicked(object sender, EventArgs e)
{
MyCollection = new ObservableCollection<SomeItem>();
// Can also use a BackgroundWorker
var collectionData = await GetCollectionData();
foreach(var item in collectionData)
{
MyCollection.Add(item);
}
}
I'm using C# 5.0 async and await keywords for asynchronous operations, but you can also use a BackgroundWorker that does your background work.
You can also use Dispatcher.BeginInvoke() for some lighter background work (such as copying data into MyCollection), although for heavy work I find it still locks up the UI so I prefer to use background threads.
It is not possible to modify the contents of an ObservableCollection on a separate thread if a view is bound to this collection, instead you can override ObservableCollection and provide support for it and use it across your application.
This sample contains exactly what you want - http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/
When it comes to threads and ui-elements one of the most important rules to follow which may safe you a lot of trouble in the long run is to keep ui-element instantiation on the ui-thread. Surely you can manage that. And if you need to change those objects from another thread you can use the Dispatcher.
(The threading model reference may also be of interest)
Thank you everyone for your help... a guy from MS visited the company (sorry for the commercial annotation) to do other things, I stoled him and show this behavior. In a matter of 2 minutes founds the source of the problem... which I'm not sure to really understand.
It happens that I'm using an ICollectionView to display a sorted/filtered version of my problematic ObservableCollection. I was creating this ICollectionView in the constructor of my class, so at the moment of deserialization it was created in another thread. He suggested to move this creation to a further time in code (when the related property gets read). This solved the problem.
However the ObservableCollection, created in that other thread, now lets me add new item. Not sure why, but now it works.
Sorry for being this late and thank you again.
I have an ObservableCollection that gets data from a foreach. The UI updates great when changes are made to the collection, but there is a lag as the collection size starts to increase in size. I have about 500+ items in my collection that get deleted, reordered and added. But I've noticed that there is a definite lag between when the changes are made and when they are reflected in the UI. The example below is just a simple one. Obviously, the progress bar won't be on for long in this case (fraction of a second), but the point I'm making is that the UI won't reflect the changes I've made for many, many noticeable seconds after the progress bar is stopped below. What event can I capture or implement in my object that will allow me to add code that updates the progress bar while the ObservableCollection is being pushed to the UI? Or does someone want to send me a "DealWithIt" dog picture :)
a_progressbar.IsIndeterminate = true;
foreach (Group<SomeItem> sortedItem in sortedItems)
{
OList.Add(sortedItem);
}
a_progressbar.IsIndeterminate = false;
Update Here is the code I used to make it work.
_dataBinder = new BackgroundWorker();
_dataBinder.DoWork += new DoWorkEventHandler(DataBinderWork);
_dataBinder.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DataBinderComplete);
...
private void DataBinderComplete(object sender, RunWorkerCompletedEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
a_progressbar.IsIndeterminate = false;
});
}
private void DataBinderWork(object sender, DoWorkEventArgs e)
{
Dispatcher.BeginInvoke(() => LotsOfWork());
}
This sounds like a good job for the BackgroundWorker component. The component will allow you to perform expensive operations in a background thread so your UI won't hang; meanwhile, the UI thread can be notified via events so it knows when the work has completed.
Update:
I must have misunderstood the question. If your question is about a list in your UI that is bound to the ObservableCollection being slow to update when adding a bunch of items, one thing you could attempt to do is to build a new ObservableCollection, filling it with the data you need, then assigning the new collection to the UI.
I've had problems with ObservableCollection in the past because it raises an event every time an item is added, which makes the UI responsiveness poor. My workaround was to create a sub-class of the collection where you could do an AddRange which only raised one event, but simply building a new collection that isn't bound to the UI then binding it after it's been populated is another option.
A big warning against the use of ProgressBar.IsIndeterminate - it takes more than 60% of the CPU resources and can have disastrous impact in some cases. See this article for more info.
I am designing the architecture for a module where a search takes place.
the search takes some time, and I would like to have the UI to be responsive, so I get to delegate the retrieval of the data to a class that will do the search on a separate thread.
Then, I can think of two options :
either
1° : the search method returns a viewmodel with empty data as a return value (place holder), but once the search process is over, the viewmodel's members will be updated and the result displayed on screen thanks to data binding,
2° : the search method doesn't have any return type, but once the search process is over, an event is raised and the viewmodel with its final values is passed in the event args so it can be used by the calling code. (and eventually be consumed by a view)
Any thoughts on this one ?
EDIT: Of course, with solution 1° I'm refering to WPF databinding on the objects returned by the search-results "place-holder" objects
If you use a BackgroundWorker, the design pattern is done for you. Call your search method in the DoWork event handler, and put the results in the Results property of the DoWorkEventArgs that's passed in.
Update the UI with the results in the RunWorkerCompleted event handler (they'll be in the RunWorkerCompletedEventArgs object's Results property).
If you want to update the UI while the search is in progress, have your search method call ReportProgress and update the UI in the ProgressChanged event handler. You can put anything you like into the UserState property of the ProgressChangedEventArgs, including intermediate search results, though you do have to be careful not to pass any objects that the background thread is going to touch as it continues executing.
I'd take advantage of data binding and bind your search result presentation control to a observable collection of search results and update that collection in your ViewModel from a background worker thread. Then it doesn't required any callbacks or update messages in you code, it just works as you'd expect taking advantage of the plumbing in place in WPF.
You could also leverage Priority Binding (http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx). It provides the fast / slow options and you don't have to worry about update threads. Unless of course you are looking to add some visuals to let the user know something is happening.
Here is what I did on a recent project.
The IsBusy property is something I have in the base class for all my viewmodels. It is a boolean that a view can bind to if it wants to display some kind of waiting control like a spinner or whatever.
The _retrieveData field is just an Action that I set up during the construction of the viewmodel. I do this because the viewmodel may get its list of Cars in several different ways - so _retrieveData may have different code in it based on the constructor that was used. After _retrieveData gets the data, it will set the private backer, _cars with the data. So after _retrieveData is done, setting the public Cars to the value with the new data in _cars causes the PropertyChangedEvent which lets the view know to update itself.
So the effect is that when the view goes to get the data for the first time, it returns immediately but gets null. Then a few seconds later, it gets the actual data. During that time, the UI is responsive. And also, the IsBusy is true if the UI wants to let the user know that it is working in the background.
Not sure if this is a good way to handle it, but it is working for me so far.
public List<Car> Cars
{
get
{
if (this._cars == null)
{
base.IsBusy = true;
// Start a background thread to get the data...
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object nullObject)
{
this._retrieveData.Invoke();
this.Cars = this._cars;
base.IsBusy = false;
}));
// While waiting on the background thread, return null for now. When the background thread
// completes, the setter will raise OnPropertyChanged and the view will know its time to bind...
return this._cars;
}
return this._cars;
}
set
{
this._cars = value;
base.OnPropertyChanged("Cars");
}
}