11

ठीक है, तो यह सवाल विंडोज फोन 7/सिल्वरलाइट (अद्यतन WP7 टूल्स, सितंबर 2010) से संबंधित है, विशेष रूप से अंतर्निहित ObservableCollection<T> फ़िल्टरिंग।संग्रह दृश्य दृश्य पर स्वचालित रूप से फ़िल्टर और/या क्रमबद्ध क्रम को कैसे अपडेट करें, जब कोई व्यक्तिगत आइटम की संपत्ति बदलती है?

WP7 टेम्पलेट पिवोट कंट्रोल एप्लिकेशन के साथ मिलकर, मैंने एक समस्या में भाग लिया है जिससे ObservableCollection<T> में अंतर्निहित आइटम को बदलना, परिणामस्वरूप ऑन-स्क्रीन ListBox को अपडेट किया जा रहा है। असल में, नमूना ऐप में दो पिवट होते हैं, जो सीधे अंतर्निहित ObservableCollection<T> से बंधे होते हैं, और दूसरा CollectionViewSource (यानी अंतर्निहित ObservableCollection<T> पर फ़िल्टर किए गए दृश्य का प्रतिनिधित्व करता है)।

अंतर्निहित आइटम कि ObservableCollection<T> जोड़े जा रहे हैं INotifyPropertyChanged लागू, जैसे इतना:

public class ItemViewModel : INotifyPropertyChanged 
{  
    public string LineOne 
    { 
     get { return _lineOne; } 
     set 
     { 
      if (value != _lineOne) 
      { 
       _lineOne = value; 
       NotifyPropertyChanged("LineOne"); 
      } 
     } 
    } private string _lineOne; 

    public string LineTwo 
    { 
     get { return _lineTwo; } 
     set 
     { 
      if (value != _lineTwo) 
      { 
       _lineTwo = value; 
       NotifyPropertyChanged("LineTwo"); 
      } 
     } 
    } private string _lineTwo; 

    public bool IsSelected 
    { 
     get { return _isSelected; } 
     set 
     { 
      if (value != _isSelected) 
      { 
       _isSelected = value; 
       NotifyPropertyChanged("IsSelected"); 
      } 
     } 
    } private bool _isSelected = false; 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged(String propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

फिर, मुख्य वर्ग में, एक डाटा संग्रह गढ़ी गई है (संक्षिप्तता के लिए कम सूची, यह भी ध्यान दें कि अन्य के विपरीत आइटम, LoadData के तीन() प्रविष्टियों IsSelected है == सच):

public class MainViewModel : INotifyPropertyChanged 
{ 
    public MainViewModel() 
    { 
    this.Items = new ObservableCollection<ItemViewModel>(); 
    } 

    public ObservableCollection<ItemViewModel> Items { get; private set; } 

    public bool IsDataLoaded 
    { 
    get; 
    private set; 
    } 

    public void LoadData() 
    { 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime one", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" }); 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime two", LineTwo = "Dictumst eleifend facilisi faucibus" }); 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime three", IsSelected = true, LineTwo = "Habitant inceptos interdum lobortis" }); 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime four", LineTwo = "Nascetur pharetra placerat pulvinar" }); 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime five", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" }); 
    this.Items.Add(new ItemViewModel() { LineOne = "runtime six", LineTwo = "Dictumst eleifend facilisi faucibus" }); 
    this.IsDataLoaded = true; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    public void NotifyPropertyChanged(String propertyName) 
    { 
    if (null != PropertyChanged) 
    { 
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    } 
} 

MainPage.xaml फ़ाइल में, पहली धुरी अपने ItemSourceObservableCollection<T> सूची पर सीधे आधारित है। दूसरे पिवोट के भीतर, ऑन-स्क्रीन लिस्टबॉक्स में इसकी ItemSource संपत्ति CollectionViewSource पर सेट है, जिसका अंतर्निहित स्रोत ऊपर में ObservableCollection<T> पर आधारित है।

<phone:PhoneApplicationPage.Resources> 
    <CollectionViewSource x:Key="IsSelectedCollectionView" Filter="CollectionViewSource_SelectedListFilter"> 
    </CollectionViewSource> 
</phone:PhoneApplicationPage.Resources> 

