Если вы работаете с XAML на любом уровне, вы должны использовать привязку для любых данных, которые вы обрабатываете в своем приложении. А если вы не используете связывание, то, вероятно, вам следует это сделать — как для удобства, так и для целей будущей совместимости. Но это выходит за рамки этой статьи. Со многими приложениями для Windows Phone, над которыми я работаю, я заметил одну общую черту — привязка почти всегда одинакова, с небольшими изменениями, адаптированными к конкретной структуре проекта.
Он состоит из следующих компонентов:
- Класс привязки (viewmodel, если хотите) — центральный блок привязки для определенных областей приложения.
- Модель — очевидно, представление класса данных, которые используются приложением.
- Представление — пользовательский интерфейс, например — экземпляр PhoneApplicationPage.
Эй, разве это не основная структура для MVVM ? Это действительно так. Однако я заметил, что многие разработчики часто переходят на использование сторонних библиотек, которые организуют для них базовую привязку без необходимости выполнять «прокладку». Но всегда ли это необходимо? Из своего собственного опыта я знаю, что сторонние библиотеки могут стоить очень дорого, если их использовать там, где они не нужны. DLL-файлы MVVM могут быть не такими уж плохими, но зачем импортировать дополнительную сборку с большим мертвым весом, если требуются только базовые возможности связывания?
Хватит говорить, давайте посмотрим на код. Сначала давайте посмотрим, что является основным связующим мостом:
using System; using System.ComponentModel; using System.Collections.ObjectModel; namespace BindingHarness { public class BindingCentral : INotifyPropertyChanged { static BindingCentral instance = null; static readonly object padlock = new object(); public BindingCentral() { if (NewLinks == null) NewLinks = new ObservableCollection<Link>(); } public static BindingCentral Instance { get { lock (padlock) { if (instance == null) { instance = new BindingCentral(); } return instance; } } } private ObservableCollection<string> _newLinks; public ObservableCollection<string> NewLinks { get { return _newLinks; } set { if (_newLinks != value) { _newLinks = value; NotifyPropertyChanged("NewLinks"); } } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { System.Windows.Deployment.Current.Dispatcher.BeginInvoke( () => { PropertyChanged(this, new PropertyChangedEventArgs(info)); }); } } } }
Прежде всего, вы можете заметить, что этот класс следует одноэлементному шаблону, описанному здесь Джоном Скитом . Упрощенный, но потокобезопасный. Вы, наверное, знаете, что когда вы создаете ссылки на статические классы в XAML, действительно легко получить доступ к данным, доступным через них. В случае классов, основанных на экземплярах, эта проблема может добавить некоторые сложности, и именно поэтому в самом классе есть статический экземпляр, который оказывается очень полезным для целей связывания.
BindingCentral орудия не INotifyPropertyChanged — ничего слишком сложного. Всякий раз, когда свойство изменяется, возникает событие PropertyChanged , уведомляющее приложение о том, что одно из свойств, которые могут быть связаны где-то (не всегда так, конечно), изменено, и должны быть предприняты надлежащие действия по обновлению.
ВОПРОС: Почему я использую System.Windows.Deployment.Current.Dispatcher.BeginInvoke вместо простого вызова PropertyChanged ?
Это связано с тем, что при привязке к элементу пользовательского интерфейса сама привязка будет обрабатываться в потоке пользовательского интерфейса. Поэтому обновлением также манипулируют в потоке пользовательского интерфейса. Без надлежащей обработки между потоками это вызовет исключение. Альтернативой этому было бы использование диспетчера, который уже связан с экземпляром PhoneApplicationPage , хотя я бы не рекомендовал идти по этому пути, потому что это создает дополнительный слой потоков, который не нужен.
xmlns:local="clr-namespace:BindingHarness"
В разделе ресурсов я добавляю ссылку на фактический класс привязки, с которым я работал. Обратите внимание, что нигде в этой декларации я не упоминаю о существовании статического экземпляра.
<Application.Resources> <local:BindingCentral x:Key="BindingCentral"></local:BindingCentral> </Application.Resources>
Использование свойства Instance делегировано элементам, которые связаны с реальным классом. Например, если у меня есть ListBox, который связан с ObservableCollection внутри BindingCentral, выражение привязки будет выглядеть так:
<ListBox ItemsSource="{Binding Path=Instance.NewLinks,Source={StaticResource BindingCentral}}">
Поздравляю, теперь у вас есть очень простой механизм MVVM. Это может потребовать серьезных корректировок в зависимости от проекта, но для многих задач, где используется только несколько коллекций и свойств, это идеальное облегченное решение.