2010-08-31 59 views
17

我使用(玩家)綁定到CollectionViewSource一個DataGrid本身結合到列表框的當前選擇的項目(水平),其中包含每個項集合進行排序/在DataGrid中顯示:CollectionViewSource排序僅在第一次它被綁定到一個源

<ListBox Name="lstLevel" 
     DisplayMemberPath="Name" 
     IsSynchronizedWithCurrentItem="True" /> 

...

<!-- DataGrid source, as a CollectionViewSource to allow for sorting and/or filtering --> 
<CollectionViewSource x:Key="Players" 
         Source="{Binding ElementName=lstLevel, 
             Path=SelectedItem.Players}"> 
    <CollectionViewSource.SortDescriptions> 
    <scm:SortDescription PropertyName="Name" /> 
    </CollectionViewSource.SortDescriptions> 
</CollectionViewSource> 

...

<DataGrid Name="lstPlayers" AutoGenerateColumns="False" 
      CanUserSortColumns="False" 
      ItemsSource="{Binding Source={StaticResource Players}}"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Name" 
          Binding="{Binding Path=Name, Mode=TwoWay}" 
          Width="*" /> 
     <DataGridTextColumn Header="Age" 
          Binding="{Binding Path=Age, Mode=TwoWay}" 
          Width="80"> 
     </DataGridTextColumn> 
    </DataGrid.Columns> 
    </DataGrid> 

(整個C#代碼here,XAML代碼here,整個測試項目here - 除了DataGrid中我添加了一個簡單的列表框的球員,以確保它不是一個DataGrid問題)

問題是玩家在第一次被顯示時被排序,但是一旦我從ListBox中選擇另一個級別,他們就不會被排序了。此外,在第一次顯示玩家時修改姓名會根據變化對他們進行排序,但一旦水平發生變化就不會再進行排序。

所以它看起來像改變CollectionViewSource的源不知何故打破排序功能,但我不知道爲什麼,也不知道如何解決它。有誰知道我做錯了什麼?

(我做了過濾器的測試,但一個一直工作如預期)

框架是.NET 4

+0

我以前經歷過同樣的事情 - 而不是每次創建一個新的對象,你可以刪除並重新插入其內容? – Dave 2010-08-31 15:30:18

+0

除了額外的工作之外,通過創建/釋放對象會不必要地對託管堆進行碎片化,如果可以的話,我寧願避免它。 – RedGlyph 2010-08-31 18:58:28

回答

10

大問題,一個有趣的觀察。仔細檢查後,DataGrid看起來會在設置新項之前清除之前ItemsSource的排序描述。下面是其OnCoerceItemsSourceProperty代碼:

private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue) 
{ 
    DataGrid grid = (DataGrid) d; 
    if ((baseValue != grid._cachedItemsSource) && (grid._cachedItemsSource != null)) 
    { 
     grid.ClearSortDescriptionsOnItemsSourceChange(); 
    } 
    return baseValue; 
} 

此行爲僅發生在一個DataGrid。如果您使用ListBox(顯示上面的「Players」集合),行爲將會不同,並且從父數據網格中選擇不同的項目後,SortDescriptions仍將保留。

所以我猜這個解決方案是在父DataGrid中的選定項(即「lstLevel」)發生改變時,以某種方式重新應用Players集合的排序描述。

但是,我不是100%確定這一點,可能需要更多的測試/調查。我希望我能夠貢獻一些東西。 =)

編輯:

作爲建議的解決方法,你可以把對lstLevel.SelectionChanged處理程序在你的構造,設置lstLevel.ItemsSource屬性之前。事情是這樣的:

lstLevel.SelectionChanged += 
    (sender, e) => 
    { 
     levels.ToList().ForEach((p) => 
     { 
      CollectionViewSource.GetDefaultView(p.Players) 
       .SortDescriptions 
       .Add(new SortDescription("Name", ListSortDirection.Ascending)); 
     }); 
    }; 

lstLevel.ItemsSource = levels; 

EDIT2:

在回答你至於鍵盤導航遇到的問題,我建議,而不是處理「CurrentChanged」事件,你處理lstLevel .SelectionChanged事件代替。我發佈了您需要在下面進行的必要更新。只需複製粘貼到您的代碼,看看它是否正常工作。

XAML:

<!-- Players data, with sort on the Name column --> 
<StackPanel Grid.Column="1"> 
    <Label>DataGrid:</Label> 
    <DataGrid Name="lstPlayers" AutoGenerateColumns="False" 
     CanUserSortColumns="False" 
     ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Name" 
         Binding="{Binding Path=Name, Mode=TwoWay}" 
         Width="*" /> 
      <DataGridTextColumn Header="Age" 
         Binding="{Binding Path=Age, Mode=TwoWay}" 
         Width="80"> 
      </DataGridTextColumn> 
     </DataGrid.Columns> 
    </DataGrid> 
</StackPanel> 

<StackPanel Grid.Column="2"> 
    <Label>ListBox:</Label> 
    <ListBox ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}" DisplayMemberPath="Name" /> 
</StackPanel> 

代碼隱藏(構造函數):

lstLevel.SelectionChanged += 
    (sender, e) => 
    { 
     levels.ToList().ForEach((p) => 
     { 
      CollectionViewSource.GetDefaultView(p.Players) 
       .SortDescriptions 
       .Add(new SortDescription("Name", ListSortDirection.Ascending)); 
     }); 
    }; 
lstLevel.ItemsSource = levels; 
+0

++,非常有見識!我添加了一個單獨的ListBox,因爲我不信任DataGrid,但我沒有嘗試單獨測試沒有DataGrid的ListBox。我使用這個控件的次數越多,它證明的越奇怪(過去2天中已經報告了3個錯誤)。我會從建議開始並讓你知道。謝謝! – RedGlyph 2010-08-31 19:04:28

+1

改編了幾位(沒有按原樣工作,但足夠接近),用變通方法更新了問題。再次感謝! – RedGlyph 2010-08-31 20:08:47

+0

很高興幫助。是的,我發現DataGrid在後臺清除SortDescriptions是相當令人驚訝的。我不是很確定爲什麼,但可能有一個很好的理由,它是這樣做的...或者是的,也許這是一個錯誤。 =) – ASanch 2010-08-31 20:14:24

4

一個更好的解決辦法: CollectionViewSource sorting only the first time it is bound to a source

實現自己的DataGrid:

public class SDataGrid : DataGrid 
{ 
    static SDataGrid() 
    { 
     ItemsControl.ItemsSourceProperty.OverrideMetadata(typeof(SDataGrid), new FrameworkPropertyMetadata((PropertyChangedCallback)null, (CoerceValueCallback)null)); 
    } 
} 

當前實現中唯一強制回調函數是 ,用於清除排序描述。你可以簡單地通過 覆蓋元數據來「剪切」這段代碼。在Silverlight上不可行:OverrideMetadata API 未公開。雖然我不確定Silverlight是否受此 錯誤影響。其他風險和副作用可能適用。

+0

儘管有報道稱這種解決方案無效,但它似乎是在我的代碼中完成的。 – linac 2016-07-06 15:24:23

5

我可以通過簡單地調用PropertyChanged來暴露視圖的屬性來解決這個問題,讓視圖刷新(並清除排序),然後添加排序描述。

+0

+ 1.這似乎也適用於我。最簡單的解決方案,沒有任何代碼麻煩。謝謝! – Anttu 2013-09-03 10:12:44