Статьи

Простая привязка для простых (возможно, нет) приложений Windows Phone

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

Есть идея лучшего связывания без использования сторонних библиотек? Поделитесь этим в комментариях ниже!