समस्या:अद्यतन 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.Age
Notify ObservableCollection when Item changes में जवाब के अनुसार बदल जाता है के तहत कर रहा हूँ।
आप क्या करने की कोशिश कर रहे हैं? एक अवलोकन संग्रह संग्रह को देख रहा है, न कि बच्चों की संपत्तियों। अगर कोई बच्चा संपत्ति बदलता है तो संग्रह करने का क्या फायदा होता है? – michael
@ माइकल, मैं संग्रह में किसी आइटम को अपडेट होने पर 'आइटम्स कंट्रोल' रीफ्रेश करना चाहता हूं। –
हालांकि यह क्या हासिल करेगा, संग्रह में सभी वस्तुओं के लिए जिम्मेदार हैं। यूआई प्राप्त करने के बाद संपत्ति कुछ भी नहीं बदलेगी ... संदर्भ अभी भी वही है। – michael