UserControl作为ListBox中的DataTemplate

时间:2011-09-12 14:12:12

标签: binding mvvm user-controls listbox datacontext

我希望在其他UserControls(如页面或窗口)中将我的UserControl重用为DataTemplates,在此示例中是在ListBox中。一切都是MVVM。

我有一个名为“CardControl”的UserControl来显示一个简单的对象“Card”。卡有两个属性,“ID”和“CardImage”。控件DataContext是通过XAML设置的。如果我在VS或Blend中打开此UserControl,它会向我显示我在相应的ViewModel中定义的虚拟卡。

现在我有另一个名为“CardSetControl”的UserControl,它应该显示一组卡片。所以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事情是如何以最好的方式完成的?

4 个答案:

答案 0 :(得分:15)

在您的示例中,UserControl DataContext 将是当前选定的卡片。它流入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>

答案 1 :(得分:13)

对于ListBox控件,为项源中的每个项创建一个推断的ListBoxItem。将Item设置为DataContext,并将ItemTemplate设置为模板。由于DataContext继承,因此您不必显式设置它,因为它已经是DataTemplate中的Card实例。

对于这种情况,您不必在CardControl上设置DC,因为它是为您设置的。

<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 :(得分:1)

感谢您的回答,但我发现我的问题是我提到的另一个问题。如果按照你描述的方式进行操作,我可以看到我的UserControls(CardControl)被用作ListBox项目的模板,ID和图像也能正确显示。

除此之外,我总是想知道为什么可以显示ID和图像,而我无法绑定到我在ViewModel中定义的其他属性。

今天我发现了一篇关于DataContext层次结构的有趣文章。在那里,据说ListBox中的DataContext与ListBox所在的页面上的DataContext不同。我之前没有看到过,所以我认为我必须以某种方式设置DataContext,就像我在题。现在我可以绑定到所有属性。

以下是文章: http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/

答案 3 :(得分:0)

我遇到了类似的问题,并使用以下代码修复了该问题。我在这里发布,以便其他人可以从中受益。

如果可以帮助您,请不要忘记上传我的答案。

EmpUserControl.xaml文件

<UserControl x:Class="Sample.UserControls.EmpUserControl"
             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:uc="clr-namespace:Sample.UserControls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"></RowDefinition>
            <RowDefinition Height="20"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Grid.Column="0">First Name: </Label>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding FirstName}" />
        <Label Grid.Row="1" Grid.Column="0">Last Name: </Label>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding LastName}" />
    </Grid>
</UserControl>

EmpUserControl.xaml.cs

namespace Sample.UserControls
{
    /// <summary>
    /// Interaction logic for EmpUserControl.xaml
    /// </summary>
    public partial class EmpUserControl : UserControl
    {
        public EmpUserControl()
        {
            InitializeComponent();
        }
    }
}

MainWindow.xaml文件

<Window x:Class="Sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Sample"
        xmlns:uc="clr-namespace:Sample.UserControls" 
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="900" WindowStyle="SingleBorderWindow" 
        ResizeMode="NoResize">
    <Grid>
        <ListBox Name="LbEmp" Width="220" Height="220" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <uc:EmpUserControl DataContext="{Binding}"></uc:EmpUserControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

MainWindow.xaml.cs文件

namespace Sample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void LoadEmpListBox(){
            var empList = new List<EmpViewModel>{
                new EmpViewModel { FirstName = "Rajeev", LastName = "Kumar" },
                new EmpViewModel { FirstName = "Sita", LastName = "Hedge" },
                new EmpViewModel { FirstName = "Deepika", LastName = "PL" }
            };
            LbEmp.ItemsSource = empList;
        }
    }
}

EmpViewModel模型

public class EmpViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}