2015-02-10 7 views
8

समस्या:अद्यतन ItemsControl एक ObservableCollection में एक आइटम अद्यतन किया जाता है जब

  • आप घोषणा एक ItemsControl (या एक नियंत्रण ItemsControl से प्राप्त) ध्यान में रखते हुए।
  • आप ItemsControl.ItemsSource संपत्ति को अपने व्यूमोडेल में ObservableCollection पर बांधें।
  • ObservableCollection से किसी आइटम को जोड़ने/निकालने पर अपेक्षित आपके दृश्य अपडेट।
  • लेकिन, जब आप ObservableCollection में किसी आइटम की संपत्ति बदलते हैं तो दृश्य अपडेट नहीं होता है।

पृष्ठभूमि:

ऐसा लगता है कि यह एक आम समस्या कई WPF डेवलपर्स का सामना करना पड़ा है। यह कई बार कहा गया है:

Notify ObservableCollection when Item changes

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)

ObservableCollection and Item PropertyChanged

मेरे कार्यान्वयन:

मैं Notify ObservableCollection when Item changes में स्वीकार किए जाते हैं समाधान को लागू करने की कोशिश की। मूल विचार ObservableCollection में प्रत्येक आइटम के लिए अपने मेनविंडो व्यू मॉडेल में PropertyChanged हैंडलर को हुक करना है। जब किसी आइटम की संपत्ति बदल जाती है, तो ईवेंट हैंडलर लागू किया जाएगा और किसी भी तरह से दृश्य अपडेट किया गया है।

मुझे काम करने के लिए कार्यान्वयन नहीं मिल सका। मेरा कार्यान्वयन यहाँ है।

ViewModels:

class ViewModelBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void RaisePropertyChanged(string propertyName = "") 
    { 
     var handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

आइटम ViewModel:

class EmployeeViewModel : ViewModelBase 
{ 
    private int _age; 
    private string _name; 

    public int Age 
    { 
     get { return _age; } 
     set 
     { 
      _age = value; 
      RaisePropertyChanged("Age"); 
     } 
    } 

    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      RaisePropertyChanged("Name"); 
     } 
    } 

    public override string ToString() 
    { 
     return string.Format("{0} is {1} years old", Name, Age); 
    } 
} 

मुख्य विंडो ViewModel:

class MainWindowViewModel : ViewModelBase 
{ 
    private ObservableCollection<EmployeeViewModel> _collection; 

    public MainWindowViewModel() 
    { 
     _collection = new ObservableCollection<EmployeeViewModel>(); 
     _collection.CollectionChanged += MyItemsSource_CollectionChanged; 

     AddEmployeeCommand = new DelegateCommand(() => AddEmployee()); 
     IncrementEmployeeAgeCommand = new DelegateCommand(() => IncrementEmployeeAge()); 
    } 

    public ObservableCollection<EmployeeViewModel> Employees 
    { 
     get { return _collection; } 
    } 

    public ICommand AddEmployeeCommand { get; set; } 
    public ICommand IncrementEmployeeAgeCommand { get; set; } 

    public void AddEmployee() 
    { 
     _collection.Add(new EmployeeViewModel() 
      { 
       Age = 1, 
       Name = "Random Joe", 
      }); 
    } 

    public void IncrementEmployeeAge() 
    { 
     foreach (var item in _collection) 
     { 
      item.Age++; 
     } 
    } 

    private void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.NewItems != null) 
      foreach (EmployeeViewModel item in e.NewItems) 
       item.PropertyChanged += ItemPropertyChanged; 

     if (e.OldItems != null) 
      foreach (EmployeeViewModel item in e.OldItems) 
       item.PropertyChanged -= ItemPropertyChanged; 
    } 

    private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     RaisePropertyChanged("Employees"); 
    } 
} 

दृश्य:

<Window x:Class="WpfApplication2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" 
    xmlns:d="clr-namespace:Iress.IosPlus.DynamicOE.Controls" 
    Title="MainWindow" Height="350" Width="350"> 

<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="0.3*"></ColumnDefinition> 
     <ColumnDefinition Width="0.7*"></ColumnDefinition> 
    </Grid.ColumnDefinitions> 

    <StackPanel Grid.Column="0"> 
     <Button Command="{Binding AddEmployeeCommand}">Add Employee</Button> 
     <Button Command="{Binding IncrementEmployeeAgeCommand}">Increment Employee Age</Button> 
    </StackPanel> 

    <Grid Grid.Column="1"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="0.1*"></RowDefinition> 
      <RowDefinition></RowDefinition> 
     </Grid.RowDefinitions> 
     <TextBlock Grid.Row="0" Text="{Binding Path=Employees[0]}"></TextBlock> 
     <ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees}" BorderBrush="Red" BorderThickness="1"></ItemsControl> 
    </Grid> 
</Grid> 

मेरे परिणाम:

