Inside the class there is a private variable of the list, which can vary according to the logic of the class. There is a public property that provides read-only elements for the internal list. So I need an event about changing the internal list item. What I need is nothing to do with the graphical interface. This is required for equipment management class. Here are two options, as I thought of doing, but how correctly I do not know. Maybe in general there are some other ways.
class TestClass1 { // Меняется внутри класса private bool[] _states; // Для чтения элементов массива _states извне. private ReadOnlyCollection<bool> _readOnlyStates; public ReadOnlyCollection<bool> States { get { return _readOnlyStates; } } // Событие, передающее индекс изменного элемента. public event Action<int> States1Changed; private void UpdateStates(int index, bool newValue) { if (_states[index] != newValue) _states[index] = newValue; } public TestClass1() { _states = new bool[20]; _readOnlyStates = Array.AsReadOnly(_states); } } class TestClass2 { // Меняется внутри класса private ObservableCollection<bool> _states; // Для чтения элементов массива _states извне. private ReadOnlyObservableCollection<bool> _readOnlyStates; public ReadOnlyObservableCollection<bool> States { get { return _readOnlyStates; } } // Для получения события об изменении сделать следующее // (States as INotifyCollectionChanged).CollectionChanged private void UpdateStates(int index, bool newValue) { if (_states[index] != newValue) _states[index] = newValue; } public TestClass2() { _states = new ObservableCollection<bool>(); _readOnlyStates = new ReadOnlyObservableCollection<bool>(_states); } } UPDATE 1
There was a need for such a list, I also don’t know how to act better. The list stores objects with properties. The class must be able to change the properties of the elements and notify the subscriber. The user can only read the properties of the elements. Only such an option occurred to me.
class ListItem { public bool Property1 { get; private set; } public double Property2 { get; private set; } public ListItem(bool property1, double property2) { Property1 = property1; Property2 = property2; } } class TestClass { private List<ListItem> _list; private ReadOnlyCollection<ListItem> _readOnlyList; public TestClass() { _list = new List<ListItem>(); _readOnlyList = _list.AsReadOnly(); } public ReadOnlyCollection<ListItem> List { get { return _readOnlyList; } } public event ListChangedEventHandler ListChanged; public void UpdateProperty1(int index, bool property1) { if (_list[index].Property1 != property1) { _list[index] = new ListItem(property1, _list[index].Property2); ListChanged(this, new ListChangedEventArgs(ListChangedType.ItemChanged, index)); } } public void UpdateProperty2(int index, double property2) { if (_list[index].Property2 != property2) { _list[index] = new ListItem(_list[index].Property1, property2); ListChanged(this, new ListChangedEventArgs(ListChangedType.ItemChanged, index)); } } } UPDATE 2
I came up with this implementation. The user is only open IMortarInfo for reading. Inside there is a closed implementation of this interface MortarInfo with the ability to edit. Also added INotifyPropertyChanged to simplify writing code with events. Because of the impossibility of bringing IList<MortarInfo> to IList<IMortarInfo> I had to make a crutch.
public interface IMortarInfo : INotifyPropertyChanged { double Angle { get; } bool State { get; } } public class Mortars { private class MortarInfo : NotifyPropertyChanged, IMortarInfo { private double _angle; private bool _state; public double Angle { get { return _angle; } set { if (_angle == value) return; _angle = value; OnPropertyChanged(); } } public bool State { get { return _state; } set { if (_state == value) return; _state = value; OnPropertyChanged(); } } public MortarInfo() : this(0, false) { } public MortarInfo(double angle, bool state) { _angle = angle; _state = state; } } private readonly BindingList<MortarInfo> _mortarsInfo; private readonly List<IMortarInfo> _listForReadOnlyMortarsInfo; private readonly ReadOnlyCollection<IMortarInfo> _readOnlyMortarsInfo; public ReadOnlyCollection<IMortarInfo> MortarsInfo { get { return _readOnlyMortarsInfo; } } public event ListChangedEventHandler MortarInfoChanged; public Mortars() { const int mortarsCount = 6; _mortarsInfo = new BindingList<MortarInfo>(); _mortarsInfo.ListChanged += ListChanged; _listForReadOnlyMortarsInfo = new List<IMortarInfo>(); _readOnlyMortarsInfo = _listForReadOnlyMortarsInfo.AsReadOnly(); for (int i = 0; i < mortarsCount; i++) _mortarsInfo.Add(new MortarInfo()); } private void ListChanged(object sender, ListChangedEventArgs e) { switch(e.ListChangedType) { case ListChangedType.ItemAdded: _listForReadOnlyMortarsInfo.Add(_mortarsInfo[e.NewIndex]); break; case ListChangedType.ItemDeleted: _listForReadOnlyMortarsInfo.RemoveAt(e.NewIndex); break; } if (MortarInfoChanged != null) MortarInfoChanged(this, e); } } You can also do without a crutch, but then during editing you will have to do ghost types.
public class Mortars { private readonly BindingList<IMortarInfo> _mortarsInfo; private readonly ReadOnlyCollection<IMortarInfo> _readOnlyMortarsInfo; public ReadOnlyCollection<IMortarInfo> MortarsInfo { get { return _readOnlyMortarsInfo; } } public event ListChangedEventHandler MortarInfoChanged { add { _mortarsInfo.ListChanged += value; } remove { _mortarsInfo.ListChanged -= value; } } public Mortars() { const int mortarsCount = 6; _mortarsInfo = new BindingList<IMortarInfo>(); _readOnlyMortarsInfo = new ReadOnlyCollection<IMortarInfo>(_mortarsInfo); for (int i = 0; i < mortarsCount; i++) _mortarsInfo.Add(new MortarInfo()); } public void SomeMethod() { (_mortarsInfo[2] as MortarInfo).State = true; } }