2016-10-31 29 views
1

如果我有點DataGrid中(其中ItemsSource綁定到CollectionViewSource),並把在視圖模型如何在打開分組時打開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) 

編輯:代碼由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; 
    } 
} 

類頁眉:

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

你嘗試實現在數組中的元素'IComparable'?假設你可以找出它正在談論的陣列。但你可以通過觀察窗口觀察很多東西。 –

+0

分組和排序屬於查看不查看模型,那麼你可以使用'xaml'代替後面的代碼。我一直使用它與列表和數據表,它一直工作。你能告訴我們如何從開始到'綁定'點創建'CVS'? – 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在排序順序。
  • 零:此實例發生在與obj相同的排序順序中。
  • 大於零:此實例遵循排序順序中的obj。

編輯:

HeaderBindingSource情況下,你也可以使用:

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

謝謝,但如果我有5列我如何知道我應該比較哪個屬性?除此之外,DataGrid可以在不實現IComparable的情況下對它們進行比較。它是導致異常的分組。 –

+0

我想至少你想'group'或'sort by'的每個屬性都需要實現'ICompareable'。標準類型(如int這樣做)。您是否使用其他類類型的某些屬性(自定義類...)? 'TestItem'中的'IComparable'實現是一種默認排序(如果沒有選擇屬性)。也許它丟失了,當你:'MyCollectionViewSource.SortDescriptions.Clear();'這些項目將被排序,因爲TestItem是通過它的'IComparable'實現排序的。 – WPFGermany

+0

即使我沒有做MyCollectionViewSource.SortDescriptions.Clear(),我也會得到同樣的異常。事情是我沒有實現任何自定義排序。它只是沒有禁用xaml。如果沒有人能幫助我只需要在xaml中禁用它。 WPF和特別是WPF DataGrid缺乏所有基本/默認功能。分組不應該破壞排序。 –

相關問題