2012-06-24 27 views
8

को अनदेखा करता है मुझे एक WPF DataGrid (System.Windows.Controls.DataGrid में .NET 4.0) को सॉर्ट करने के बारे में एक अजीब समस्या है।WPF DataGrid सॉर्टडिस्क्रिप्शन

इसके ItemsSource DataContext वस्तु की एक संपत्ति के लिए बाध्य है:

<DataGrid HeadersVisibility="Column" SelectedIndex="0" MinHeight="30" ItemsSource="{Binding FahrtenView}" AutoGenerateColumns="False" x:Name="fahrtenDG"> 

FahrtenView इस तरह दिखता है:

public ICollectionView FahrtenView 
    { 
     get 
     { 
      var view = CollectionViewSource.GetDefaultView(_fahrten); 
      view.SortDescriptions.Add(new SortDescription("Index", ListSortDirection.Ascending)); 
      return view; 
     } 
    } 

डेटा ग्रिड अनुसार क्रमबद्ध हो जाता है। हालांकि यह केवल पहली बार सॉर्ट किया जाता है जब इसे डेटाकॉन्टेक्स्ट असाइन किया जाता है। उसके बाद, डेटाकॉन्टेक्स्ट को बदलना (डेटा पदानुक्रम में किसी अन्य "अभिभावक" ऑब्जेक्ट को चुनकर) अभी भी संपत्ति FahrtenView का मूल्यांकन करने का कारण बनता है (मैं एक बीपी डाल सकता हूं और डीबगर वहां रोक सकता है) लेकिन जोड़ा गया सॉर्ट डिस्क्रिप्शन पूरी तरह अनदेखा कर दिया जाता है, इसलिए सॉर्टिंग करता है अब और काम नहीं करो।

यहां तक ​​कि प्रत्येक DataContextChange पर fahrtenDG.Items.Refresh() को कॉल करने से भी मदद नहीं मिलती है।

मुझे यकीन है कि WPF DataGrid को सॉर्ट करने के लिए यह तरीका है, है ना? तो यह नौकरी पूरी तरह से पहली बार कहने के बाद पूरी तरह से काम करने से इनकार क्यों करता है?

कोई विचार? मैं बहुत आभारी रहूंगा।

चीयर्स, हेंड्रिक

+0

आप एक जवाब के रूप में अपने अद्यतन जोड़ना चाहिए, तो यह स्वीकार करते हैं (जब आप कर सकते हैं) –

+0

Yepp, तुम सही हो। किया हुआ! –

+0

http://stackoverflow.com/questions/9560528/issue-sorting-datagrid और http://stackoverflow.com/questions/6176771/wpf-datagrid-icollectionview-sorting-bug –

उत्तर

8

मैं अपनी हिम्मत पर एक संक्षिप्त झलक देखने की डेटा ग्रिड से विरासत में मिला दिया है की तरह लगता है। क्या मैं पाया है कि कुछ रहस्यमय कारणों के लिए है, हालांकि पहली बार OnItemsSourceChanged बुलाया जाता है, सब कुछ ठीक लग रहा है, के हर निम्नलिखित कॉल में शामिल है OnItemsSourceChanged ItemsSource संग्रह दृश्य की SortDescription सूची खाली है।

इसी कारण से मैंने एक कस्टम SetupSortDescription ईवेंट जोड़ा है जिसे OnItemsSourceChanged के अंत में बुलाया जाता है। अब मैं इवेंट हैंडलर फ़ंक्शन में सॉर्ट विवरण जोड़ रहा हूं, जो एक आकर्षण की तरह काम कर रहा है।

मैं इसे WPF टूलकिट डेटाग्रिड में एक बग मानता हूं।गेटर में ICollectionView पुनः बनाने तथा जल्दी-जल्दी DeferRefresh बुला() द्वारा -

यहाँ मेरी ओवरराइड OnItemsSourceChanged

protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
    { 
     if (SetupSortDescriptions != null && (newValue != null)) 
      SetupSortDescriptions(this, new ValueEventArgs<CollectionView>((CollectionView)newValue)); 

     base.OnItemsSourceChanged(oldValue, newValue); 
    } 
+1

