Как сделать свойство ObservableCollection только для чтения? - PullRequest
36 голосов
/ 19 ноября 2009

Я хотел бы выставить свойство в модели представления, содержащей список объектов (из базы данных).

Мне нужно, чтобы эта коллекция была доступна только для чтения. То есть я хочу запретить Add / Remove и т. Д., Но позволить foreach и индексаторам работать. Мое намерение состоит в том, чтобы объявить приватное поле, содержащее редактируемую коллекцию, и ссылаться на него с помощью общедоступного свойства только для чтения. Следующим образом

public ObservableCollection<foo> CollectionOfFoo { 
     get { 
         return _CollectionOfFoo;
     }
}

Однако этот синтаксис просто не позволяет изменить ссылку на коллекцию. Это не мешает добавлять / удалять и т. Д.

Как правильно это сделать?

Ответы [ 5 ]

60 голосов
/ 20 мая 2010

Принятый [ранее] ответ на самом деле будет возвращать различные ReadOnlyObservableCollection при каждом доступе к ReadOnlyFoo. Это расточительно и может привести к тонким ошибкам.

Предпочтительным решением является:

public class Source
{
    Source()
    {
        m_collection = new ObservableCollection<int>();
        m_collectionReadOnly = new ReadOnlyObservableCollection<int>(m_collection);
    }

    public ReadOnlyObservableCollection<int> Items
    {
        get { return m_collectionReadOnly; }
    }

    readonly ObservableCollection<int> m_collection;
    readonly ReadOnlyObservableCollection<int> m_collectionReadOnly;
}

См. ReadOnlyObservableCollection anti-pattern для полного обсуждения.

8 голосов
/ 18 марта 2013

Мне не нравится использовать ReadOnlyObservableCollection<T>, так как это похоже на ошибку / неработающий класс; Вместо этого я предпочитаю контрактный подход.

Вот что я использую, чтобы учесть ковариацию:

public interface INotifyCollection<T> 
       : ICollection<T>, 
         INotifyCollectionChanged
{}

public interface IReadOnlyNotifyCollection<out T> 
       : IReadOnlyCollection<T>, 
         INotifyCollectionChanged
{}

public class NotifyCollection<T> 
       : ObservableCollection<T>, 
         INotifyCollection<T>, 
         IReadOnlyNotifyCollection<T>
{}

public class Program
{
    private static void Main(string[] args)
    {
        var full = new NotifyCollection<string>();
        var readOnlyAccess = (IReadOnlyCollection<string>) full;
        var readOnlyNotifyOfChange = (IReadOnlyNotifyCollection<string>) full;


        //Covarience
        var readOnlyListWithChanges = 
            new List<IReadOnlyNotifyCollection<object>>()
                {
                    new NotifyCollection<object>(),
                    new NotifyCollection<string>(),
                };
    }
}
0 голосов
/ 19 ноября 2009

Вы также можете переопределить используемый вами класс списка и поместить неизменный флаг в один из конструкторов так, чтобы он не добавлялся / не удалялся, если он был создан с установленным значением true.

0 голосов
/ 19 ноября 2009

Использование ReadOnlyObservableCollection

public ReadOnlyObservableCollection<T> ReadOnlyFoo
{
    get { return new ReadOnlyObservableCollection<T> (_CollectionOfFoo); }
}

Как уже указывалось, пожалуйста, используйте ответ Эрика Дж, так как он по ошибке возвращает каждый раз новый экземпляр.

0 голосов
/ 19 ноября 2009

Вы можете изменить тип вашей собственности на IEnumerable:

public IEnumerable<foo> CollectionOfFoo { 
     get { 
         return _CollectionOfFoo;
     }
}

Я не верю, что есть стандартный интерфейс, который предоставляет индексатор. Если вам это нужно, вы можете написать интерфейс и расширить ObservableCollection для его реализации:

public interface IIndexerCollection<T> : IEnumerable<T>
{
    T this[int i]
    {
        get;
    }
}

public class IndexCollection<T> : ObservableCollection<T>, IIndexerCollection<T>
{
}
...