<!--LayoutRoot is the root grid where all page content is placed--> 
<Grid x:Name="LayoutRoot" Background="Transparent"> 
    <!--Pivot Control--> 
    <controls:Pivot Title="MY APPLICATION"> 
     <!--Pivot item one--> 
     <controls:PivotItem Header="first"> 
      <!--Double line list with text wrapping--> 
      <ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}"> 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <StackPanel Margin="0,0,0,17" Width="432"> 
          <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/> 
          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/> 
         </StackPanel> 
        </DataTemplate> 
       </ListBox.ItemTemplate> 
      </ListBox> 
     </controls:PivotItem> 

     <!--Pivot item two--> 
     <controls:PivotItem Header="second"> 
      <!--Triple line list no text wrapping--> 
      <ListBox x:Name="SecondListBox" Margin="0,0,-12,0" ItemsSource="{Binding Source={StaticResource IsSelectedCollectionView}}"> 
        <ListBox.ItemTemplate> 
         <DataTemplate> 
          <StackPanel Margin="0,0,0,17"> 
           <TextBlock Text="{Binding LineOne}" TextWrapping="NoWrap" Margin="12,0,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}"/> 
           <TextBlock Text="{Binding LineThree}" TextWrapping="NoWrap" Margin="12,-6,0,0" Style="{StaticResource PhoneTextSubtleStyle}"/> 
          </StackPanel> 
         </DataTemplate> 
        </ListBox.ItemTemplate> 
       </ListBox> 
     </controls:PivotItem> 
    </controls:Pivot> 
</Grid> 

<!--Sample code showing usage of ApplicationBar--> 
<phone:PhoneApplicationPage.ApplicationBar> 
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> 
     <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click"/> 
     <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/> 
     <shell:ApplicationBar.MenuItems> 
      <shell:ApplicationBarMenuItem Text="MenuItem 1"/> 
      <shell:ApplicationBarMenuItem Text="MenuItem 2"/> 
     </shell:ApplicationBar.MenuItems> 
    </shell:ApplicationBar> 
</phone:PhoneApplicationPage.ApplicationBar> 

ध्यान दें कि MainPage.xaml.cs में, Resources खंड में CollectionViewSource पर Filter विशेषता के ऊपर एक फिल्टर हैंडलर, जो उन वस्तुओं है कि है IsSelected के माध्यम से sifts असाइन किया गया है सत्य पर सेट करें:

public partial class MainPage : PhoneApplicationPage 
{ 
    public MainPage() 
    { 
     InitializeComponent(); 
     DataContext = App.ViewModel; 
     this.Loaded += new RoutedEventHandler(MainPage_Loaded); 
    } 

    private void MainPage_Loaded(object sender, RoutedEventArgs e) 
    { 
     if (!App.ViewModel.IsDataLoaded) 
     { 
      App.ViewModel.LoadData(); 
      CollectionViewSource isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource; 
      if (isSelectedListView != null) 
      { 
       isSelectedListView .Source = App.ViewModel.Items; 
      } 
     } 
    } 

    private void CollectionViewSource_SelectedListFilter(object sender, System.Windows.Data.FilterEventArgs e) 
    { 
     e.Accepted = ((ItemViewModel)e.Item).IsSelected; 
    } 

    private void ApplicationBarIconButton_Click(object sender, EventArgs e) 
    { 
     ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1]; 
     item.IsSelected = !item.IsSelected; 
    } 
} 

यह भी ध्यान दें कि लोडिन के तुरंत बाद डेटा को जी, मैं CollectionViewSource प्राप्त करता हूं और ObservableCollection<T> सूची के रूप में अपना डेटा स्रोत सेट करता हूं, ताकि फ़िल्टरिंग हो सकने वाला मूल डेटा हो।

आवेदन भार, डेटा की उम्मीद के रूप में प्रदर्शित किया जाता है ObservableCollection<T> जो IsSelected सच है, दूसरी धुरी में प्रदर्शित किया जा रहा है में उन वस्तुओं के साथ,:

alt text alt text

आपको लगता है कि मैं ध्यान देंगे क्लिक किए जाने पर ObservableCollection<T> में अंतिम आइटम की IsSelected संपत्ति को क्लिक करते समय एप्लिकेशन बार आइकन को अनमंत्रित किया गया है (MainPage.xaml.cs में अंतिम फ़ंक्शन देखें)।