आपकी पोस्ट के लिए बहुत बहुत धन्यवाद हेन्ड्रिक!अकेले दृश्य मॉडल के माध्यम से इस बग को प्राप्त करना वास्तव में असंभव है - किसी को कस्टम डेटाग्रिड को कार्यान्वित करना होगा। मैंने एक घटना (नीचे कोड) की बजाय संलग्न संपत्ति का उपयोग करके एक छोटा संशोधन किया। – kat

+0

आपका स्वागत है, दोस्त। –

+0

धन्यवाद! मैंने एक घटना के बजाए एमवीवीएम का उपयोग करने के लिए थोड़ा सा सुधार किया - सॉर्टडिस्क्रिप्शन की एक सूची के लिए बाध्यकारी प्रदान करके जिसे आपने केवल एक बार सेटअप किया था। मेरा दूसरा जवाब देखें। –

1

यदि आप एक ही संग्रह पर CollectionViewSource.GetDefaultView (..) फोन आप एक ही collectionview वस्तु वापस, यही कारण है कि एक समान sortdescription struct जोड़ने एक परिवर्तन ट्रिगर नहीं करता समझा सकता है मिलता है।

fahrtenDG.Items.Refresh() काम नहीं कर सकता क्योंकि आप बाध्य संग्रह को रीफ्रेश नहीं कर रहे हैं।

संग्रहViewSource.GetDefaultView (_fahrten)। रीफ्रेश() काम करना चाहिए - मैं इसका संदर्भ रखूंगा।

आपकी व्याख्या से मुझे डेटाकॉन्टेक्स्ट में काफी बदलाव नहीं मिलता है - क्या आप इसे किसी नए ऑब्जेक्ट में बदल रहे हैं? यदि ऐसा है तो आपके सभी बाइंडिंग का पुनर्मूल्यांकन करना चाहिए। क्या यह हमेशा एक ही संग्रह है, और लिस्टलेमेंट्स पर आपकी इंडेक्स प्रॉपर्टी बदलती है, और यही कारण है कि आप बदलाव की उम्मीद करते हैं - यदि ऐसा है तो आपकी सूची तत्व को INotifyPropertyChanged कार्यान्वयन की आवश्यकता हो सकती है, क्योंकि अगर संग्रह नहीं बदलता है तो इसकी कोई आवश्यकता नहीं है सहारा।

आपका OnItemsSourceChanged (..) कार्यान्वयन हैक :)

1

मैं दृश्य मॉडल के साथ इस समस्या को हल करने की कोशिश है। हालांकि मैं पुष्टि कर सकता हूं कि हेन्ड्रिक का समाधान ही एकमात्र ऐसा है जो विश्वसनीय रूप से काम करता है। अगर वह किसी की मदद करता है तो मैं नीचे पूरा कोड पोस्ट करना चाहता था।

देखें

<controls:SortableDataGrid 
    ItemsSource="{Binding InfoSorted}" 
    PermanentSort="{Binding PermanentSort}" 
    CanUserSortColumns="False" /> 

देखें मॉडल

public ObservableCollection<Foo> Info { get; private set; } 
public ICollectionView InfoSorted { get; private set; } 
public IEnumerable<SortDescription> PermanentSort { get; private set; } 

कस्टम नियंत्रण

public class SortableDataGrid : DataGrid 
    { 
     public static readonly DependencyProperty PermanentSortProperty = DependencyProperty.Register(
      "PermanentSort", 
      typeof(IEnumerable<SortDescription>), 
      typeof(SortableDataGrid), 
      new FrameworkPropertyMetadata(null)); 

     public IEnumerable<SortDescription> PermanentSort 
     { 
      get { return (IEnumerable<SortDescription>)this.GetValue(PermanentSortProperty); } 
      set { this.SetValue(PermanentSortProperty, value); } 
     } 

     protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
     { 
      var sort = this.PermanentSort; 
      if (sort != null) 
      { 
       sort = sort.ToList(); 
       var collectionView = newValue as ICollectionView; 
       if (collectionView != null) 
       { 
        using (collectionView.DeferRefresh()) 
        { 
         collectionView.SortDescriptions.Clear(); 
         foreach (SortDescription sorter in sort) 
         { 
          collectionView.SortDescriptions.Add(sorter); 
         } 
        } 
       } 
      } 

      base.OnItemsSourceChanged(oldValue, newValue); 
     } 
    } 
3

मैं WPF डेटा ग्रिड के लिए एक व्यवहार बनाने के लिए कैट से interited डेटा ग्रिड का इस्तेमाल किया।

