WPF ListBox with multiple sources of different types

Deadly 提交于 2019-12-12 04:47:08

问题


I was actually setting up a sample application for something entirely different, but then I was trying this:

I have a collection of Movies. I'll have a list box which displays all the movies. The list box, however, provides them as buttons, so that you can click onto a button and play the movie.

The code is:

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

Then I was thinking of adding a single Button to the end, with the text "Add", and when I click onto that button, I can add a new movie.

I don't find a solution to provide this. While searching the Internet I found HierarchicalDataTemplate and CompositeCollection; both looking promising at first, but I failed to get it working as I want. I was thinking of MultiBinding, too, but again I seem to fail.

So, I guess my question is:
How can I add a single Add-button to my collection of movies?

Or more generic: How can I add several sources/collections of data of different types to a list box?


回答1:


Use CompositeCollection and DataTemplate for types.

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

Here is the result in Kaxaml




回答2:


You can create List of objects, add Movies and then add another type for instance "string" object. Then you need to create class deriving from DataTemplateSelector which goes throughout List and determines whether element is Movie object. If so then it returns proper DataTemplate otherwise it return DataTemplate for button.

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

then in XAML you do

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

Bear in mind that ButtonTemplate and MovieTemplate must be created before applying them since StaticResource demands this. MovieTemplate is the same template which you already have, only provide template in case of string for your Button. At the end you set ItemTemplateSelector as follows

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

As a result you are supposed to be displayed



来源:https://stackoverflow.com/questions/25413252/wpf-listbox-with-multiple-sources-of-different-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!