【问题标题】:reference grouping controls of ItemsControl引用 ItemsControl 的分组控件
【发布时间】:2012-12-05 14:32:35
【问题描述】:

我有一个带有 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();
    }
}

【问题讨论】:

    标签: .net itemscontrol expander listcollectionview


    【解决方案1】:

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

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

    CollectionViewGroup 还有一个有点误导的属性,叫做Name。乍一看,您可能会认为它是一个包含组名的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();
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多