//********************************************************* // // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the MIT License. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. // //********************************************************* //! @file //! Helpers that make authoring C++/WinRT components easier. namespace wil { #ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED /// @cond #define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED namespace details { template struct single_threaded_property_storage { T m_value{}; single_threaded_property_storage() = default; single_threaded_property_storage(const T& value) : m_value(value) { } operator T&() { return m_value; } operator T const&() const { return m_value; } template auto operator=(Q&& q) { m_value = wistd::forward(q); return *this; } }; } // namespace details /// @endcond template struct single_threaded_property : std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T> { single_threaded_property() = default; template single_threaded_property(TArgs&&... value) : base_type(std::forward(value)...) { } using base_type = std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T>; T operator()() const { return *this; } // This is the only setter exposed. We don't expose `operator()(Q&& q)`, // since that is what C++/WinRT uses to implement public setters. Since // single_threaded_property is intended for readonly properties, we // don't want to expose that. // // To set the value of this property *internally* (within your // implementation), use this `operator=`: // // MyProperty = 42; // // MyProperty(42); // won't work // // For settable properties, use single_threaded_rw_property instead. template auto& operator=(Q&& q) { static_cast(*this) = std::forward(q); return *this; } }; template struct single_threaded_rw_property : single_threaded_property { using base_type = single_threaded_property; template single_threaded_rw_property(TArgs&&... value) : base_type(std::forward(value)...) { } using base_type::operator(); // needed in lieu of deducing-this template auto& operator()(Q&& q) { return *this = std::forward(q); } // needed in lieu of deducing-this template auto& operator=(Q&& q) { base_type::operator=(std::forward(q)); return *this; } }; #endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED #if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)) || \ defined(WIL_DOXYGEN) // WinRT / XAML helpers /// @cond #define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION namespace details { template struct event_base { winrt::event_token operator()(const T& handler) { return m_handler.add(handler); } auto operator()(const winrt::event_token& token) noexcept { return m_handler.remove(token); } template auto invoke(TArgs&&... args) { return m_handler(std::forward(args)...); } private: winrt::event m_handler; }; } // namespace details /// @endcond /** * @brief A default event handler that maps to * [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1). * @tparam T The event data type. */ template struct untyped_event : wil::details::event_base> { }; /** * @brief A default event handler that maps to * [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2). * @tparam T The event data type. * @details Usage example: * @code * // In IDL, this corresponds to: * // event Windows.Foundation.TypedEventHandler OkClicked; * wil::typed_event OkClicked; * @endcode */ template struct typed_event : wil::details::event_base> { }; #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H) #if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))) || \ defined(WIL_DOXYGEN) // INotifyPropertyChanged helpers /// @cond #define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA namespace details { #ifdef WINRT_Microsoft_UI_Xaml_Data_H using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler; using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs; #elif defined(WINRT_Windows_UI_Xaml_Data_H) using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler; using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs; #endif } // namespace details /// @endcond /** * @brief Helper base class to inherit from to have a simple implementation of * [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged). * @tparam T CRTP type * @details When you declare your class, make this class a base class and pass your class as a template parameter: * @code * struct MyPage : MyPageT, wil::notify_property_changed_base * { * wil::single_threaded_notifying_property MyInt; * MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { } * // or * WIL_NOTIFYING_PROPERTY(int, MyInt, 42); * }; * @endcode */ template struct notify_property_changed_base { using Type = T; auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value) { return m_propertyChanged.add(value); } void PropertyChanged(winrt::event_token const& token) { m_propertyChanged.remove(token); } Type& self() { return *static_cast(this); } /** * @brief Raises a property change notification event * @param name The name of the property * @return * @details Usage example\n * C++ * @code * void MyPage::DoSomething() * { * // modify MyInt * // MyInt = ... * * // now send a notification to update the bound UI elements * RaisePropertyChanged(L"MyInt"); * } * @endcode */ auto RaisePropertyChanged(std::wstring_view name) { return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name}); } protected: winrt::event m_propertyChanged; }; /** * @brief Implements a property type with notifications * @tparam T the property type * @details Use the INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the * right property name, and bind it to the `notify_property_changed_base` implementation. */ template struct single_threaded_notifying_property : single_threaded_rw_property { using Type = T; using base_type = single_threaded_rw_property; using base_type::operator(); template auto& operator()(Q&& q) { return *this = std::forward(q); } template auto& operator=(Q&& q) { if (q != this->operator()()) { static_cast(*this) = std::forward(q); if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr)) { (*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{m_name}); } } return *this; } template single_threaded_notifying_property( winrt::event* npc, const winrt::Windows::Foundation::IInspectable& sender, std::wstring_view name, TArgs&&... args) : single_threaded_rw_property(std::forward(args)...), m_name(name), m_npc(npc), m_sender(sender) { } single_threaded_notifying_property(const single_threaded_notifying_property&) = default; single_threaded_notifying_property(single_threaded_notifying_property&&) = default; std::wstring_view Name() const noexcept { return m_name; } private: std::wstring_view m_name; winrt::event* m_npc; winrt::weak_ref m_sender; }; /** * @def WIL_NOTIFYING_PROPERTY * @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage * alternative to wil::single_threaded_notifying_property. * @details You can pass an initializer list for the initial property value in the variadic arguments to this macro. */ #define WIL_NOTIFYING_PROPERTY(type, name, ...) \ type m_##name{__VA_ARGS__}; \ auto name() const noexcept \ { \ return m_##name; \ } \ auto& name(type value) \ { \ if (m_##name != value) \ { \ m_##name = std::move(value); \ RaisePropertyChanged(L"" #name); \ } \ return *this; \ } /** * @def INIT_NOTIFYING_PROPERTY * @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor. */ #define INIT_NOTIFYING_PROPERTY(NAME, VALUE) NAME(&m_propertyChanged, *this, L"" #NAME, VALUE) #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) } // namespace wil