Wednesday, December 15, 2010

WPF ListView – ScrollIntoView

Lately a colleague of mine had a requirement to add items to a ListView programmatically. After adding an item, the ListView was supposed to scroll automatically so that the newly item came into view. First attempts to use the ScrollIntoView method of the ListView failed. There was just no scrolling at all. So I decided to check things up and here are the results.
ListView uses an ItemContainerGenerator that generates the UI and the visual tree for the ListView and its items. Whenever you add an item to the ListView the generator recreates all the item container elements. It does this in an asynchronous way. Therefore if you add an item programmatically and call ScrollIntoView right afterwards the container items will not have been created at this time and that’s why you won’t see any changes in the UI.
The trick here is to subscribe to the StatusChanged event of the ItemContainerGenerator:

listView.ItemContainerGenerator.StatusChanged += new ItemContainerGenerator_StatusChanged;

In the event handler you can check if the current status of the generator is equal to GeneratorStatus.ContainersGenerated:

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
    if(listView.ItemContainerGenerator.Status ==
GeneratorStatus.ContainersGenerated)
    {
        var info = listView.Items[listView.Items.Count - 1] as FileInfo;
        if (info == null)
            return;

        listView.ScrollIntoView(info);
    }
}

If this is the case, all items have been created and you can use the ScrollIntoView method. In the example application I used a list of FileInfo objects that were created by a background thread one after another. The parameter of the ScrollIntoView method is of type object and one might suggest passing a ListViewItem, but that won’t work. Just get an item from the Items collection of the ListView and pass it to the method and you will see that the ListView gets scrolled automatically. And don’t forget to unsubscribe from the StatusChanged event. Otherwise nobody will be able to scroll the ListView from the UI.

Tuesday, December 7, 2010

MVVM in WPF - Code Division

Often I hear the discussion where to put certain code within the MVVM pattern in WPF to achieve loose coupling and reduce unnecessary dependencies. Frameworks like PRISM (or Composite Application Guidance for WPF), Caliburn, CoreMVVM and others try to address this problem in different ways. From speakers at conferences you may hear things like: “Never put any code in the code-behind file, except InitializeComponent.” Others make excessive use of code behind and leave the view model relatively dumb.
According to my experiences and beliefs you should always find the right balance of where to put what kind of code. For myself, I try to follow the undermentioned guidelines:

-         The View which consists of the XAML file and code-behind should never have a reference to its ViewModel. It should not even know the type of the view model (see my last blog entry on how to achieve this). Having said that it is evident that the ViewModel should not know the View it is bound to.
-         Communication between View and ViewModel takes place only by the use of Binding. Use Two-Way Binding if needed. Maybe use helper properties in the ViewModel. Implement INotifyPropertyChanged and use classes like ObservableCollection to notify the View of changes.
-         Code-Behind files may contain additional code if that code represents intrinsic functionality that can not or not easily be achieved by using Binding. An example might be the automatic update of a field according to another field in the View based upon a more complex calculation. You may also use Converters.
-         Use Commands to route events from the View to the ViewModel. Josh Smith made a really cool and lightweight CommandSink solution which uses RoutedCommands. The disadvantage in Josh’s example is that you need a type reference to your ViewModel in the View. But there are other solutions to avoid that (for those who are interested, you may download the BASTA 2010 example project from Tobias Richling).

In order to meet all of these guidelines you will need quiet some infrastructure in your application that produces some kind of overhead and may even have an impact on performance. So it is up to you to decide if’s worth to have that overhead.