2010-08-31 19 views
17
Bir CollectionViewSource bağlı bir DataGrid ( oyuncu) kullanıyorum

, kendisinin bir ListBox seçili öğeye bağlı için (seviyeleri), içeren her öğe bağlanır bir koleksiyon DataGrid'de görüntülenen/sıralanmasını: ...CollectionViewSource bir kaynaktan

<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> 

-

(bütün C# kodu here, XAML kod here, tüm test projesi here DataGrid'e ek olarak ben emin bir DataGrid sorun değildi yapmak, oyuncular için basit bir liste kutusu ekledik)

Sorun, oyuncuların ilk gösterildikleri sıraya göre sıralandıklarıdır, ancak ListBox'tan başka bir seviye seçtiğimde, artık sıralanmazlar. Ayrıca, oyuncuların ilk kez gösterildiği isimleri değiştirmek, değişiklikleri değiştirdikten sonra değiştirecek, ancak seviye değiştikten sonra artık değişmeyecektir.

Bu nedenle, CollectionViewSource kaynağının değiştirilmesi bir şekilde sıralama özelliğini bozuyor gibi görünüyor, ancak neden veya nasıl düzeltileceği konusunda hiçbir fikrim yok. Yanlış yaptığımı bilen var mı?

(Ben bir filtre ile bir test yaptım, ancak bir beklendiği gibi çalışmaya devam)

çerçeve .NET 4.

+0

Aynı şeyi daha önce de yaşadım - her seferinde yeni bir nesne oluşturmak yerine, içeriğini kaldırabilir ve yeniden yerleştirebilir misiniz? – Dave

+0

Ek çalışmanın yanı sıra, nesneler oluşturarak/bırakarak yönetilen yığının gereksiz yere parçalanmasını engellerdim, yapabilseydim bundan kaçınmayı tercih ederim. – RedGlyph

cevap

10

Büyük soru ilginç bir gözlemdir. Daha yakından incelendiğinde, DataGrid'in yeni bir tane ayarlanmadan önce bir önceki ItemsSource'un sıralama açıklamalarını temizlediği görülür.

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

Bu davranış sadece DataGrid üzerinde gerçekleşir: Burada OnCoerceItemsSourceProperty için kodudur. Bunun yerine bir ListBox kullandıysanız (yukarıdaki "Oyuncular" koleksiyonunu görüntülemek için), davranış farklı olacaktır ve SortDescriptions, parent datagrid öğesinden farklı öğeleri seçtikten sonra da kalır.

Bu nedenle, buradaki çözüm, bir şekilde DataGrid (yani "lstLevel") öğesindeki seçili öğe değiştiğinde Oyuncular koleksiyonunun sıralama açıklamalarını yeniden uygulamaktır. Bununla birlikte, bu konuda% 100 emin değilim ve muhtemelen daha fazla test/araştırmaya ihtiyaç duyuyorum. Ancak, bu konuda% 100 emin değilim. Umarım bir şeyler yapabildim umarım. =)

DÜZENLEME: Önerilen bir çözüm olarak

, sen lstLevel.ItemsSource özelliğini ayarlamadan önce, Yapıcınızda içinde lstLevel.SelectionChanged için bir işleyici koyabilirsiniz. Böyle bir şey:

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

lstLevel.ItemsSource = levels; 

EDIT2: Klavye navigasyon açısından karşılaşıyorsanız sorunlara yanıt olarak

, yerine "CurrentChanged" olayını taşıma, sen lstLevel ele düşündürmektedir .SelectionChanged olayı yerine. Aşağıda yapmanız gereken gerekli güncellemeleri gönderiyorum.Kodunuza kopyalayıp yapıştırarak düzgün çalışıp çalışmadığını görün.

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> 

kod arkadaki (kurucu):

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

++, çok anlayışlı! DataGrid'e güvenmediğim için ayrı bir ListBox ekledim, ancak ListBox'u DataGrid olmadan tek başına denemeye çalışmadım. Bu kontrolü ne kadar çok kullanırsam, o kadar ilginç olur ki (3 hata zaten son 2 günde bildirilmiştir). Öneriden başlayacağım ve size bildireceğim. Teşekkürler! – RedGlyph

+1

Birkaç bit uyarlanmış (olduğu gibi çalışmadı, ancak yeterince yakın), geçici çözüm ile güncelleştirilmiş soru. Tekrar teşekkürler! – RedGlyph

+0

Yardım etmekten memnun oldum. Evet, DataGrid'in sahnelerin arkasındaki SortDescription'ları temizlemesini oldukça şaşırtıcı buldum. Neden olduğundan emin değilim ama muhtemelen bunu yapmanın iyi bir nedeni var ... ya da evet, belki de bir böcek. =) – ASanch

4

Daha iyi bir geçici çözüm: CollectionViewSource sorting only the first time it is bound to a source

Kendi DataGrid Uygulanışı:

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

Geçerli uygulamada zorlama geri çağrısı yapan tek şey, sıralama açıklamalarını temizlemesidir. Bu kodu meta verilerini geçersiz kılarak basitçe "kesebilirsiniz". Silverlight'ta geçerli değil: OverrideMetadata API herkese açık değil. Silverlight'ın bu hatasından etkilendiğinden emin değilim. Diğer riskler ve yan etkiler geçerli olabilir.

+0

Bu çözümün işe yaramayacağına dair raporlar olsa da, bu benim kodumda bir iş yapıyor gibi görünüyor. – linac

5

Bu özelliği, yalnızca görüntüyü açığa çıkaran, görünümün yenilenmesini (ve sıralamayı temizleyen) ve ardından sıralama açıklamalarını ekleyerek PropertyChanged özelliğini çağırarak giderebildim.

+0

+ 1.Bu da benim için çalışıyor gibi görünüyordu. Herhangi bir kod arkası zahmetine girmeden en basit çözüm. Teşekkürler! – Anttu

İlgili konular