【问题标题】:WPF ListBox with multiple sources of different types具有多个不同类型源的 WPF ListBox
【发布时间】:2014-08-20 19:48:44
【问题描述】:

我实际上是在为完全不同的东西设置一个示例应用程序,但后来我尝试这样做:

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

代码是:

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

然后我想在末尾添加一个按钮,带有文本“添加”,当我单击该按钮时,我可以添加一个新电影。

我找不到提供此功能的解决方案。在网上搜索时,我发现了HierarchicalDataTemplateCompositeCollection;起初看起来都很有希望,但我没能按我的意愿工作。我也在想MultiBinding,但我似乎又失败了。

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

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

【问题讨论】:

    标签: wpf listbox itemssource multibinding


    【解决方案1】:

    使用CompositeCollectionDataTemplate 作为类型。

    <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 classDataType 属性。
    • 好吧,在 Window.Resources(或 ListBox.Resources 中)声明两个具有 DataType 属性的 DataTemplate 是不够的。我没有看到电影的标题(我的意思是我看到了这里不令人满意的默认模板)。当我将 DataTemplate 放入我的 List.ItemTemplate 时,我只能使用其中一个,但至少我可以看到我的电影。使用 DataTemplateSelector 解决了这个问题。
    • 上面的例子说明了另一个。
    【解决方案2】:

    您可以创建对象列表,添加电影,然后添加另一种类型,例如“字符串”对象。然后您需要创建派生自 DataTemplateSelector 的类,该类贯穿整个 List 并确定元素是否为 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 与您已有的模板相同,仅在您的 Button 为字符串的情况下提供模板。 最后你设置 ItemTemplateSelector 如下

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

    因此,您应该被显示

    【讨论】:

    • 您的回答对于让它发挥作用非常有帮助。但是,我不喜欢将它绑定到表示所有可能数据的 object 类型的属性(我想这就是您所说的,对吧?)。我选择了另一个答案中提供的 CompositeCollection 解决方案,并将其与 DataTemplateSelector 结合使用。无论如何都要 +1
    猜你喜欢
    • 2019-08-31
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    相关资源
    最近更新 更多