व्यवहार प्रारंभिक सॉर्टडिस्क्रिप्शन बचाता है और उन्हें ItemsSource के प्रत्येक परिवर्तन पर लागू करता है। आप IEnumerable<SortDescription> भी प्रदान कर सकते हैं जो प्रत्येक परिवर्तन पर एक रिसॉर्ट का कारण बनता है। वैकल्पिक SortDescriptions पैरामीटर

<DataGrid ItemsSource="{Binding View}" > 
    <i:Interaction.Behaviors> 
     <commons:DataGridSortBehavior SortDescriptions="{Binding SortDescriptions}"/> 
    </i:Interaction.Behaviors> 
</DataGrid> 

ViewModel ICollectionView सेटअप के साथ

व्यवहार

public class DataGridSortBehavior : Behavior<DataGrid> 
{ 
    public static readonly DependencyProperty SortDescriptionsProperty = DependencyProperty.Register(
     "SortDescriptions", 
     typeof (IEnumerable<SortDescription>), 
     typeof (DataGridSortBehavior), 
     new FrameworkPropertyMetadata(null, SortDescriptionsPropertyChanged)); 

    /// <summary> 
    ///  Storage for initial SortDescriptions 
    /// </summary> 
    private IEnumerable<SortDescription> _internalSortDescriptions; 

    /// <summary> 
    ///  Property for providing a Binding to Custom SortDescriptions 
    /// </summary> 
    public IEnumerable<SortDescription> SortDescriptions 
    { 
     get { return (IEnumerable<SortDescription>) GetValue(SortDescriptionsProperty); } 
     set { SetValue(SortDescriptionsProperty, value); } 
    } 


    protected override void OnAttached() 
    { 
     var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof (DataGrid)); 
     if (dpd != null) 
     { 
      dpd.AddValueChanged(AssociatedObject, OnItemsSourceChanged); 
     } 
    } 

    protected override void OnDetaching() 
    { 
     var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof (DataGrid)); 
     if (dpd != null) 
     { 
      dpd.RemoveValueChanged(AssociatedObject, OnItemsSourceChanged); 
     } 
    } 

    private static void SortDescriptionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     if (d is DataGridSortBehavior) 
     { 
      ((DataGridSortBehavior) d).OnItemsSourceChanged(d, EventArgs.Empty);     
     } 
    } 

    public void OnItemsSourceChanged(object sender, EventArgs eventArgs) 
    { 
     // save description only on first call, SortDescriptions are always empty after ItemsSourceChanged 
     if (_internalSortDescriptions == null) 
     { 
      // save initial sort descriptions 
      var cv = (AssociatedObject.ItemsSource as ICollectionView); 
      if (cv != null) 
      { 
       _internalSortDescriptions = cv.SortDescriptions.ToList(); 
      } 
     } 
     else 
     { 
      // do not resort first time - DataGrid works as expected this time 
      var sort = SortDescriptions ?? _internalSortDescriptions; 

      if (sort != null) 
      { 
       sort = sort.ToList(); 
       var collectionView = AssociatedObject.ItemsSource as ICollectionView; 
       if (collectionView != null) 
       { 
        using (collectionView.DeferRefresh()) 
        { 
         collectionView.SortDescriptions.Clear(); 
         foreach (var sorter in sort) 
         { 
          collectionView.SortDescriptions.Add(sorter); 
         } 
        } 
       } 
      } 
     } 
    } 
} 

XAML

View = CollectionViewSource.GetDefaultView(_collection); 
View.SortDescriptions.Add(new SortDescription("Sequence", ListSortDirection.Ascending)); 

वैकल्पिक: अस्थिर SortDescriptions

public IEnumerable<SortDescription> SortDescriptions 
{ 
    get 
    { 
     return new List<SortDescription> {new SortDescription("Sequence", ListSortDirection.Ascending)}; 
    } 
} 
4

मैं हेंड्रिक के जवाब पर थोड़ा एक घटना के बजाय MVVM का उपयोग करने के लिए सुधार प्रदान करने के लिए ViewModel संपत्ति।

