如果我在DataGrid中排序一列(ItemsSource绑定到CollectionViewSource)并在ViewModel中打开分组
MyCollectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription("Table"));
我得到例外:
命令处理程序“GroupDataGrid”中的未处理异常System.InvalidOperationException:无法比较数组中的两个元素。 ---> System.ArgumentException:至少有一个对象必须实现IComparable。
然而,在添加GroupDescription之前,我尝试删除排序:
MyCollectionViewSource.SortDescriptions.Clear();
由于某种原因,这还不够。堆栈跟踪:
at System.Collections.Comparer.Compare(Object a, Object b)
at MS.Internal.Data.SortFieldComparer.Compare(Object o1, Object o2)
at System.Array.SorterGenericArray.SwapIfGreaterWithItems(Int32 a, Int32 b)
at System.Array.SorterGenericArray.PickPivotAndPartition(Int32 lo, Int32 hi)
at System.Array.SorterGenericArray.IntroSort(Int32 lo, Int32 hi, Int32 depthLimit)
at System.Array.SorterGenericArray.IntrospectiveSort(Int32 left, Int32 length)
--- End of inner exception stack trace ---
at System.Array.SorterGenericArray.IntrospectiveSort(Int32 left, Int32 length)
at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer)
at System.Array.Sort(Array array, IComparer comparer)
at MS.Internal.Data.SortFieldComparer.SortHelper(ArrayList al, IComparer comparer)
at MS.Internal.Data.DataExtensionMethods.Sort(IList list, IComparer comparer)
at System.Windows.Data.ListCollectionView.PrepareLocalArray()
at System.Windows.Data.ListCollectionView.RefreshOverride()
at System.Windows.Data.CollectionView.RefreshInternal()
at System.Windows.Data.CollectionView.RefreshOrDefer()
at System.Windows.Data.ListCollectionView.SortDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.ComponentModel.SortDescriptionCollection.OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index)
at System.ComponentModel.SortDescriptionCollection.InsertItem(Int32 index, SortDescription item)
at System.Collections.ObjectModel.Collection`1.Insert(Int32 index, T item)
at System.Windows.Controls.ItemCollection.SynchronizeCollections[T](NotifyCollectionChangedEventArgs e, Collection`1 origin, Collection`1 clone)
at System.Windows.Controls.ItemCollection.SortDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.ComponentModel.SortDescriptionCollection.OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index)
at System.ComponentModel.SortDescriptionCollection.InsertItem(Int32 index, SortDescription item)
at System.Collections.ObjectModel.Collection`1.Insert(Int32 index, T item)
at System.Windows.Controls.DataGrid.AddGroupingSortDescriptions()
at System.Windows.Controls.DataGrid.OnItemsGroupDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
at System.Collections.ObjectModel.Collection`1.Insert(Int32 index, T item)
at System.Windows.Controls.ItemCollection.SynchronizeCollections[T](NotifyCollectionChangedEventArgs e, Collection`1 origin, Collection`1 clone)
at System.Windows.Controls.ItemCollection.OnInnerGroupDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Windows.WeakEventManager.ListenerList`1.DeliverEvent(Object sender, EventArgs e, Type managerType)
at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args)
at System.Collections.Specialized.CollectionChangedEventManager.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChange(NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.InsertItem(Int32 index, T item)
at System.Collections.ObjectModel.Collection`1.Add(T item)
编辑:代码由reaquest添加。 BindingSource / ViewModel类中的SetValue导致PropertyChanged事件。
行的类:
public class SelectedNetObjectBindingSource : NetObjectBindingSource
{
public SelectedObjectsTableGroupBindingSource Table { get; set; }
public SelectedNetObjectBindingSource(NetObject netObject, SelectedObjectsTableGroupBindingSource table)
: base(netObject)
{
Table = table;
}
}
其父类:
public class NetObjectBindingSource : BindingSource
{
public NetObject Data { get; protected set; }
public NetObjectBindingSource(NetObject netObject)
{
Data = netObject;
}
}
groupheader的类:
public class SelectedObjectsTableGroupBindingSource : GroupHeaderBindingSource
{
public NetObjectTableId TableId { get; private set; }
public SelectedObjectsTableGroupBindingSource(NisTable nisTable, GroupHeadersInfo headersInfo)
: base(nisTable.TableId.ToString(), nisTable.Description, headersInfo)
{
TableId = nisTable.TableId;
}
}
它的父类:
public class GroupHeaderBindingSource : BindingSource, IGroupHeader, IComparable
{
public string Name { get; private set; }
public string Description { get; private set; }
public int GroupedCount { get; set; }
private GroupHeadersInfo _headersInfo;
private bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (SetValue(ref _isExpanded, value))
{
NisDllInterface.SetUserSetting(_headersInfo.GroupSection, Name, value ? "True" : "False");
_headersInfo.UpdateExpandedInfo(value);
}
}
}
public GroupHeaderBindingSource(string name, string description, GroupHeadersInfo headersInfo)
{
_headersInfo = headersInfo;
Name = name;
Description = description;
var value = UserSettings.Instance.GetSetting(headersInfo.GroupSection, Name);
if (value != null)
{
if (value.Equals("False"))
{
IsExpanded = false;
}
else
{
IsExpanded = true;
}
}
else
{
IsExpanded = true;
}
}
public int CompareTo(object obj)
{
if (obj != null && obj is GroupHeaderBindingSource)
{
var groupHeader = (GroupHeaderBindingSource) obj;
return Description.CompareTo(groupHeader.Description);
}
return 0;
}
}
视图模型:
public ICollectionView SelectedObjectItems
{
get
{
return _selectedObjectsView;
}
set
{
this.SetValue(ref _selectedObjectsView, value);
}
}
public bool AreSelectedObjectsGroupedByTable
{
get
{
return _areSelectedObjectsGroupedByTable;
}
set
{
this.SetValue(ref _areSelectedObjectsGroupedByTable, value);
if (_areSelectedObjectsGroupedByTable)
SelectedObjectItems.GroupDescriptions.Add(new PropertyGroupDescription("Table"));
else
SelectedObjectItems.GroupDescriptions.Clear();
SaveObjectsSetting(GroupedSettingName, _areSelectedObjectsGroupedByTable ? "True" : "False");
SelectedObjectsGroupHeaders.IsGrouped = value;
SelectedObjectItems.Refresh();
CallAfterSelectedObjectsRefresh(new GroupedRowsRefreshedEventArgs(
_areSelectedObjectsGroupedByTable ? GroupedGrid.Grouped : GroupedGrid.Ungrouped));
}
}
// in viewmodel constructor
SelectedObjectItems = CollectionViewSource.GetDefaultView(new List<NetObject>());
public void RefreshSelection()
{
RefreshSelectionObjects();
Selection = GetSelectedObjects();
// Selected objects
if (SelectedObjectsExpanded)
{
IsFetchObjectsAnywayVisible = false;
bool isTooManyObjects = SelectedObjectsInstance.Count > MaxSelectedObjectCount;
if (isTooManyObjects && !_fetchObjectsAnyway)
{
SelectedObjectsInfoPanelText = App.Current.Resources["MmsMsgTooManyObjectsSelectd"].ToString();
SelectedObjectItems = CollectionViewSource.GetDefaultView(new List<NetObject>());
ShowSelectedObjectsInfoPanel = true;
IsFetchObjectsAnywayVisible = true;
}
else if (SelectedObjectsInstance.Count == 0)
{
SelectedObjectsInfoPanelText = App.Current.Resources["NisStrNoObjectsSelected"].ToString();
SelectedObjectItems = CollectionViewSource.GetDefaultView(new List<NetObject>());
ShowSelectedObjectsInfoPanel = true;
}
else
{
SelectedObjectItems = CollectionViewSource.GetDefaultView(GetSelectedObjects().ToList());
if (_areSelectedObjectsGroupedByTable)
SelectedObjectItems.GroupDescriptions.Add(new PropertyGroupDescription("Table"));
else
SelectedObjectItems.GroupDescriptions.Clear();
ShowSelectedObjectsInfoPanel = false;
}
_selectedObjectsRefreshSkipped = false;
}
else
{
_selectedObjectsRefreshSkipped = true;
}
}
XAML:
<DataGrid Name="SelectedObjectsGrid"
Visibility="{Binding ShowSelectedObjectsInfoPanel, Converter={StaticResource ReverseBoolToVisibilityConverter}}"
ItemsSource="{Binding SelectedObjectItems}"
SelectionMode="Extended"
CanUserAddRows="False"
AutoGenerateColumns="False"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.VirtualizationMode="Standard"
IsReadOnly="True"
Grid.Row="0" Margin="0,0,4,0">
<DataGrid.Resources>
<ContextMenu x:Key="SelectedObjectRowContextMenu"
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{DynamicResource XpStrKeepSelectionTableShort}" Command="{ui:CommandHandler KeepTable}" CommandParameter="{Binding}"/>
<MenuItem Header="{DynamicResource XpStrRemoveSelectionTableShort}" Command="{ui:CommandHandler RemoveTable}" CommandParameter="{Binding}"/>
<MenuItem Header="{DynamicResource XpStrList}" Command="{ui:CommandHandler ToggleSelectedObjectsGrouping}" CommandParameter="False"
Visibility="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}" >
<MenuItem.Icon>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="Black"
Geometry="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource ReverseBoolToGeometryConverter}}" />
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource XpStrGroup}" Command="{ui:CommandHandler ToggleSelectedObjectsGrouping}" CommandParameter="True"
Visibility="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource ReverseBoolToVisibilityConverter}}" >
<MenuItem.Icon>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="Black"
Geometry="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToGeometryConverter}}" />
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</MenuItem.Icon>
</MenuItem>
<Separator Visibility="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}" />
<MenuItem Header="{DynamicResource XpStrHintTreeExpandAll}" Command="{ui:CommandHandler ExpandOrCollapseAll}" CommandParameter="True"
Visibility="{Binding DataContext.SelectedObjectsGroupHeaders.DoCollapsedGroupsExist, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}">
<MenuItem.Icon>
<Image Source="{Binding Converter={StaticResource nameToBitmapSource}}" DataContext="BmpTreeExpandAll"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource XpStrHintTreeCollapseAll}" Command="{ui:CommandHandler ExpandOrCollapseAll}" CommandParameter="False"
Visibility="{Binding DataContext.SelectedObjectsGroupHeaders.DoExpandedGroupsExist, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}">
<MenuItem.Icon>
<Image Source="{Binding Converter={StaticResource nameToBitmapSource}}" DataContext="BmpTreeCollapseAll"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource Theme.DataGrid.Row.Style}">
<Setter Property="ContextMenu" Value="{StaticResource SelectedObjectRowContextMenu}" />
<EventSetter Event="MouseDoubleClick" Handler="SelectedObjectsRow_DoubleClick" />
</Style>
</DataGrid.RowStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{ui:CommandHandler ObjectsGridSelectionChangedCommand}" CommandParameter="{Binding SelectedItems,ElementName=SelectedObjectsGrid}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding Path=Name.IsExpanded}"
Tag="{Binding Path=Name}"
Background="#FF112255" BorderBrush="#FF002255"
Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
<Expander.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="{DynamicResource XpStrKeepSelectionTableShort}" Command="{ui:CommandHandler KeepTable}" CommandParameter="{Binding Name}" />
<MenuItem Header="{DynamicResource XpStrRemoveSelectionTableShort}" Command="{ui:CommandHandler RemoveTable}" CommandParameter="{Binding Name}" />
<MenuItem Header="{DynamicResource XpStrList}" Command="{ui:CommandHandler ToggleSelectedObjectsGrouping}" CommandParameter="False" >
<MenuItem.Icon>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="Black" Geometry="{Binding DataContext.AreSelectedObjectsGroupedByTable,
Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource ReverseBoolToGeometryConverter}}" />
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</MenuItem.Icon>
</MenuItem>
<Separator Visibility="{Binding DataContext.AreSelectedObjectsGroupedByTable, Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}" />
<MenuItem Header="{DynamicResource XpStrHintTreeExpandAll}" Command="{ui:CommandHandler ExpandOrCollapseAll}" CommandParameter="True"
Visibility="{Binding DataContext.SelectedObjectsGroupHeaders.DoCollapsedGroupsExist,
Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}">
<MenuItem.Icon>
<Image Source="{Binding Converter={StaticResource nameToBitmapSource}}" DataContext="BmpTreeExpandAll"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource XpStrHintTreeCollapseAll}" Command="{ui:CommandHandler ExpandOrCollapseAll}" CommandParameter="False"
Visibility="{Binding DataContext.SelectedObjectsGroupHeaders.DoExpandedGroupsExist,
Source={x:Reference SelectedObjectsGrid}, Converter={StaticResource BoolToVisibilityConverter}}">
<MenuItem.Icon>
<Image Source="{Binding Converter={StaticResource nameToBitmapSource}}" DataContext="BmpTreeCollapseAll"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Expander.ContextMenu>
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Margin="5,0,0,0" IsHitTestVisible="False">
<Run Text="{Binding Path=Name.Description, Mode=OneWay}" />
<Run Text=" ("/>
<Run Text="{Binding Path=Name.GroupedCount, Mode=OneWay}" />
<Run Text=")"/>
</TextBlock>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter/>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="{DynamicResource XpStrLabel}" Binding="{Binding Data.Label}" />
<DataGridTextColumn Header="{DynamicResource XpStrClass}" Binding="{Binding Data.Class.Name}" />
</DataGrid.Columns>
</DataGrid>
答案 0 :(得分:0)
假设您的馆藏来源是ObservableCollection<NetObject>
,您需要实施以下内容:
public class NetObject : IComparable
{
// Your Items implementation ....
public int CompareProperty { get; set; }
public int CompareTo(object obj)
{
if(!(obj is NetObject)) throw new Exception("The Object can't been compared");
if (this.CompareProperty > ((NetObject)obj).CompareProperty) return 1;
if (this.CompareProperty < ((NetObject)obj).CompareProperty) return -1;
return 0;
}
}
CompareTo
可以专注于你的每个属性或领域....
返回值应为:
修改强>
如果是HeaderBindingSource
,您也可以使用:
public GroupHeaderBindingSource(string name, string description, GroupHeadersInfo headersInfo) : BindingSource, IGroupHeader, IComparable
{
return 0;
}