【问题标题】:UserControl as DataTemplate inside ListBox用户控件作为 ListBox 内的 DataTemplate
【发布时间】:2011-11-15 09:33:49
【问题描述】:

我想在其他用户控件(如页面或窗口)中重用我的用户控件作为数据模板,在此示例中是在列表框内。一切都是 MVVM。

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

现在我有另一个名为“CardSetControl”的用户控件,它应该显示卡片集合。所以 ViewModel 有一个 ObservableCollection 类型的属性,称为“Cards”。

代码如下:

<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 是如何以最干净的方式完成的?

【问题讨论】:

    标签: binding mvvm user-controls listbox datacontext


    【解决方案1】:

    我遇到了类似的问题,我使用以下代码修复了它。我在这里发帖是为了让其他人从中受益。

    如果对你有帮助,请不要忘记转发我的回答。

    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; }
    }
    

    【讨论】:

      【解决方案2】:

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

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

      今天我发现了一篇关于 DataContext hierachy 的有趣文章。据说 ListBox 中的 DataContext 与 ListBox 所在的页面上的 DataContext 不同。我之前没有看到,所以我想我必须以我在题。现在我可以绑定到所有属性了。

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

      【讨论】:

      • 遗憾的是,您所指的网站可能已经不复存在了。
      【解决方案3】:

      对于 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> 
      

      应该适合你。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 威尔,对不起,我基本上给出了和你完全相同的答案。我应该在下次回答之前刷新页面;)
        • 这样,模型(卡)作为 DataContext 传递给用户控件。如果我想让该用户控件使用它的视图模型怎么办?我应该如何将收到的模型传递给视图模型并将视图模型绑定到控件的视图?
        • @OndrejJanacek:不应将用户控件设计为拥有自己的视图模型。它们的表面应该有公共的可绑定属性,用户将绑定到他们自己的视图模型。这个答案更适合 OP,而不是最佳实践:/
        • @Will 这是非常有见地的信息,谢谢。
        猜你喜欢
        • 1970-01-01
        • 2011-05-21
        • 2011-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-09
        • 1970-01-01
        • 2011-12-17
        相关资源
        最近更新 更多