public static readonly DependencyProperty SortDescriptionsProperty = DependencyProperty.Register("SortDescriptions", typeof(List<SortDescription>), typeof(ReSolverDataGrid), new PropertyMetadata(null)); 

    /// <summary> 
    /// Sort descriptions for when grouped LCV is being used. Due to bu*g in WCF this must be set otherwise sort is ignored. 
    /// </summary> 
    /// <remarks> 
    /// IN YOUR XAML, THE ORDER OF BINDINGS IS IMPORTANT! MAKE SURE SortDescriptions IS SET BEFORE ITEMSSOURCE!!! 
    /// </remarks> 
    public List<SortDescription> SortDescriptions 
    { 
     get { return (List<SortDescription>)GetValue(SortDescriptionsProperty); } 
     set { SetValue(SortDescriptionsProperty, value); } 
    } 

    protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue) 
    { 
     //Only do this if the newValue is a listcollectionview - in which case we need to have it re-populated with sort descriptions due to DG bug 
     if (SortDescriptions != null && ((newValue as ListCollectionView) != null)) 
     { 
      var listCollectionView = (ListCollectionView)newValue; 
      listCollectionView.SortDescriptions.AddRange(SortDescriptions); 
     } 

     base.OnItemsSourceChanged(oldValue, newValue); 
    } 
+1

मेरे लिए अच्छी तरह से काम किया, अच्छा समाधान। – willem

0

मैं संलग्न व्यवहार का उपयोग करने के Juergen's approach का समर्थन करता हूं। हालांकि, चूंकि मैंने इस समस्या का मेरा संस्करण तब उत्पन्न हुआ जब मैंने दृश्य मॉडल वर्ग में संग्रह दृश्य दृश्य वस्तु घोषित की थी, इसलिए मुझे नीचे दिए गए कोड में दिखाए गए ईवेंट हैंडलर SortDescriptions_CollectionChanged जोड़कर समस्या को हल करने के लिए और अधिक प्रत्यक्ष पाया गया। यह कोड पूरी तरह से दृश्य मॉडल वर्ग के भीतर है।

public CollectionViewSource FilteredOptionsView 
{ 
    get 
    { 
     if (_filteredOptionsView == null) 
     { 
      _filteredOptionsView = new CollectionViewSource 
      { 
       Source = Options, 
       IsLiveSortingRequested = true 
      }; 
      SetOptionsViewSorting(_filteredOptionsView); 
      _filteredOptionsView.View.Filter = o => ((ConstantOption)o).Value != null; 
     } 
     return _filteredOptionsView; 
    } 
} 
private CollectionViewSource _filteredOptionsView; 

protected void SetOptionsViewSorting(CollectionViewSource viewSource) 
{ 
    // define the sorting 
    viewSource.SortDescriptions.Add(_optionsViewSortDescription); 
    // subscribe to an event in order to handle a bug caused by the DataGrid that may be 
    // bound to the CollectionViewSource 
    ((INotifyCollectionChanged)viewSource.View.SortDescriptions).CollectionChanged 
            += SortDescriptions_CollectionChanged; 
} 

protected static SortDescription _optionsViewSortDescription 
        = new SortDescription("SortIndex", ListSortDirection.Ascending); 

void SortDescriptions_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) 
{ 
    var collection = sender as SortDescriptionCollection; 
    if (collection == null) return; 
    // The SortDescriptions collection should always contain exactly one SortDescription. 
    // However, when DataTemplate containing the DataGrid bound to the ICollectionView 
    // is unloaded, the DataGrid erroneously clears the collection. 
    if (collection.None()) 
     collection.Add(_optionsViewSortDescription); 
} 
0

धन्यवाद! यह मुझे लड़ाकू चला रहा था! मैंने अपनी जरूरतों को सुइट करने के लिए अपना कोड संशोधित किया। यह मूल रूप से क्रमबद्ध विवरणों को जारी रखता है और जब भी वे उड़ाते हैं उन्हें पुनर्स्थापित करते हैं। यह दूसरों की मदद कर सकते हैं:

private List<SortDescription> SortDescriptions = null; 

protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
{ 
if (newValue is CollectionView collectionView) 
    if (SortDescriptions == null) 
    SortDescriptions = new List<SortDescription>(collectionView.SortDescriptions); 
    else 
    foreach (SortDescription sortDescription in SortDescriptions) 
    collectionView.SortDescriptions.Add(sortDescription); 

base.OnItemsSourceChanged(oldValue, newValue); 
} 
संबंधित मुद्दे