具有多个不同类型源的WPF列表框

具有多个不同类型源的WPF列表框,wpf,listbox,itemssource,multibinding,Wpf,Listbox,Itemssource,Multibinding,我实际上是为完全不同的东西设置了一个示例应用程序,但后来我尝试了以下方法: 我有一套电影。我会有一个显示所有电影的列表框。但是,列表框将它们作为按钮提供,以便您可以单击按钮并播放电影 代码是: <StackPanel DockPanel.Dock="Top"> <ListBox ItemsSource="{Binding Movies}" SelectedItem="{Binding Path=SelectedMovie}"> <ListBo

我实际上是为完全不同的东西设置了一个示例应用程序,但后来我尝试了以下方法:

我有一套
电影
。我会有一个显示所有电影的列表框。但是,列表框将它们作为按钮提供,以便您可以单击按钮并播放电影

代码是:

<StackPanel DockPanel.Dock="Top">
    <ListBox ItemsSource="{Binding Movies}" SelectedItem="{Binding Path=SelectedMovie}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                           Width="{Binding (FrameworkElement.ActualWidth),
                                   RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Button Content="{Binding Title}"
                        Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                                  Path=DataContext.PlayMovieCommand}"
                        CommandParameter="{Binding Id}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

然后我想在结尾添加一个按钮,文本为“Add”,当我点击该按钮时,我可以添加一部新电影

我没有找到一个解决方案来提供这一点。在搜索互联网时,我发现了
层次数据模板
复合收集
;起初,这两个项目看起来都很有希望,但我没能让它按我所希望的那样发挥作用。我也在考虑多重绑定,但我似乎又失败了

所以,我想我的问题是:
如何将单个添加按钮添加到我的电影收藏中

或更通用:
如何将不同类型的数据源/集合添加到列表框中?

您可以创建对象列表,添加电影,然后添加另一种类型,例如“字符串”对象。然后,您需要创建从DataTemplateSelector派生的类,该类贯穿整个列表并确定元素是否为Movie对象。如果是,则返回正确的DataTemplate,否则返回按钮的DataTemplate

class DTS : DataTemplateSelector
{
    public DataTemplate MovieTemplate { get; set; }
    public DataTemplate ButtonTemplate { get; set; }

    public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        return item is Movie ? MovieTemplate : ButtonTemplate;
    }
}
然后在XAML中,您可以

 <local:DTS x:Key="DTS" ButtonTemplate="{StaticResource ButtonTemplate}" MovieTemplate="{StaticResource MovieTemplate}"/>

请记住,ButtonTemplate和MovieTemplate必须在应用它们之前创建,因为StaticResource需要这样做。MovieTemplate是与您已有的模板相同的模板,仅在按钮为字符串的情况下提供模板。 最后,您设置了ItemTemplateSelector,如下所示

   <ListBox ItemsSource="{Binding lista}" ItemTemplateSelector="{StaticResource DTS}"/>

因此,你应该被展示出来

对类型使用
CompositeCollection
DataTemplate

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>
    <XmlDataProvider x:Key="MoviesData" XPath="Movies/Movie">
      <x:XData>
      <Movies xmlns="">
        <Movie Title="The Shawshank Redemption" />
        <Movie Title="The Godfather" />
        <Movie Title="The Dark Knight" />
      </Movies>
      </x:XData>
    </XmlDataProvider>
    <XmlDataProvider x:Key="MyButtonsData" XPath="MyButtons/MyButton">
      <x:XData>
      <MyButtons xmlns="">
        <MyButton Title="Add Movie" />
      </MyButtons>
      </x:XData>
    </XmlDataProvider>
    <DataTemplate DataType="Movie">
      <Button Content="{Binding XPath=@Title}"
              Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                        Path=DataContext.PlayMovieCommand}"
              CommandParameter="{Binding Id}" />
  </DataTemplate>
    <DataTemplate DataType="MyButton">
      <Button Content="{Binding XPath=@Title}"
              Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                        Path=DataContext.AddMovieCommand}" />
  </DataTemplate>  

  </Page.Resources>
    <ListBox>
      <ListBox.ItemsSource>
        <CompositeCollection>
        <CollectionContainer Collection="{Binding Source={StaticResource MoviesData}}"/>
        <CollectionContainer Collection="{Binding Source={StaticResource MyButtonsData}}"/>
        </CompositeCollection>
      </ListBox.ItemsSource>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True"
                           Width="{Binding (FrameworkElement.ActualWidth),
                                   RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Page>

以下是
Kaxaml


我花了一些时间将列表与视图模型绑定,因为FindAncestor和ElementName都没有给我列表。但是,当我将CollectionViewSource添加到我的资源中并绑定到该源时,我就得到了我的电影列表。尽管如此,我还是必须实现另一个解决方案中建议的DataTemplateSelector我接受这个答案,因为它为我提供了如何向列表中添加多个源的基本概念。您不需要实现DataTemplateSelector。您需要研究
DataTemplate类的
DataType
属性。好吧,在窗口中声明两个DataTemplate具有DataType属性。资源(或ListBox.Resources)是不够的。我没有看到电影的标题(我的意思是我看到了默认的模板,这在这里并不令人满意)。当我将DataTemplate放入List.ItemTemplate时,我只能使用其中一个,但至少我可以看电影。使用DataTemplateSelector解决了这个问题。上面的示例说明了另一个问题。您的回答对它的运行非常有帮助。然而,我不喜欢绑定对象类型的属性,它代表所有可能的数据(我想这就是你说的,对吧?)。我选择了另一个答案中提供的CompositeCollection解决方案,并将其与DataTemplateSelector相结合+1无论如何