Что такое ObservableCollection и когда её использовать?
ObservableCollection<T> — это коллекция из пространства имён System.Collections.ObjectModel, которая представляет собой динамический список объектов, автоматически уведомляющий интерфейс пользователя об изменениях: добавлении, удалении или обновлении элементов. Это делает её крайне полезной и практически стандартной для работы с коллекциями данных в архитектуре MVVM в приложениях на Xamarin.Forms.
Основные характеристики ObservableCollection
-
Поддерживает уведомление об изменениях — реализует интерфейс INotifyCollectionChanged.
-
Работает с привязкой данных (Data Binding) — изменения в коллекции автоматически отображаются в UI, например в ListView, CollectionView и других элементах.
-
Реализует IList<T> и IEnumerable<T> — с коллекцией можно работать как со списком.
-
Удобна для MVVM-подхода — позволяет ViewModel уведомлять View о добавлении/удалении элементов без дополнительных действий.
Когда использовать ObservableCollection
-
Когда вы хотите отображать список элементов в интерфейсе, который может динамически меняться.
-
Когда необходимо, чтобы UI автоматически обновлялся при изменении коллекции.
-
При использовании двусторонней привязки (two-way binding) к элементам, как ListView, CollectionView, Picker, ListBox и т.д.
-
Когда нужно реализовать добавление, удаление, обновление элементов из ViewModel, сохраняя синхронность с UI.
Пример использования в ViewModel
using System.Collections.ObjectModel;
using System.ComponentModel;
public class Contact
{
public string Name { get; set; }
public string Phone { get; set; }
}
public class ContactsViewModel : INotifyPropertyChanged
{
public ObservableCollection<Contact> Contacts { get; }
public ContactsViewModel()
{
Contacts = new ObservableCollection<Contact>
{
new Contact { Name = "Анна", Phone = "123-456" },
new Contact { Name = "Иван", Phone = "987-654" }
};
}
public event PropertyChangedEventHandler PropertyChanged;
}
Привязка к XAML
<ContentPage.BindingContext>
<local:ContactsViewModel />
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding Contacts}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" Detail="{Binding Phone}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Когда вы добавите или удалите элементы из Contacts, ListView обновится автоматически.
Работа с ObservableCollection
Добавление:
Contacts.Add(new Contact { Name = "Мария", Phone = "555-123" });
Удаление:
var contactToRemove = Contacts.FirstOrDefault(c => c.Name == "Анна");
if (contactToRemove != null)
Contacts.Remove(contactToRemove);
Очистка:
Contacts.Clear();
Как это работает под капотом
Класс ObservableCollection<T> реализует интерфейс INotifyCollectionChanged и генерирует событие CollectionChanged, которое отслеживается элементами управления (например, ListView).
Это событие содержит информацию о:
-
Типе изменения (Add, Remove, Reset, Replace, Move)
-
Элементах, участвующих в изменении
-
Индексах изменений
Благодаря этому фреймворк Xamarin.Forms или WPF может частично обновлять UI, не перерисовывая весь список.
Ограничения ObservableCollection
Не уведомляет об изменении свойств объектов внутри коллекции
Если у вас есть объект Contact и вы изменяете Name, ListView не отреагирует на это изменение, если Contact не реализует INotifyPropertyChanged.
Решение:
```python
public class Contact : INotifyPropertyChanged
{
private string name;
public string Name
{
get => name;
set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
public string Phone { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
**Не потокобезопасна
**Изменения ObservableCollection из другого потока (например, при асинхронной загрузке) вызывают исключения. Добавлять/удалять элементы можно **только из UI-потока**.
<br/>Решение: использовать Device.BeginInvokeOnMainThread() или MainThread.InvokeOnMainThreadAsync() из Xamarin Essentials:
<br/>```python
Device.BeginInvokeOnMainThread(() =>
{
Contacts.Add(new Contact { Name = "Новый", Phone = "000-000" });
});
Нет поддержки сортировки или фильтрации
Чтобы отсортировать коллекцию, нужно создавать новую ObservableCollection:
```python
var sorted = Contacts.OrderBy(c => c.Name).ToList();
Contacts.Clear();
foreach (var item in sorted)
Contacts.Add(item);
### **ObservableCollection vs List**
| **Свойство** | **List<T>** | **ObservableCollection<T>** |
| --- | --- | --- |
| Уведомляет UI об изменениях | ❌ Нет | ✅ Да |
| --- | --- | --- |
| Поддержка Data Binding | ❌ Ограниченная | ✅ Полная |
| --- | --- | --- |
| Подходит для MVVM | ❌ Не рекомендуется | ✅ Рекомендуется |
| --- | --- | --- |
| Производительность | ✅ Быстрее | ❌ Медленнее при больших объёмах |
| --- | --- | --- |
### **Альтернативы и расширения**
- **BindingList<T>** — устаревший тип из WinForms, поддерживает уведомления, но менее эффективен и не рекомендуется в новых проектах.
- **ReadOnlyObservableCollection<T>** — только для чтения (без возможности добавлять извне).
- **Расширенные коллекции** (через сторонние библиотеки, например DynamicData, MvvmCross.ObservableCollections) — поддержка фильтрации, сортировки, буферизации и др.
### **Пример расширенного использования**
Допустим, вы хотите добавить кнопку, при нажатии на которую в список добавляется новый элемент.
#### **ViewModel:**
```python
public ICommand AddContactCommand { get; }
public ContactsViewModel()
{
Contacts = new ObservableCollection<Contact>();
AddContactCommand = new Command(() =>
{
Contacts.Add(new Contact { Name = "Новый контакт", Phone = "000-000" });
});
}
XAML:
<Button Text="Добавить контакт"
Command="{Binding AddContactCommand}" />
ObservableCollection<T> — это мощный инструмент, позволяющий строить динамические, интерактивные пользовательские интерфейсы в приложениях Xamarin.Forms. Она играет центральную роль в реализации шаблона MVVM, обеспечивая синхронизацию между представлением (View) и моделью (ViewModel) без необходимости вручную обновлять интерфейс.