ItemsControl的引用分组控件

时间:2012-12-05 14:32:36

标签: .net itemscontrol expander listcollectionview

我有一个带有ListCollectionView的ItemControl作为数据源。 ItemControl使用Expander控件对项目进行分组。当我执行ListCollectionView.Refresh()时,扩展的Expander控件将被折叠。如何扩展展开的控件?

<ItemsControl HorizontalAlignment="Stretch" ItemsSource="{Binding}" Name="ItemsControl1">
        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type GroupingWithExpander:DataItem}">
                <TextBlock Text="{Binding Text}" />
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="GroupItem">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="GroupItem">
                                    <Expander BorderThickness="1" BorderBrush="Gray" IsExpanded="{Binding IsExpanded}">
                                        <Expander.Resources>
                                            <GroupingWithExpander:DataGroupToNameConverter x:Key="DataGroupToNameConverter" />
                                        </Expander.Resources>
                                        <Expander.Header>
                                            <TextBlock Text="{Binding Name, Converter={StaticResource DataGroupToNameConverter}}" />
                                        </Expander.Header>
                                        <StackPanel Orientation="Vertical">
                                            <ItemsPresenter Margin="5 0" />
                                        </StackPanel>
                                    </Expander>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </ItemsControl.GroupStyle>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel Orientation="Vertical" IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    <TextBox Name="TextBox1" />
    <Button Click="Button_Click">
        <Button.Content>
            <TextBlock Text="do something" />
        </Button.Content>
    </Button>



 public partial class MainWindow : Window{
public MainWindow()
            {
                InitializeComponent();
                var group1 = new DataGroup {IsExpanded = false, Name = "group #1"};
                var group2 = new DataGroup {IsExpanded = true, Name = "group #2"};
                const int itemsCount = 6;
                DataItem[] dataItems = new DataItem[itemsCount];
                for (int i = 0; i < itemsCount; i++)
                {
                    DataItem item = new DataItem
                                        {
                                            Group = (i%2 == 0 ? group1 : group2),
                                            Text = System.IO.Path.GetRandomFileName()
                                        };
                    dataItems[i] = item;
                }
                ListCollectionView v = new ListCollectionView(dataItems);            
                v.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
                v.Filter = FilterDataItem;
                this.DataContext = v;            
            }

            private bool FilterDataItem(object o)
            {
                DataItem item = o as DataItem;
                return item.Contains(this.TextBox1.Text);            
            }

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                ListCollectionView v = (ListCollectionView) this.DataContext;
                v.Refresh();
            }
    }

    class DataGroup : IEquatable<DataGroup>, INotifyPropertyChanged
{
    public string Name { get; set; }

    private bool _isExpanded;
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set 
        { 
            _isExpanded = value; 
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("IsExpanded"));
        }
    }

    public bool Equals(DataGroup other)
    {
        return other != null && this.Name == other.Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

    class DataItem
{
    public string Text { get; set; }
    public DataGroup Group { get; set; }

    public virtual bool Contains(string filterString)
    {
        return Text != null && (string.IsNullOrEmpty(filterString) || Text.Contains(filterString));
    }
}




    class DataGroupToNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is DataGroup)) throw new ArgumentException("type DataGroup is expected", "value");
        DataGroup g = (DataGroup) value;
        return g.Name;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

1 个答案:

答案 0 :(得分:1)

你非常接近一个有效的解决方案。关键是要知道组模板中控件的DataContext将是CollectionViewGroup(实际上是CollectionViewGroup的后代,因为CollectionViewGroup是抽象的)。当框架绑定到应用了分组的​​数据源时,框架会为您创建这些组。

Items有一个名为CollectionViewGroup的属性,该属性是一个集合,其中包含属于Items所代表的组的项目。由于每个小组至少会有一个成员,因此您可以使用DataGroup的第一个成员来访问Items[0].Group.IsExpanded,例如Name,但有更简单的方法。

CollectionViewGroup还有一个名为string的误导性属性。乍一看,您可能会认为它是一个包含该组名称的object,但它实际上是一个Group,其中包含对您告诉数据源的实例的引用分组。

在您的示例中,您已告诉它按DataGroup属性进行分组,该属性为Name,因此DataGroup实际上会为DataGroup实例指定该属性基。

因此,您可以通过在绑定中使用虚线路径来获取<Expander IsExpanded="{Binding Name.IsExpanded}"> <Expander.Header> <TextBlock Text="{Binding Name.Name}" /> </Expander.Header> <StackPanel Orientation="Vertical"> <ItemsPresenter Margin="5 0" /> </StackPanel> </Expander> 的属性,即:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ListCollectionView v = (ListCollectionView)this.DataContext;

    var groups = new List<DataGroup>();
    for (int i = 0; i < v.Count; i++)
    {
        var item = v.GetItemAt(i) as DataItem;
        if (item != null)
        {
            if (!groups.Contains(item.Group))
            {
                groups.Add(item.Group);
            }
        }
    }

    foreach (var group in groups)
    {
        group.IsExpanded = !group.IsExpanded;
    }

    v.Refresh();
}

我做了这些更改,现在你的例子运行正常。您也不再需要值转换器了。

我还更改了您的按钮单击事件,以证明绑定在两个方向上都有效(当您单击按钮时,它会切换每个组的扩展状态):

{{1}}

我刚刚使用分组的ComboBox进行了类似的练习,所以我正在回答这个问题,希望虽然它可能不再与您相关,但它可能会帮助处于类似困境的其他人。