DataTemplateOf<T> for DataTemplates of collections

Topics: Conventions, Feature Requests, UI Architecture
May 15, 2011 at 10:59 AM

I just wanted to know what everyone thought about this idea and what area's of CM would need to be changed to implement it?

I've been struggling with the concept of  how to handle common data templates for a particular model. I the scenario where i have a Model what has the same preview shown in 3 parts of my application each in a collection. to make this common between each section i could create a UserControl for each one and inject the model into the constructer like this.

 

<UserControl x:Class="LibrarySample.Views.BookPreviewView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    d:DesignHeight="480" d:DesignWidth="480">
    <StackPanel Grid.Column="0">
        <TextBlock x:Name="Book_Name" Margin="5" TextWrapping="Wrap" />
        <TextBlock x:Name="Book_Description" Margin="10,5,5,5" TextWrapping="Wrap" Foreground="#FF7E7E7E"/>
    </StackPanel>
</UserControl>

With the viewmodel like this 

    public class BookPreviewViewModel : Screen
    {
        public BookPreviewViewModel(Book book)
        {
            Book = book;
          
        }
        public Book Book { get; set; }
    }
The problem here though is that if i already have a collection of Books I need to create a new collection of BookPreviewViewModel's and add each book to it like this 
        public CategoryViewModel(BookCategory category)
        {
            var books = Library.GetBooks(category);
            BookPreviews = new ObservableCollection<BookPreviewViewModel>();
            foreach (var book in books)
            {
                BookPreviews.Add(new BookPreviewViewModel(new Book() { Name = "Some Book", Description = "Once apon a time.." }));
            }
        }
        public ObservableCollection<BookPreviewViewModel> BookPreviews { get; set; }
This makes the UI very disjointed from my data structure and creates problems like what happens if a book is added to the collection from another source. After thinking about it for a bit i came up with this ide
DataTemplateOf<T>
It would be a base class your ViewModels could inherit from that tells CM that this ViewModel is a datatemplate that should be used anywhere you find a collection of T. The class could look like
    public class DataTemplateOf<T> : Screen
    {
        public DataTemplateOf(T model, string context = "")
        {
            Model = model;
        }
        public T Model { get; set; }
    }
I thought an optional context might be good if you want to restrict it to certain areas. This way you ViewModel for the book preview looks like this
    public class BookPreviewViewModel : DataTemplateOf<Book>
    {
        public BookPreviewViewModel(Book book) : base(book) { }
    }
The categories ViewModel would simply contain a collection of books, CM would see that there is a template registed for that type and use BooksPreviewViewModel as the DataTemplate
<UserControl x:Class="LibrarySample.Views.CategoryView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">

    <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
        <StackPanel>
            <TextBlock Text="Category"/>
            <ListBox x:Name="BookPreviews" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"/>
        </StackPanel>
    </Grid>
</UserControl>

    public class CategoryViewModel
    {
        public CategoryViewModel(BookCategory category)
        {
            var Books = Library.GetBooks(category);
        }
        public ObservableCollection<Book> Books { get; set; }
    }