binding - UserControl como DataTemplate dentro de ListBox
mvvm user-controls (3)
Quiero reutilizar mis UserControls en otros UserControls como página o ventana como DataTemplates, en este ejemplo dentro de un ListBox. Todo es MVVM.
Tengo un UserControl llamado "CardControl" para mostrar un objeto simple "Tarjeta". La tarjeta tiene dos propiedades, "ID" y "CardImage". Los controles DataContext se establecen a través de XAML. Si abro este UserControl en VS o Blend, me muestra la tarjeta ficticia que he definido en el ViewModel correspondiente.
Ahora tengo otro UserControl llamado "CardSetControl", que debería mostrar una colección de tarjetas. Así que el ViewModel tiene una propiedad de tipo ObservableCollection <Card> llamada "Tarjetas".
Aquí está el código:
<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>
Después de leer un montón de artículos sobre MVVM y DataContext / Binding, todavía no conseguí que funcionara. ¿Cómo se hace todo esto jerárquicamente USerControls / DataContexts de la mejor manera limpia?
En su ejemplo, el DataContext del UserControl
sería la Tarjeta actualmente seleccionada. Fluye hacia el UserControl
y sus controles secundarios como cualquier otro UIElement
recibe el DataContext
de su control principal.
Esto funcionaría:
<ListBox x:Name="MyList" ItemsSource="{Binding CardSet.Cards}">
<ListBox.ItemTemplate>
<DataTemplate>
<UserControls:CardControl />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Donde CardControl es:
<UserControl x:Class="MySolution.CardControl"
OtherProperties="Not shown to keep this example small">
<StackPanel>
<TextBlock Text="{Binding ID}" />
<Image Source="{Binding Image}" />
</StackPanel>
</UserControl>
Gracias por sus respuestas, pero descubrí que mi problema era otro que mencioné. Si lo hago de la manera que lo describió, puedo ver que mis UserControls (CardControl) se usan como plantilla para los elementos de ListBox y la identificación y la imagen se muestran correctamente.
Además de eso, siempre me pregunté por qué se pueden mostrar ID e Imagen mientras no puedo enlazar a otras propiedades que he definido en el ViewModel.
Hoy encontré un interesante artículo sobre la jerarquía de DataContext. Allí se dice que el DataContext dentro de un ListBox NO ES el mismo DataContext que en la página en la que está el ListBox. No lo había visto antes, así que pensé que tenía que configurar el DataContext de alguna manera como mencioné en el pregunta. Ahora puedo atarme a todas las propiedades.
Aquí está el artículo: http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
Para el control ListBox, se crea un ListBoxItem inferido para cada elemento en el origen de los elementos. El elemento se establece como DataContext y su ItemTemplate se establece como la plantilla. Dado que DataContext se hereda, no tiene que configurarlo explícitamente porque ya es la instancia de Tarjeta en su DataTemplate.
Para este caso, no tiene que configurar el DC en CardControl porque está configurado para usted.
<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>
Debería trabajar para ti.