UserControl как DataTemplate внутри ListBox

Я хочу повторно использовать мои UserControls в других UserControls, таких как страница или окно, как DataTemplates, в этом примере внутри ListBox. Все это MVVM.

У меня есть UserControl, называемый CardControl, для отображения простого объекта "Карточка". Карта имеет два свойства: "ID" и "CardImage". Элементы управления DataContext устанавливаются через XAML. Если я открою этот UserControl в VS или Blend, он покажет мне фиктивную карту, которую я определил в соответствующей ViewModel.

Теперь у меня есть еще один UserControl, называемый "CardSetControl", который должен отображать коллекцию карт. Таким образом, ViewModel имеет одно свойство типа ObservableCollection <Card> называемые "Карты".

Вот код:

<ListBox x:Name="MyList" ItemsSource="{Binding CardSet.Cards}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel>

        <!-- WORKING, but not what i want -->
        <TextBlock Text="{Binding ID}" /> // would display ID of Card
        <Image Source="{Binding Image}" /> // would display Image of Card

        <!-- NOT WORKING, but this is how i want it to work -->
        <UserControls:CardControl DataContext="{Binding "Current listbox item as DataContext of CardControl???"}" />

      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Прочитав массу статей о MVVM и DataContext/Binding, я все равно не смог его работать. Как все целые иерархические объекты USerControls/DataContexts выполняются наилучшим образом?

Ответы

Ответ 1

Для элемента управления ListBox для каждого элемента в источнике элементов создается предполагаемый ListBoxItem. Элемент задан как DataContext, а ваш ItemTemplate задан как шаблон. Поскольку DataContext наследуется, вам не нужно явно устанавливать его, поскольку он уже является экземпляром Карты в вашем DataTemplate.

В этом случае вам не нужно устанавливать DC на CardControl, потому что он установлен для вас.

<ListBox x:Name="MyList" ItemsSource="{Binding CardSet.Cards}"> 
  <ListBox.ItemTemplate> 
    <DataTemplate> 
      <StackPanel> 

        <!-- WORKING, but not what i want --> 
        <TextBlock Text="{Binding ID}" /> // would display ID of Card 
        <Image Source="{Binding Image}" /> // would display Image of Card 

        <!-- NOT WORKING, but this is how i want it to work --> 
        <UserControls:CardControl /> 

      </StackPanel> 
    </DataTemplate> 
  </ListBox.ItemTemplate> 
</ListBox> 

Должен работать для вас.

Ответ 2

В вашем примере DataContext UserControl будет отображаться в текущий момент. Он втекает в UserControl, а его дочерние элементы управления, как и любой другой UIElement, получает DataContext своего родительского элемента управления.

Это будет работать:

<ListBox x:Name="MyList" ItemsSource="{Binding CardSet.Cards}">
  <ListBox.ItemTemplate>
    <DataTemplate>
        <UserControls:CardControl />
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Где CardControl:

<UserControl x:Class="MySolution.CardControl" 
             OtherProperties="Not shown to keep this example small">
      <StackPanel>
        <TextBlock Text="{Binding ID}" /> 
        <Image Source="{Binding Image}" />
      </StackPanel>
</UserControl>

Ответ 3

Спасибо за ваши ответы, но я узнал, что моя проблема была другой, о которой я упомянул. Если это так, как вы описали, я вижу, что мои UserControls (CardControl) используются в качестве шаблона для элементов ListBox, а идентификатор и изображение отображаются правильно.

Кроме того, я всегда задавался вопросом, почему ID и изображение могут отображаться, пока я не могу привязываться к некоторым другим свойствам, которые я определил в ViewModel.

Сегодня я нашел интересную статью о DataContext hierachy. Там сказано, что DataContext внутри ListBox НЕ является тем же DataContext, что и на странице, в которой находится ListBox. Я не видел этого раньше, поэтому я решил установить DataContext каким-то образом, как я упоминал в вопрос. Теперь я могу привязываться ко всем свойствам.

Вот статья: http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/