यहाँ है मेरे सवाल की जड़ - जब मैं लागू बार आइकन पर क्लिक करें, मैं देख सकता जब सूची का अंतिम आइटम अपने IsSelected संपत्ति सही पर सेट किया, howoever दूसरी धुरी इस बदली हुई आइटम प्रदर्शित नहीं करता है । मैं देख सकता हूं कि NotifyPropertyChanged() हैंडलर को आइटम पर निकाल दिया जा रहा है, हालांकि संग्रह इस तथ्य को नहीं उठा रहा है, और इसलिए पिवोट 2 में सूची बॉक्स इस तथ्य को प्रतिबिंबित करने के लिए नहीं बदलता है कि संग्रह में एक नया आइटम जोड़ा जाना चाहिए ।

मुझे यकीन है कि मुझे कुछ मौलिक/बुनियादी कुछ याद आ रहा है, लेकिन असफल रहा है, क्या किसी को संग्रह प्राप्त करने का सबसे अच्छा तरीका पता है और यह खुशी से खेलने के लिए अंतर्निहित वस्तुओं को जानता है?

मुझे लगता है कि यह समस्या सॉर्टिंग के साथ-साथ फ़िल्टरिंग पर भी लागू होती है ((इस अर्थ में कि अगर CollectionViewSource सॉर्टिंग पर आधारित है, तो जब किसी आइटम की एक प्रकार की प्रॉपर्टी का उपयोग किया जाता है, तो सॉर्ट ऑर्डर संग्रह को भी इस पर प्रतिबिंबित करना चाहिए))

+0

मुझे एक ही समस्या है।हम उम्मीद करते हैं कि अंतर्निहित संग्रह में परिवर्तनों के आधार पर दृश्य गतिशील रूप से अपडेट हो जाए लेकिन यह नहीं है। तो यह वास्तव में सॉफ्टवेयर की एक समस्या है जो हम स्वाभाविक रूप से अपेक्षा करते हैं और/या एमएसडीएन दस्तावेज पूरा नहीं कर रहे हैं। – JustinM

उत्तर

3

जब ऐसा होता है तो आप इसे नफरत नहीं करते हैं, मैंने प्रश्न पोस्ट करने के बाद 5 मिनट नहीं चले, और मुझे पता चला कि समस्या क्या है - और यह कुछ था काफी बुनियादी CollectionViewSource ऑब्जेक्ट पर, View संपत्ति है, जिसमें Refresh() फ़ंक्शन है। ObservableCollection<T> परिवर्तनों में निहित अंतर्निहित आइटम पर किसी संपत्ति के बाद इस फ़ंक्शन को कॉल करना ऐसा लगता है।

private void MainPage_Loaded(object sender, RoutedEventArgs e) 
{ 
    if (!App.ViewModel.IsDataLoaded) 
    { 
     App.ViewModel.LoadData(); 
     m_isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource; 
     if (m_isSelectedListView != null) 
     { 
      m_isSelectedListView.Source = App.ViewModel.Items; 
     } 
    } 
} 

फिर, दृश्य पर Refresh() फोन से कोई भी आइटम के बाद:

असल में, सब मैं करना था एक सदस्य चर में CollectionViewSource वस्तु बदलने के लिए, और फिर इसे बचाने के लिए जब LoadData() कहा जाता है था अंतर्निहित ObservableCollection<T> परिवर्तनों में। तो MainPage.xaml.cs में, अभी पिछले आइटम बदलने के बाद, कॉल ताज़ा करने के लिए जोड़ें:

private void ApplicationBarIconButton_Click(object sender, EventArgs e) 
{ 
    ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1]; 
    item.IsSelected = !item.IsSelected; 
    m_isSelectedListView.View.Refresh(); 
} 

... और दूसरी धुरी के ListBox तुरन्त अद्यतन किया जाता है। कोड की इतनी छोटी रेखा, अंतर की पूरी दुनिया!

