Share via


MVVM and ScatterView

ScatterView provides a simple way to create applications that enhance the Microsoft PixelSense experience. It is a common way to visualize content that can be manipulated freely by user.

Figure 1 ScatterView with some different ScatterViewItems

All those who have tried to bind a ScatterView to a collection of objects ended up noticing that a ScatterViewItem will wrap every generated item.

Binding UI controls to collections exposed by the ViewModel is one of the principle at the base of the MVVM pattern and I like to apply it to Surface SDK apps too.

I would like to use the ScatterView in an MVVM context; this would mean bind it to a collection of different objects exposed by the ViewModel that would be represented in different way, just like in Figure 1.

There are two problems to overcome to get the result:

  •  How to apply the appropriate template to each kind of object respecting the MVVM pattern

Once we solved this problem, we will deal with

  • Correctly sizing the generated ScatterViewItems

Let’s face the problems one by one.

How to apply the appropriate template to each kind of object respecting the MVVM pattern

I saw many apps where a common workaround for this problem is to expose (from ViewModel) a collection of ScatterViewItems (or derived from).

This is something acceptable but we are coupling the ViewModel with View because in this scenario the ViewModel would reference Surface SDK 2.0 assemblies.

A solution to this problem would be using a DataTeplateSelector: In this way, we will be able to apply different template depending to the type of item:

Imagine having an interface:

  

 

public interface ISearchable 
  { 
  } 

 

 And some object implementing it:

 

public class  Item : ObservableObject, ISearchable  
    
    {  
    
        // concrete implementation..  
    
    }  
    
     
    
    public class  SearchBarViewModel : ViewModelBase, ISearchable  
    
    {  
    
        // concrete implementation..  
    
    }  
    
     
    
    public class  SearchResultViewModel : ViewModelBase, ISearchable  
    
    {  
    
        // concrete implementation..  
    
    }

 

In this way, we can expose a collection of ISearchable:

public class  SurfaceShellViewModel : ViewModelBase 
{ 
    public ObservableCollection<ISearchable> SearchableCollection  
    { 
        get { return _searchableCollection; } 
        set 
        { 
            _searchableCollection = value; 
            RaisePropertyChanged("SearchableCollection"); 
        } 
    } 
} 

 

And we can bind it to the ScatterView:

<s:SurfaceWindow
  
    xmlns declaration omitted> 
  
    <s:SurfaceWindow.DataContext> 
  
        <vm:SurfaceShellViewModel /> 
  
    </s:SurfaceWindow.DataContext> 
  
    <l:DragDropScatterView ItemsSource="{Binding Path=SearchableCollection}" >  
  
    </l:DragDropScatterView> 
  
</s:SurfaceWindow> 
  
  

 

Now what we need is a way to instruct the ScatterView to apply different template depending on the concrete type of object (in our case Item, SearchBarViewModel or SearchResultViewModel).

The simplest way to do this is to use a DataTemplateSelector. The DataTemplateSelector has a single method to override: SelectTemplate(object item, DependencyObject container). In this method we decide which DataTemplate the ScatterView have to choose based on the type of object passed.

The following sample demonstrate how to implement it:   

 

public class  ScatterViewDataTemplateSelector : DataTemplateSelector 
  
    { 
  
        public DataTemplate SearchBarTemplate { get; set; } 
  
        public DataTemplate SearchResultTemplate { get; set; } 
  
        public DataTemplate ItemDataTemplate { get; set; } 
  
   
  
        public override  DataTemplate SelectTemplate(object item, DependencyObject container) 
  
        { 
  
            DataTemplate retVal = null; 
  
   
  
            if (item is SearchBarViewModel) 
  
                retVal = SearchBarTemplate; 
  
            else if (item is  SearchResultViewModel) 
  
                retVal = SearchResultTemplate; 
  
            else if (item is  Item) 
  
                retVal = ItemDataTemplate; 
  
   
  
            return retVal; 
  
        } 
  
    } 
  
   
  
