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/