Если я сортирую один столбец в DataGrid (который ItemsSource привязан к CollectionViewSource) и превратить группировку на в ViewModelКак отключить сортировку WPF DataGrid при включении группировки?
MyCollectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription("Table"));
я получаю исключение:
Необработанное исключение в командной обработчиком системы «GroupDataGrid». InvalidOperationException: Не удалось сравнить два элемента в массиве. ---> System.ArgumentException: по крайней мере один объект должен реализовать IComparable.
Однако перед добавлением GroupDescription я пытаюсь удалить сортировки:
MyCollectionViewSource.SortDescriptions.Clear();
По какой-то причине этого не достаточно. Stacktrace:
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)
EDIT: Код, добавленный reaquest. SetValue в классах BindingSource/ViewModel приводит к событию 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;
}
}
ViewModel:
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>
ли вы попробовать реализовать 'IComparable' на элементы в массиве? Предполагая, что вы можете выяснить, какой массив он говорит. Но вы можете наблюдать многое, глядя на окно часов. –
Группировка и сортировка относится к модели View not view, тогда вы можете использовать ее с помощью 'xaml' вместо кода позади. Я использую его со списками и DataTables, и он всегда работал. Можете ли вы показать нам, как вы создаете «CVS» от начала до точки «Binding»? – XAMlMAX
Я не пытался реализовать IComparable, потому что есть несколько столбцов, которые можно сортировать. Кроме того, существуют классы, представляющие строки и классы, представляющие группу. Я просто хотел удалить сортировку, когда datagrid сгруппирован. –