2016-10-31 11 views
1

Если я сортирую один столбец в 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> 
+0

ли вы попробовать реализовать 'IComparable' на элементы в массиве? Предполагая, что вы можете выяснить, какой массив он говорит. Но вы можете наблюдать многое, глядя на окно часов. –

+0

Группировка и сортировка относится к модели View not view, тогда вы можете использовать ее с помощью 'xaml' вместо кода позади. Я использую его со списками и DataTables, и он всегда работал. Можете ли вы показать нам, как вы создаете «CVS» от начала до точки «Binding»? – XAMlMAX

+0

Я не пытался реализовать IComparable, потому что есть несколько столбцов, которые можно сортировать. Кроме того, существуют классы, представляющие строки и классы, представляющие группу. Я просто хотел удалить сортировку, когда datagrid сгруппирован. –

ответ

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 в порядке сортировки.
  • Zero: Этот экземпляр встречается в том же положении в порядке сортировки, что и obj.
  • Больше нуля: Этот экземпляр следует obj в порядке сортировки.

EDIT:

В случае HeaderBindingSource вы также можете использовать:

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

спасибо, но если у меня 5 столбцов, как узнать, какое свойство я должен сравнивать? Кроме того, DataGrid уже может сравнивать их без реализации IComparable. это группировка, которая вызывает исключение. –

+0

Я думаю, что, по крайней мере, каждое свойство, которое вы хотели бы «группировать» или «сортировать по», должно реализовать «ICompareable». Стандартные типы (например, int делают это). Используете ли вы некоторые свойства других типов классов (пользовательские классы ...)? Реализация 'IComparable' в' TestItem' является своего рода сортировкой по умолчанию (если свойство не выбрано). Возможно, он отсутствует, и когда вы: 'MyCollectionViewSource.SortDescriptions.Clear();' элементы будут отсортированы, так как TestItem будет отсортирован по его реализации «IComparable». – WPFGermany

+0

Я получаю такое же исключение, даже если я не делаю MyCollectionViewSource.SortDescriptions.Clear(); дело в том, что я не реализовал какую-либо пользовательскую сортировку. он просто не отключен в xaml. если никто не сможет помочь мне просто отключить его в xaml. WPF и особенно WPF DataGrid не имеют всех базовых/стандартных функций. группировка не должна прерывать сортировку. –