उस समय मुझे उस प्रश्न को लिखने के लिए लिया गया, वहां एक सौ चीजें हैं जो मैं कर सकता था :-(आह ठीक है, मुझे लगता है कि कभी भी देर से नहीं - यहां जवाब पोस्ट करने का विचार किया गया है, अगर केवल सहेजने के लिए किसी और ने अपने बालों को फाड़ कर जैसे मैंने किया।

+1

'ऑब्जर्वेबल कोलेक्शन' स्वचालित रूप से 'इनोटिफ़ाईप्रॉपर्टी चेंज' लागू करता है जहां 'कलेक्शन व्यूसोर्स' नहीं होता है। इसका मतलब है कि आपको स्पष्ट रूप से 'CollectionViewSource' को बताना होगा कि इसे स्वयं अपडेट करना होगा। 'रीफ्रेश();' कॉल करके आप यही कर रहे हैं। –

4

मुझे इस समस्या को संभालना पड़ा और हालांकि 'रीफ्रेश()' समाधान अच्छी तरह से काम करता है, हालांकि इसे निष्पादित करने में काफी लंबा समय लगता है क्योंकि यह केवल एक आइटम संपत्ति के लिए पूरी सूची को रीफ्रेश करता है घटना बहुत अच्छी नहीं है। और वास्तविक समय डेटा के परिदृश्य में प्रत्येक 1 सेकंड में संग्रह दर्ज करने के बाद, यदि आप इस दृष्टिकोण का उपयोग करते हैं तो मैं आपको उपयोगकर्ता अनुभव में परिणाम की कल्पना करता हूं :)

मैं एक समाधान के साथ आया जो आधार है : जब एक संग्रह संग्रह में लिपटे संग्रह में किसी आइटम को डीडी करना, फिर आइटम को फ़िल्टर अनुमान द्वारा मूल्यांकन किया जाता है और, इस परिणाम के आधार पर, दृश्य में प्रदर्शित होता है या नहीं।

तो ताज़ा करने के बजाय(), मैं उस ऑब्जेक्ट का एक सम्मिलन अनुकरण कर रहा हूं जिसने अपनी संपत्ति अपडेट की है। ऑब्जेक्ट के सम्मिलन को अनुकरण करके, इसे रीफ्रेश के साथ पूरी सूची को रीफ्रेश करने के बिना फ़िल्टर पूर्वानुमान द्वारा स्वचालित रूप से मूल्यांकन किया जा रहा है।

यहाँ ऐसा करने में कोड है:

व्युत्पन्न नमूदार संग्रह:

namespace dotnetexplorer.blog.com 
{ 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 

/// <summary> 
/// Derived class used to be able to manage filter application when a collection item property changed 
/// whithout having to do a refresh 
/// </summary> 
internal sealed class CustomObservableCollection : ObservableCollection<object> 
{ 
    /// <summary> 
    /// Initializes a new instance of the <see cref = "CustomObservableCollection " /> class. 
    /// </summary> 
    public CustomObservableCollection() 
    { 
    } 

    /// <summary> 
    /// Initializes a new instance of the <see cref="CustomObservableCollection "/> class. 
    /// </summary> 
    /// <param name="source"> 
    /// The source. 
    /// </param> 
    public CustomObservableCollection (IEnumerable<object> source) 
     : base(source) 
    { 
    } 

    /// <summary> 
    /// Custom Raise collection changed 
    /// </summary> 
    /// <param name="e"> 
    /// The notification action 
    /// </param> 
    public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     OnCollectionChanged(e); 
    } 
} 
} 

और वहाँ जब आइटम संपत्ति बदली हुई घटना जहां स्थानापन्न स्रोत एक CustomObservableCollection है receiveing ​​उपयोग करने के लिए कोड है:

 private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 

       // To avoid doing a refresh on a property change which would end in a very hawful user experience 
       // we simulate a replace to the collection because the filter is automatically applied in this case 
       int index = _substituteSource.IndexOf(sender); 

       var argsReplace = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, 
                     new List<object> { sender }, 
                     new List<object> { sender }, index); 
       _substituteSource.RaiseCollectionChanged(argsReplace); 
      } 

     } 
    } 

आशा है कि इससे मदद मिलेगी!

+0

धन्यवाद ब्रूनो, मुझे आपका समाधान पसंद है (आपको मुझसे एक अप-वोट मिला है) .. ऐसा लगता है कि एक फ़िल्टर डालने के बिना फ़िल्टर पूर्वानुमान को 'ट्रिगर' करने का एक बेहतर तरीका होना चाहिए .. इसमें देखेंगे फिर जब मुझे मौका मिलता है .. –

+0

Thx;) लेकिन एक और समाधान खोजने में शुभकामनाएं, मैं लंबे समय से उस पर अटक गया हूं, अपने स्मार्ट खोज घटक के लिए बेहतर समाधान जानने की कोशिश कर रहा हूं। अब तक, कोई बेहतर समाधान नहीं मिला ... – Bruno

संबंधित मुद्दे