如何在打开分组时关闭WPF DataGrid排序?

时间:2016-10-31 19:06:51

标签: wpf datagrid wpfdatagrid collectionviewsource

如果我在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>

1 个答案:

答案 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可以专注于你的每个属性或领域....

返回值应为:

  • 小于零:此实例在排序顺序中位于obj之前。
  • 零:此实例与obj在排序顺序中的位置相同。
  • 大于零:此实例遵循排序顺序中的obj。

修改

如果是HeaderBindingSource,您也可以使用:

public GroupHeaderBindingSource(string name, string description, GroupHeadersInfo headersInfo) : BindingSource, IGroupHeader, IComparable
{
    return 0; 
}