📜 ⬆️ ⬇️

WRL and BindableAttribute

The main pattern in developing UI applications for Windows Runtime is MVVM. The documentation states that the binding object can be a CLR object, a user interface object, a Windows Runtime object (if it has the BindableAttribute attribute or if it implements ICustomPropertyProvider).

The simplest scenario in developing applications is to add the BindableAttribute attribute to the ViewModel class and implement the INotifyPropertyChanged interface. If you are interested in how to do this using MIDL, C ++ and WRL, then welcome under cat.

Interface Description

Create an idl file and describe the interfaces.

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; } } 

In the DataBinding namespace, we define several objects. The first is an exclusive interface for the class. This interface has a single integer property, Value, available for reading and writing. The second is the factory interface for creating a class object. The third is an object class that implements two interfaces. The activatable attribute of the Number class indicates that an object of this class can be created without passing parameters to the factory. The bindable attribute is just that BindableAttribute that is required for the data binding mechanism to work. The description of this attribute is contained in the file “windows.ui.xaml.customattributes.idl”, therefore this file is imported in the second line of the code. Compiling the file, we get the header file, which then will need to be included in our file with the code.
')
Interface implementation

Much of the work on describing interfaces for us is performed by the MIDL compiler when creating a header file, and the use of WRL simplifies the implementation of specified interfaces by defining macros and template classes. Valid code might look like this:

 #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); 

This code defines two classes. The first is the same runtime class Number. Implements the INumber and INotifyPropertyChanged interfaces, overriding the pure virtual interface methods. The second is a factory class for creating objects of the Number class. Successful compilation of the code requires the existence of the “windows.ui.xaml.customattributes.h” header file, since its inclusion directive is automatically inserted by the MIDL compiler into the included header file (but you can also manually remove this directive from the h-file code). I created it in the $ (WindowsSDK_MetadataPath) directory.

Check

Having compiled the component code, you can use it in any project for Windows Runtime. I tested in a C # project. Created a simple view that displays the value of the object's Value property using a DataBinding, and changed the value of the property of the object by timer (3 seconds so that the element has time to appear with the initial value of the property).

Source: https://habr.com/ru/post/248787/


All Articles