我有一个带有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();
}
}
答案 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进行了类似的练习,所以我正在回答这个问题,希望虽然它可能不再与您相关,但它可能会帮助处于类似困境的其他人。