Trying to bind a list of submodels to a ListView

Sep 6, 2010 at 4:04 PM

Hi Guys,

I'm currently trying to build a MDI window. The basic samples worked just fine so far, but I've been struggling a bit.

So here's the ShellViewModel:

public class ShellViewModel : PropertyChangedBase
{
    public ShellViewModel()
    {
        AvailableScreens = new BindableCollection<MdiViewModel>();
        var mdiViewModel = new ProductListViewModel();
        AvailableScreens.Add(mdiViewModel);
        CurrentActiveView = mdiViewModel;
    }

    public IList<MdiViewModel> AvailableScreens { get; set; }

    private MdiViewModel currentActiveView;

    public MdiViewModel CurrentActiveView
    {
        get { return currentActiveView; }
        set
        {
            currentActiveView = value;
            NotifyOfPropertyChange(() => CurrentActiveView);
        }
    }
}

The View is not that spectacular so far as I'm still playing around with this stuff:

<UserControl x:Class="Life.Store.Client.Views.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:ViewModels="clr-namespace:Life.Store.Client.ViewModels" 
             xmlns:vw="clr-namespace:Life.Store.Client.Views" mc:Ignorable="d" 
             d:DesignHeight="600" d:DesignWidth="800" Height="600" Width="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <DataTemplate DataType="{x:Type ViewModels:ProductListViewModel}">
                <vw:ProductListView />
            </DataTemplate>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
        	<ColumnDefinition />
        </Grid.ColumnDefinitions>
        
        <StackPanel Grid.Column="0">
            <TextBlock>Life.Store</TextBlock>
            
            <Separator />
            
            <ListView x:Name="AvailableScreens">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <WrapPanel>
                            <BulletDecorator />
                            <Button x:Name="DisplayView">
                                <TextBlock Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
                            </Button>
                        </WrapPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            
            <Separator />
            <Button x:Name="DisplayView" Content="Click me" />
        </StackPanel>
        <ContentPresenter Grid.Column="1" Content="{Binding Path=CurrentActiveView}" DataContext="{Binding CurrentActiveView}" Width="200" Height="300">
            
        </ContentPresenter>
    </Grid>
</UserControl>

So here are my issues:

In the DataTemplate I can't bind with the familiar x:Name syntax but have to write Binding expressions like this:

<TextBlock Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
The ProductListViewModel I am trying to bind the ListViewItem also exposed the method: DisplayView() and it never gets called.
Since it's no ICommand I don't know how do bind to it through a Binding Expression, and since the x:Name="DisplayView" is not working I'm a bit lost :)

Hope this makes some sense.. Caliburn looks really cool and I really really like the MVVM approach it enables, but since I'm still new to WPF and Caliburn I find myself clueless all the time :)
I'm also a bit puzzled that there aren't that any good tutorials on how to build a Tabbed Application Shell with Caliburn.Micro since it seems to me like this is a fairly common szenario..

While at it: What's the correct way to open up a Modal Dialog Window with MVVM? Submodel hanging off the openening windows ViewModel? Is this possible with pure XAML and binding expressions or would I just write that inside a command?

Anyway, thanks for this great framework. 

greetings Daniel
Coordinator
Sep 6, 2010 at 4:33 PM

Unfortunately, we cannot automatically apply conventions to the contents of DataTemplates. The reason for this is that we have no way of intercepting WPF/Silverlight's template creation mechanism. To get around this you have a few options:

1. Do not use conventions inside of DataTemplates; Use explicit bindings and Message.Attach instead.

2. Extract all DataTemplates into UserControls which will re-enable conventions across the UserControl assuming you are using Convention binding to those controls or View.Model syntax. This is a good idea for large templates, but tedious for small ones

3. Use the Bind.Model attached property on the root UIElement of the DataTemplate like this Bind.Model="{Binding}" Doing this will cause conventions to be bound against the DataTemplate.

 

Also, you may want to consider using Conductor<T>.WithCollection.OneActive as your base class. This has the concept of a screen collection and a current screen. Databinding this class can allow you to easily set up an MDI-type shell. I have a blog post coming in the near future on Screens/Conductors in CM with a Billy-Hollis style sample shell which will demonstrate some of this.

The easiest way to show an application-wide modal dialog is to use the WindowManager. Pass it a view model and it will find the view, bind them and show it in a Window/ChildWindow. I'll be covering the WindowManager after the Screens/Conductors article :)

Sep 6, 2010 at 8:52 PM

Thanks for the help!

Are you going to write a book on Caliburn? That would be really great.. 

There are so many patterns and concepts involved in building a good MVVM WPF Application so a good book on that topic would be very interesting..

 

greetings Daniel

Sep 7, 2010 at 8:40 AM

Another Question just came to mind: What if I can't use convention based binding with Caliburn and want to bind a Button to a Command?

My ViewModels are freed from the ICommand hassle through Caliburn, but how do I bind the methods if the x:Name Syntax is not working?

 

greetings Daniel

Sep 7, 2010 at 1:53 PM

Hi Daniel,

Use Message.Attach as Rob suggested in his list of options. I ran into the same problem with my datatemplates and Message.Attach works perfectly.

Coordinator
Sep 7, 2010 at 2:25 PM

I highly recommend reading through the documentation. Many of these questions are answered in there :)