मेरी क्रियान्वयन को सत्यापित करने के लिए, मैं बहुत की तरह एक दृश्य बनाना होगा। TextBlock.Text संग्रह में पहली आइटम के लिए बाध्य है। ItemsControl संग्रह से ही जुड़ा हुआ है।

  • "कर्मचारी जोड़ें" बटन दबाने संग्रह में एक EmployeeViewModel वस्तु कहते हैं और दोनों TextBlock और ItemsControl अपेक्षा के अनुरूप अपडेट किया जाता है।
  • फिर से "कर्मचारी जोड़ें" दबाकर, ItemsControl किसी अन्य प्रविष्टि के साथ अपडेट किया गया है। महान!
  • "वृद्धि कर्मचारी आयु" बटन दबाकर। प्रत्येक आइटम की Age संपत्ति 1 से बढ़ी है। PropertyChanged ईवेंट उठाया गया है। ItemPropertyChanged इवेंट हैंडलर का आह्वान किया गया है। Textblock अपेक्षित के रूप में अद्यतन किया गया है। हालांकि, ItemsControl अद्यतन नहीं है।

मैं धारणा है कि ItemsControl भी अद्यतन किया जाना चाहिए जब Employee.AgeNotify ObservableCollection when Item changes में जवाब के अनुसार बदल जाता है के तहत कर रहा हूँ।

enter image description here

+0

आप क्या करने की कोशिश कर रहे हैं? एक अवलोकन संग्रह संग्रह को देख रहा है, न कि बच्चों की संपत्तियों। अगर कोई बच्चा संपत्ति बदलता है तो संग्रह करने का क्या फायदा होता है? – michael

+0

@ माइकल, मैं संग्रह में किसी आइटम को अपडेट होने पर 'आइटम्स कंट्रोल' रीफ्रेश करना चाहता हूं। –

+0

हालांकि यह क्या हासिल करेगा, संग्रह में सभी वस्तुओं के लिए जिम्मेदार हैं। यूआई प्राप्त करने के बाद संपत्ति कुछ भी नहीं बदलेगी ... संदर्भ अभी भी वही है। – michael

उत्तर

9

मैं Snoop का उपयोग कर XAML डिबग करने के लिए इस सवाल का जवाब मिल गया।

मुद्दा यह है कि आप ToString() विधि से जुड़ने की कोशिश कर रहे हैं और यह PropertyChanged ईवेंट नहीं बढ़ाता है। यदि आप एक्सएएमएल बाइंडिंग देखते हैं तो आप देखेंगे कि ऑब्जर्जेबल कोलेक्शन वास्तव में बदल रहा है।

Snoop Showing Correct Binding

अब प्रत्येक आइटम नियंत्रण को देखो और "पाठ" संपत्ति में बाध्यकारी ग्रंथों है। कोई नहीं है, यह सिर्फ पाठ है।

Snoop showing no data binding to elements in the items control

इस बस एक DataTemplate है कि तत्वों आप प्रदर्शित किया करना चाहते हैं के साथ एक ItemsControl ItemTemplate जोड़ने ठीक करने के लिए।

<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Employees, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Red" BorderThickness="1" > 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <TextBlock> 
       <TextBlock.Text> 
        <MultiBinding StringFormat=" {0} is {1} years old"> 
         <Binding Path="Name"/> 
         <Binding Path="Age"/> 
        </MultiBinding> 
       </TextBlock.Text> 
      </TextBlock> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

अब हमारे पास बाध्यकारी पर एक हरा प्रकाश है। RaisePropertyChanged कहा जा रहा है।

Binding correctly shows green

टा-दा!

Solution shown

+0

उत्तर के लिए धन्यवाद। मैंने 'कर्मचारी' संपत्ति (पर्यवेक्षण चयन) के लिए एक सेटटर जोड़ा और सेटटर में 'राइजप्रोपर्टी चेंज' कहा जाता है। लेकिन यह वृद्धि आइटम कर्मचारी बटन दबाते समय अपडेट करने के लिए 'आइटम नियंत्रण' को ट्रिगर नहीं करता है। –

+0

ग्रेट उत्तर। मैं आइटम की संपत्ति से जुड़ने के लिए ItemTemplate का उपयोग करके समझता हूं इस समस्या के आसपास काम कर सकता है। वास्तव में, यदि आप ItemTemplate का उपयोग करते हैं, तो हमें कलेक्शन चेंजेड ईवेंट को प्रत्येक आइटम में आइटमप्रॉपर्टी चेंज हैंडलर को हुक करने की आवश्यकता भी होती है। मुझे लगता है कि आइटम टेम्पलेट का उपयोग किये बिना इसे ठीक करने का कोई तरीका हो सकता है। यदि कोई अन्य उत्तर प्रस्तुत नहीं किया गया है। मैं आपकी स्वीकृति के रूप में चिह्नित करूंगा। –

+2

@ फ्रैंकलिउ एक आइटम टेम्पलेट घोषित करना आइटम्स कंट्रोल में आइटम को विज़ुअलाइज़ करने या ListBox जैसे व्युत्पन्न नियंत्रण का मानक तरीका है। आप एमएसडीएन पर [डेटा टेम्पलेटिंग अवलोकन] (https://msdn.microsoft.com/en-us/library/ms742521.aspx) आलेख पढ़ना चाह सकते हैं। – Clemens

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

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