使用MVVM中的自定义ItemTemplate将对象列表绑定到ItemsControl

时间:2012-07-27 20:06:59

标签: c# wpf data-binding grouping itemscontrol

当前设置

我有一个表示安装程序文件的自定义类和有关该文件的一些属性,符合以下界面

public interface IInstallerObject
{
    string FileName { get; set; }
    string FileExtension { get; set; }
    string Path { get; set; }
    int Build { get; set; }
    ProductType ProductType { get; set; }
    Architecture ArchType { get; set; }
    bool Configurable { get; set; }
    int AverageInstallTime { get; set; }
    bool IsSelected { get; set; }
}

我的ViewModel有一个名为ReadOnlyObservableCollection<IInstallerObject>的{​​{1}}媒体资源。

我的AvailableInstallerObjects有一个View,其中包含与上述财产绑定的GroupBox

ItemsControl

绑定工作正常,但用户不友好。显示了100多个项目。

需要帮助

我希望能够使用我的 <GroupBox Header="Products"> <ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <CheckBox IsChecked="{Binding Path=IsSelected}" VerticalAlignment="Center" Margin="5"/> <TextBlock Text="{Binding Path=FileName}" Margin="5" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </GroupBox> 集合,但让IInstallerObject使用以下View结构显示它们。

ItemTemplate

基本上我希望能够按 <GroupBox Header="Products"> <ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <CheckBox IsChecked="{Binding Path=IsSelected}" VerticalAlignment="Center" Margin="5"/> <TextBlock Text="{Binding Path=ProductType}" Margin="5" /> <ComboBox ItemsSource="{Binding Path=Build}" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </GroupBox> 属性进行分组,显示可用产品的列表,ProductType表示ComboBox的可用Build属性值} {s} IInstallerObject

我可以在ProductType中使用LINQ来提取分组,但我不知道我是如何绑定到我提取的内容。

我的研究也发现了使用ViewModel的可能性,但我不确定如何将其应用到我当前的设置中。

我提前感谢您的帮助。我愿意学习,如果我忽略了一些显而易见的事情,请指导我这些信息,我很乐意教育自己。

3 个答案:

答案 0 :(得分:0)

如果Build应该是一个集合类型。

所以你的课程应该像这样结构化。

Public Class Customer
 Public Property FirstName as string
Public Property LastName as string
Public Property CustomerOrders as observableCollection(OF Orders)
End Class

这应该会给你预期的结果。主要项目演示者中的每个项目将显示名字姓氏和绑定到该客户订单的组合框。 我知道这很简单但是应该这样做。

答案 1 :(得分:0)

您所要做的就是在视图中声明CollectionViewSource并将其绑定到ObservableCollection。在此对象中,您声明一个或多个GroupDescriptions,它将源分成几个组。

将此源​​绑定到列表框,为组描述创建模板,您就完成了。

可在此处找到一个示例:WPF Sample Series – ListBox Grouping, Sorting, Subtotals and Collapsible Regions。有关CollectionViewSource的更多信息,请访问:WPF’s CollectionViewSource

答案 2 :(得分:0)

对您的问题的描述让我相信您正在寻找某种类型的colapsing / expansion / groups / tree-view。

树视图的XAML

<Window x:Class="WPFLab12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:WPFLab12"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
      <GroupBox Header="Products">
        <TreeView ItemsSource="{Binding Path=ProductTypes}">
            <TreeView.Resources>
                <HierarchicalDataTemplate 
                    DataType="{x:Type loc:ProductType}"
                    ItemsSource="{Binding AvailableInstallerObjects}">
                    <TextBlock Text="{Binding Description}" />
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="{x:Type loc:InstallerObject}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding Path=IsSelected}" 
                                  VerticalAlignment="Center" Margin="5"/>
                        <TextBlock Text="{Binding Path=FileName}" Margin="5" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
      </GroupBox>
    </Grid>
</Window>

那是做什么的?好吧,它根据找到的数据类型在树中建立控件层次结构。第一个HierarchicalDataTemplate处理如何显示每个类的数据,以及它们在层次结构中的相关性。第二个HierarchicalDataTemplate处理如何显示每个InstallerObject

主窗口的代码隐藏:

public partial class MainWindow : Window
{
    public ReadOnlyObservableCollection<ProductType> ProductTypes
    {
        get { return (ReadOnlyObservableCollection<ProductType>)GetValue(ProductTypesProperty); }
        set { SetValue(ProductTypesProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ProductTypes.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ProductTypesProperty =
        DependencyProperty.Register("ProductTypes", typeof(ReadOnlyObservableCollection<ProductType>), typeof(MainWindow), new UIPropertyMetadata(null));

    public MainWindow()
    {
        this.InitializeComponent();

        this.ProductTypes = new ReadOnlyObservableCollection<ProductType>(
            new ObservableCollection<ProductType>()
            {
                new ProductType() 
                { 
                    Description = "Type A",
                    AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
                        new ObservableCollection<InstallerObject>()
                        {
                            new InstallerObject() { FileName = "A" },
                            new InstallerObject() { FileName = "B" },
                            new InstallerObject() { FileName = "C" },
                        })
                },

                new ProductType() 
                { 
                    Description = "Type B",
                    AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
                        new ObservableCollection<InstallerObject>()
                        {
                            new InstallerObject() { FileName = "A" },
                            new InstallerObject() { FileName = "D" },
                        })
                }
            });

        this.DataContext = this;
    }
}

这完全是作弊 - 通常MainWindow.cs不会作为DataContext而拥有所有这些东西。但是对于这个例子,我只是制作了ProductType的列表,并使用ProductType个实例填充每个InstallerObject类。

我使用过的类,注意我做了一些假设并修改了你的类以更好地适应这个View Model:

public class InstallerObject
{
    public string FileName { get; set; }
    public string FileExtension { get; set; }
    public string Path { get; set; }
    public int Build { get; set; }
    public bool Configurable { get; set; }
    public int AverageInstallTime { get; set; }
    public bool IsSelected { get; set; }
}

public class ProductType
{
    public string Description { get; set; }
    public ReadOnlyObservableCollection<InstallerObject> AvailableInstallerObjects
    {
        get;
        set; 
    }
    public override string ToString()
    {
        return this.Description;
    }
}

所以,在MVVM中,在我看来,你当前的InstallerObject类更像是一个模型层。您可以考虑将ViewModel中的转换为一组易于在View中管理的集合类。 ViewModel中的想法是对事物进行建模,类似于它们将如何被查看和交互。将您的InstallerObjects平面列表转换为新的分层数据集合,以便更轻松地绑定到视图。

有关使用和自定义TreeView的各种方法的更多信息:http://www.codeproject.com/Articles/124644/Basic-Understanding-of-Tree-View-in-WPF