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 0x00000001  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 <wrl.h> #include <wrl/wrappers/corewrappers.h> #include <wrl/event.h> #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<RuntimeClassType::WinRt>, INumber, INotifyPropertyChanged > { 	InspectableClass(RuntimeClass_DataBinding_Number, BaseTrust); private: 	INT32 _value; 	EventSource<IPropertyChangedEventHandler> _notifyEventSource; 	ComPtr<IPropertyChangedEventArgs> _valueChangedEventArgs; public:  	Number() 		: _value(0) 	{ 		ComPtr<IPropertyChangedEventArgsFactory> propertyChangedEventArgsFactory; 		RoGetActivationFactory( 			HStringReference(RuntimeClass_Windows_UI_Xaml_Data_PropertyChangedEventArgs).Get(), 			ABI::Windows::UI::Xaml::Data::IID_IPropertyChangedEventArgsFactory, 			reinterpret_cast<void**>(propertyChangedEventArgsFactory.GetAddressOf()));  		ComPtr<IInspectable> inner; 		propertyChangedEventArgsFactory->CreateInstance( 			HStringReference(L"Value").Get(), 			nullptr, 			reinterpret_cast<IInspectable**>(_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<IInspectable*>(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<IInspectable*>(Make<Number>().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 секунды, чтобы элемент успел отобразиться с начальным значением свойства).

ссылка на оригинал статьи http://habrahabr.ru/post/248787/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *