WRL и BindableAttribute

Основным паттерном при разработке UI приложений для Windows Runtime является MVVM. В документации говорится, что объектом привязки может быть объект CLR, объект пользовательского интерфейса, объект среды выполнения Windows Runtime (если у него есть атрибут BindableAttribute или если он реализует ICustomPropertyProvider).Наиболее простым сценарием при разработке приложений является добавление атрибута BindableAttribute к классу ViewModel и реализация интерфейса INotifyPropertyChanged. Если интересно, как это сделать с помощью MIDL, C++ и WRL, то добро пожаловать под кат.Описание интерфейсовСоздадим idl файл и опишем интерфейсы. import «inspectable.idl»; import «windows.ui.xaml.customattributes.idl»; import «windows.ui.xaml.data.idl»;

#define VERSION 0×00000001

namespace DataBinding { interface INumber; interface INumberFactory;

runtimeclass Number; }

namespace DataBinding { [exclusiveto (Number)] [uuid (5b197688–2f57–4d01–92cd-a888f10dcd90)] [version (VERSION)] interface INumber: IInspectable { [propget] HRESULT Value ([out, retval] INT32* value);

[propput] HRESULT Value ([in] INT32 value); } [exclusiveto (Number)] [uuid (baec017b-23ec-46d3–8d67–78bf0af1a9f1)] [version (VERSION)] interface INumberFactory: IInspectable { };

[activatable (1.0)] [bindable] [marshaling_behavior (agile)] [threading (both)] [version (VERSION)] runtimeclass Number { [default] interface INumber; interface Windows.UI.Xaml.Data.INotifyPropertyChanged; } } В пространстве имен DataBinding мы определяем несколько объектов. Первый — это эксклюзивный интерфейс для класса. Данный интерфейс имеет единственное целочисленное свойство Value, доступное для чтения и записи. Второй — это интерфейс фабрики для создания объекта класса. Третий- это класс объекта, который реализует два интерфейса. Атрибут activatable класса Number указывают, что объект данного класса может быть создан без передачи параметров фабрике. Атрибут bindable — это как раз тот самый BindableAttribute, который необходим для работы механизма привязки данных. Описание данного атрибута содержится в файле «windows.ui.xaml.customattributes.idl», поэтому данный файл импортируется во второй строчке кода. Скомпилировав файл, получим заголовочный файл, который потом необходимо будет подключить в нашем файле с кодом.Реализация интерфейсов Большую часть работы по описанию интерфейсов за нас выполняет MIDL компилятор при создании заголовочного файла, а использование WRL упрощает реализацию заданных интерфейсов за счёт определения макросов и шаблонных классов. Допустимый код может выглядеть так: #include #include #include #include «DataBinding_h.h»

using ABI: DataBinding: INumber; using ABI: DataBinding: INumberFactory; using ABI: Windows: UI: Xaml: Data: IPropertyChangedEventArgsFactory; using ABI: Windows: UI: Xaml: Data: INotifyPropertyChanged; using ABI: Windows: UI: Xaml: Data: IPropertyChangedEventHandler; using ABI: Windows: UI: Xaml: Data: IPropertyChangedEventArgs; using ABI: Windows: UI: Xaml: Data: PropertyChangedEventArgs; using Microsoft: WRL: RuntimeClassFlags; using Microsoft: WRL: RuntimeClassType; using Microsoft: WRL: EventSource; using Microsoft: WRL: Make; using Microsoft: WRL: RuntimeClass; using Microsoft: WRL: ActivationFactory; using Microsoft: WRL: ComPtr; using Microsoft: WRL: Wrappers: HStringReference;

class Number sealed: public RuntimeClass < RuntimeClassFlags, INumber, INotifyPropertyChanged > { InspectableClass (RuntimeClass_DataBinding_Number, BaseTrust); private: INT32 _value; EventSource _notifyEventSource; ComPtr _valueChangedEventArgs; public:

Number () : _value (0) { ComPtr propertyChangedEventArgsFactory; RoGetActivationFactory ( HStringReference (RuntimeClass_Windows_UI_Xaml_Data_PropertyChangedEventArgs).Get (), ABI: Windows: UI: Xaml: Data: IID_IPropertyChangedEventArgsFactory, reinterpret_cast(propertyChangedEventArgsFactory.GetAddressOf ()));

ComPtr inner; propertyChangedEventArgsFactory→CreateInstance ( HStringReference (L«Value»).Get (), nullptr, reinterpret_cast(_valueChangedEventArgs.GetAddressOf ()), _valueChangedEventArgs.GetAddressOf ()); }

virtual HRESULT STDMETHODCALLTYPE get_Value (INT32* value) override { *value = _value; return S_OK; }

virtual HRESULT STDMETHODCALLTYPE put_Value (INT32 value) override { _value = value; _notifyEventSource.InvokeAll (reinterpret_cast(this), _valueChangedEventArgs.Get ()); return S_OK; }

virtual HRESULT STDMETHODCALLTYPE add_PropertyChanged (IPropertyChangedEventHandler* handler, EventRegistrationToken* token) override { return _notifyEventSource.Add (handler, token); }

virtual HRESULT STDMETHODCALLTYPE remove_PropertyChanged (EventRegistrationToken token) override { return _notifyEventSource.Remove (token); } };

class NumberFactory: public ActivationFactory < INumberFactory > { InspectableClassStatic (RuntimeClass_DataBinding_Number, BaseTrust);

public:

virtual HRESULT STDMETHODCALLTYPE ActivateInstance (IInspectable** instance) override { *instance = reinterpret_cast(Make().Detach ()); return nullptr!= *instance? S_OK: E_OUTOFMEMORY; } };

ActivatableClassWithFactory (Number, NumberFactory); Данный код определяет два класса. Первый — тот самый runtime class Number. Реализует интерфейсы INumber и INotifyPropertyChanged, переопределяя pure virtual методы интерфейсов. Второй — класс фабрики, для создания объектов класса Number. Для успешной компиляции кода необходимо существования заголовочного файла «windows.ui.xaml.customattributes.h», так как директива его включения автоматически вставляется MIDL компилятором в подключаемый заголовочный файл (но можно и вручную удалить эту директиву из кода h-файла). Я его создал в директории $(WindowsSDK_MetadataPath).Проверка Скомпилировав код компонента, можно его использовать в любом проекте для Windows Runtime. Я тестировал в C# проекте. Создал простое представление, отображающие значение свойства Value объекта с помощью DataBinding, и изменил значение свойства объекта по таймеру (3 секунды, чтобы элемент успел отобразиться с начальным значением свойства).

© Habrahabr.ru