<s:SurfaceWindow 
  
    xmlns declaration omitted> 
  
    <s:SurfaceWindow.DataContext> 
  
        <vm:SurfaceShellViewModel /> 
  
    </s:SurfaceWindow.DataContext> 
  
    <l:DragDropScatterView ItemsSource="{Binding Path=SearchableCollection}" >  
  
        <s:ScatterView.ItemTemplateSelector> 
  
            <ts:ScatterViewDataTemplateSelector> 
  
                <ts:ScatterViewDataTemplateSelector.ItemDataTemplate> 
  
                    <DataTemplate> 
  
                        <Image Source="{Binding Url}" /> 
  
                    </DataTemplate> 
  
                </ts:ScatterViewDataTemplateSelector.ItemDataTemplate> 
  
                <ts:ScatterViewDataTemplateSelector.SearchBarTemplate> 
  
                    <DataTemplate> 
  
                        <v:SearchBar /> 
  
                    </DataTemplate> 
  
                </ts:ScatterViewDataTemplateSelector.SearchBarTemplate> 
  
                <ts:ScatterViewDataTemplateSelector.SearchResultTemplate> 
  
                    <DataTemplate> 
  
                        <v:SearchResultView /> 
  
                    </DataTemplate> 
  
                </ts:ScatterViewDataTemplateSelector.SearchResultTemplate> 
  
            </ts:ScatterViewDataTemplateSelector> 
  
        </s:ScatterView.ItemTemplateSelector> 
  
    </l:DragDropScatterView> 
  
</s:SurfaceWindow> 

 

 Thanks to the DataTemplateSelector we was able to bind a ScatterView with a common collection of objects, we was also able to instruct the ScatterView to apply different template depending on the object type and we have also have the ViewModel decoupled from the View.

You can still reuse your ViewModel, it’s completely decoupled you can even use it for a WinRT version of the app! 

At this point if you try to start the app to see the result you will notice that that the size set for added ScatterViewItems could be wrong. Here we come to the second problem we will solve to achieve our result. 

Correctly sizing the generated ScatterViewItems

When ScatterViewItems are added trough a data bind it could be hard to set the appropriate size end eventually to apply a style to the ScatterviewItem.

A workaround is to create a base view from witch will inherit every View used as DataTemplate for the Scatterview:

 

 

public class  BaseView : UserControl 
 { 
     public BaseView() 
     { 
         this.Loaded += new  System.Windows.RoutedEventHandler(BaseView_Loaded); 
         this.Unloaded += new  System.Windows.RoutedEventHandler(BaseView_Unloaded);    
     } 
     void BaseView_Loaded(object sender, System.Windows.RoutedEventArgs e) 
     { 
         var svi = VisualHelper.GetVisualAncestor<ScatterViewItem>(this); 
         if (svi != null) 
         { 
             svi.Height = this.ActualHeight; 
             svi.Width = this.ActualWidth; 
             ResourceDictionary LibrayStiles = new  ResourceDictionary(); 
             LibrayStiles.Source = new  Uri("/DataTemplateSelector;component/Resources/Theme.xaml", UriKind.RelativeOrAbsolute); 
             if(this is SearchBar) 
                 svi.Style = (Style)LibrayStiles["SearchInScatterViewItemStyle"]; 
             else if (this is SearchResultView) 
                 svi.Style = (Style)LibrayStiles["LibraryContainerInScatterViewItemStyle"]; 
         } 
     } 
     void BaseView_Unloaded(object sender, System.Windows.RoutedEventArgs e) 
     { 
         this.Loaded -= new  System.Windows.RoutedEventHandler(BaseView_Loaded); 
         this.Unloaded -= new  System.Windows.RoutedEventHandler(BaseView_Unloaded); 
     } 
 } 

 

In this way we are setting the appropriate height and width to the ScatterViewItem and we are also applying the correct style.

You can download a full working implementation on the MSDN Code Gallery - MVVM and ScatterView