Если вы работаете с 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. Это может потребовать серьезных корректировок в зависимости от проекта, но для многих задач, где используется только несколько коллекций и свойств, это идеальное облегченное решение.