views:

583

answers:

2

The code I'm working on is part of a WPF application that should display a set of stacks of playing cards that the user can interact with. So there's three UserControls I'm working with:

  • MainControl
  • CardStackControl
  • CardControl

In MainControl.xaml:

    <StackPanel 
        Grid.Row="0"
        Orientation="Horizontal">
        <ItemsControl 
            ItemsSource="{Binding Path=CardStacks}"
            >
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl>
    </StackPanel>

My data templates are defined in a Resources.xaml file:

<DataTemplate x:Key="MainTemplate" DataType="{x:Type ViewModel:MainViewModel}">
    <View:MainControl />
</DataTemplate>

<DataTemplate DataType="{x:Type ViewModel:CardStackViewModel}">
    <View:CardStackControl />
</DataTemplate>

<DataTemplate x:Key="CardTemplate" DataType="{x:Type ViewModel:CardViewModel}">
    <View:CardControl />
</DataTemplate>

CardStackControl.xaml:

<StackPanel Orientation="Vertical">
    <TextBlock 
        Height="30"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Text="{Binding Path=CardName}" 
        />
    <ItemsControl
        Height="Auto"
        ItemsSource="{Binding Path=Cards}"
        ItemTemplate="{StaticResource CardTemplate}"
        >
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl>
    <TextBlock 
        Height="30"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Text="{Binding Path=Count}" 
        />
</StackPanel>

CardControl.xaml:

    <TextBlock 
        Name="TitleTextBlock"
        Grid.Row="0" 
        Grid.Column="1"                   
        HorizontalAlignment="Center"                   
        VerticalAlignment="Center"
        Text="{Binding Path=Name}"
        >

MainViewModel:

public ObservableCollection<CardStackViewModel> CardStacks;

// ...

CardStackViewModel:

public class CardStackViewModel
{
    private CardStack Model;

    public string CardName
    {
        get
        {
            return this.Model.CardType.Name;
        }
    }

    public int Count
    {
        get
        {
            return this.Model.Cards.Count;
        }
    }

    public ObservableCollection<CardViewModel> Cards { get; private set; }

    // ...

CardViewModel:

    /// <summary>
    /// A reference to the Card type.
    /// </summary>
    private Card Model;

    /// <summary>
    /// Retrieves the name of the card.
    /// </summary>
    public string Name
    {
        get
        {
            return this.Model.CardType.Name;
        }
    }

    public string Caption
    {
        get
        {
            return this.Model.CardType.Text;
        }
    }

    // ...

In the MainViewModel, CardStackViewModel constructors, both ObservableCollection instances are initialized, populated and filled with some test data.

When I load the application and create the MainViewModel, the UserControls don't show up, and none of the test data is visible. I'm using this tutorial as a guide, so that MainViewModel corresponds to the "Workspaces" ViewModel on the tabbed control in that window. Granted, this example should layout each control horizontally, which is fine for now - I'm still learning WPF.

Obviously, I've left off the code-generated tags in the .xaml files and other scaffolding code (I can amend this to post up that code if it's not clear from these examples). I'm only testing/binding to the string properties on the Model classes for now.

+2  A: 

The ItemsControl in MainControl.xaml isn't specifying an ItemTemplate. Add:

ItemTemplate="{StaticResource MainTemplate}"

I think you'll also need to propagate the DataContext down on the user controls e.g.

<DataTemplate x:Key="MainTemplate" DataType="{x:Type ViewModel:MainViewModel}">
    <View:MainControl DataContext="{Binding}" />
</DataTemplate>

(and similarly for the other user controls in the other data templates).

itowlson
+1 infact Microsoft has not properly documented anywhere using only {Binding}, I also found this by an accident.
Akash Kava
I'll check this out tonight...
emptyset
+2  A: 

The way you declare the ItemsPanelTemplate is incorrect : you're declaring it as an item of the ItemsControl. You should do it like that :

    <ItemsControl 
        ItemsSource="{Binding Path=CardStacks}"
        >
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" IsItemsHost="True" />
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
Thomas Levesque
Thanks for the catch, gonna fix that up tonight.
emptyset