CppXAML
Loading...
Searching...
No Matches
Controls.h
Go to the documentation of this file.
1#pragma once
2#include <winrt/windows.ui.xaml.h>
3#include <winrt/windows.ui.xaml.media.h>
4#include <winrt/windows.UI.Xaml.Controls.h>
5#include <winrt/Windows.UI.Xaml.Interop.h>
6#include <winrt/Windows.UI.Xaml.Markup.h>
7#include <winrt/base.h>
8#include <winrt/Windows.Foundation.Collections.h>
9#include <winrt/Windows.Foundation.h>
10#include <type_traits>
11
12#include <cppxaml/utils.h>
13#include <cppxaml/VisualState.h>
14#ifdef USE_WINUI3
15#include <microsoft.ui.xaml.window.h>
16#endif
17
31namespace cppxaml {
32
33 namespace details {
39 template<typename T>
40 struct WrapperT {
41 T m_value{ nullptr };
42
43 WrapperT(const T& v = T()) : m_value(v) {}
44
45 T operator*() const {
46 return m_value;
47 }
48 const T* operator->() const { return &m_value; }
49
54 auto Name() const { return m_value.Name(); }
60 auto Name(std::wstring_view n) const { m_value.Name(n); return *this; }
61
66 auto Margin() const { return m_value.Margin(); }
72 auto Margin(cppxaml::xaml::Thickness t) const { m_value.Margin(t); return *this; }
78 auto Margin(double m) const { m_value.Margin(cppxaml::xaml::ThicknessHelper::FromUniformLength(m)); return *this; }
87 auto Margin(double left, double top, double right, double bottom) const {
88 m_value.Margin(cppxaml::xaml::ThicknessHelper::FromLengths(left, top, right, bottom)); return *this;
89 }
90
95 auto Padding() const { return m_value.Padding(); }
101 auto Padding(cppxaml::xaml::Thickness t) const { m_value.Padding(t); return *this; }
107 auto Padding(double m) const { m_value.Padding(cppxaml::xaml::ThicknessHelper::FromUniformLength(m)); return *this; }
116 auto Padding(double left, double top, double right, double bottom) const {
117 m_value.Padding(cppxaml::xaml::ThicknessHelper::FromLengths(left, top, right, bottom)); return *this;
118 }
119
120 template<typename D, std::enable_if_t<std::is_assignable_v<D, T>, bool> = true>
121 operator D() const {
122 return m_value;
123 }
124
138 template<typename TValue>
139 auto Set(cppxaml::xaml::DependencyProperty dp, TValue&& value) {
140 if constexpr (std::is_assignable_v<winrt::Windows::Foundation::IInspectable, TValue>) {
141 m_value.SetValue(dp, std::move(value));
142 }
143 else {
144 m_value.SetValue(dp, winrt::box_value(std::move(value)));
145 }
146 return *this;
147 }
148
170 auto VisualStates(const std::unordered_map<std::wstring, cppxaml::xaml::VisualStateChangedEventHandler>& map) {
171 return cppxaml::VSMListener(*this, map);
172 }
173 };
174
175 using VisualStateMap = std::unordered_map<std::wstring, cppxaml::xaml::VisualStateChangedEventHandler>;
176
177 template<typename T, typename TItems = void>
178 struct Wrapper : WrapperT<T> {};
179
183 template<>
184 struct Wrapper<cppxaml::xaml::Controls::ContentDialog> : WrapperT<cppxaml::xaml::Controls::ContentDialog> {
185 auto PrimaryButtonText(std::wstring_view t) {
186 this->m_value.as<cppxaml::xaml::Controls::ContentDialog>().PrimaryButtonText(t);
187 return *this;
188 }
189 };
190
194 template<>
195 struct Wrapper<cppxaml::xaml::Controls::StackPanel> : WrapperT<cppxaml::xaml::Controls::StackPanel> {
196 auto Orientation(cppxaml::xaml::Controls::Orientation o) {
197 this->m_value.as<cppxaml::xaml::Controls::StackPanel>().Orientation(o);
198 return *this;
199 }
200 };
201
206 template<typename TItems>
207 struct Wrapper<cppxaml::xaml::Controls::AutoSuggestBox, TItems> : WrapperT<cppxaml::xaml::Controls::AutoSuggestBox>, std::enable_shared_from_this<Wrapper<cppxaml::xaml::Controls::AutoSuggestBox, TItems>> {
208 TItems m_items;
209
210 bool m_caseInsensitive{ false };
211 Wrapper() = delete;
212 Wrapper(const TItems& items) : m_items(items) {}
213 Wrapper(const Wrapper<cppxaml::xaml::Controls::AutoSuggestBox, TItems>& other) : m_items(other.m_items) {
214 if (other.m_textChangedToken) {
215 SetEventHandlers(other.m_caseInsensitive);
216 }
217 }
218 Wrapper(Wrapper&& other) : m_items(other.m_items),
219 m_textChangedToken(std::move(other.m_textChangedToken)),
220 m_suggestionChosenToken(std::move(other.m_suggestionChosenToken))
221 {
222 other.m_textChangedToken = {};
223 other.m_suggestionChosenToken = {};
224 }
225
226 ~Wrapper() {
227 // Wrapper objects get destroyed as their only purpose is to facilitate creating the wrapped XAML objects
228 // However, wrapper objects set up event handlers (like these AutoSuggest ones). Therefore these event handlers must:
229 // a) not use any state from Wrapper
230 // b) not be de-registered upon Wrapper's destructor
231 // c) be idempotent (as more than one event handler will likely run, as Wrappers can get copy-constructed/move constructed around).
232
233 //if (m_textChangedToken) {
234 // m_value.TextChanged(m_textChangedToken);
235 //}
236 //if (m_suggestionChosenToken) {
237 // m_value.SuggestionChosen(m_suggestionChosenToken);
238 //}
239 }
240 private:
241 winrt::event_token m_textChangedToken{};
242 winrt::event_token m_suggestionChosenToken{};
243 void SetEventHandlers(bool caseInsensitive) {
244 m_caseInsensitive = caseInsensitive;
245 // work around MSVC bug: https://developercommunity.visualstudio.com/t/c3779-when-using-type-in-a-class-method-in-a-templ/1617634
246 auto GetReason = [](cppxaml::xaml::Controls::AutoSuggestBoxTextChangedEventArgs const& args) -> cppxaml::xaml::Controls::AutoSuggestionBoxTextChangeReason { return args.Reason(); };
247 m_textChangedToken = m_value.TextChanged([GetReason, caseInsensitive, items = this->m_items](cppxaml::xaml::Controls::AutoSuggestBox sender, cppxaml::xaml::Controls::AutoSuggestBoxTextChangedEventArgs args) {
248 if (GetReason(args) == cppxaml::xaml::Controls::AutoSuggestionBoxTextChangeReason::UserInput) {
249 std::wstring search = caseInsensitive ? utils::tolower(sender.Text()) : sender.Text().c_str();
250 auto vec = TItems();
251
252 for (const auto& entry : items) {
253 auto str = caseInsensitive ? utils::tolower(entry) : entry.c_str();
254 if (search == L"" || str.find(search) != -1) {
255 vec.push_back(entry);
256 }
257 }
258 auto suitableItems = winrt::single_threaded_vector<winrt::Windows::Foundation::IInspectable>();
259 winrt::Windows::Foundation::IInspectable selected{ nullptr };
260 for (const auto& e : vec) {
261 auto boxed = winrt::box_value(e);
262 if (utils::tolower(e) == search && search != L"") {
263 selected = boxed;
264 }
265 suitableItems.Append(boxed);
266 }
267 sender.ItemsSource(suitableItems);
268 if (selected) {
269 sender.Text(winrt::unbox_value<winrt::hstring>(selected));
270 }
271 }
272 });
273 m_suggestionChosenToken = m_value.SuggestionChosen([](cppxaml::xaml::Controls::AutoSuggestBox sender, cppxaml::xaml::Controls::AutoSuggestBoxSuggestionChosenEventArgs args) {
274 sender.Text(winrt::unbox_value<winrt::hstring>(args.SelectedItem()));
275 });
276
277 }
278 public:
284 auto EnableDefaultSearch(bool caseInsensitive = true) {
285 SetEventHandlers(caseInsensitive);
286 return *this;
287 }
288 };
289
290 template<typename TItems>
291 struct Wrapper<cppxaml::xaml::Controls::ItemsControl, TItems> : WrapperT<cppxaml::xaml::Controls::ItemsControl> {
292 using items_t = TItems;
293
294 };
295
299 template<>
300 struct Wrapper<cppxaml::xaml::Controls::MenuFlyoutItem> : WrapperT<cppxaml::xaml::Controls::MenuFlyoutItem> {
305 cppxaml::xaml::Controls::IconElement IconElement() const { return m_value.Icon(); }
311 auto IconElement(cppxaml::xaml::Controls::IconElement icon) const {
312 m_value.Icon(icon); return *this;
313 }
314 winrt::event_token m_clickToken{};
319 auto Click() const {
320 return m_clickToken;
321 }
327 auto Click(cppxaml::xaml::RoutedEventHandler handler) {
328 m_clickToken = m_value.Click(handler);
329 return *this;
330 }
331 };
332
336 template<>
337 struct Wrapper<cppxaml::xaml::Controls::MenuFlyout> : WrapperT<cppxaml::xaml::Controls::MenuFlyout> {
344 template<typename F>
345 auto CentralizedHandler(const F& f) {
346 if (m_eventHandlers.size() != 0) {
347 throw std::exception("Centralized handler already set");
348 }
349
350 for (auto i : m_value.Items()) {
351 if (auto mfi = i.try_as<cppxaml::xaml::Controls::MenuFlyoutItem>()) {
352 m_eventHandlers.push_back(mfi.Click([f](winrt::Windows::Foundation::IInspectable sender, cppxaml::xaml::RoutedEventArgs args) {
353 f(sender, args);
354 }));
355 }
356 }
357 return *this;
358 }
359 std::vector<winrt::event_token> m_eventHandlers{};
360 ~Wrapper() {
361 // Wrapper goes out of scope when ShowAt is called, before the event handlers have had a chance to fire. Don't dismiss them for now.
362 /*
363 if (m_eventHandlers.size() == m_value.Items().Size()) {
364 for (auto i = 0u; i < m_eventHandlers.size(); i++) {
365 if (auto mfi = m_value.Items().GetAt(i).try_as<cppxaml::xaml::Controls::MenuFlyoutItem>()) {
366 mfi.Click(m_eventHandlers[i]);
367 }
368 else {
369 break; // bail, something is off
370 }
371 }
372 }
373 */
374 }
375 };
376
377 struct GridLength2 {
378 operator cppxaml::xaml::GridLength() const { return m_length; }
379 GridLength2(double len) : m_length(cppxaml::xaml::GridLengthHelper::FromPixels(len)) {}
380 GridLength2(std::wstring_view v) {
381 m_length = winrt::unbox_value<cppxaml::xaml::GridLength>(cppxaml::xaml::Markup::XamlBindingHelper::ConvertValue(winrt::xaml_typename<cppxaml::xaml::GridLength>(), winrt::box_value(v)));
382 }
383 GridLength2(std::string_view v) : GridLength2(winrt::to_hstring(v)) {}
384 private:
385 cppxaml::xaml::GridLength m_length;
386 };
387 struct GridLengths {
388 std::vector<cppxaml::xaml::GridLength> m_lengths;
389 GridLengths(std::initializer_list<GridLength2>&& ls) {
390 for (auto& l : ls) {
391 m_lengths.push_back(l);
392 }
393 }
394 GridLengths(std::string_view sv) {
395 std::string_view last;
396 for (std::string_view::size_type pos = 0; pos < sv.length(); ) {
397 auto nextDelimiter = sv.find(",", pos);
398 last = sv.substr(pos, nextDelimiter - pos);
399 auto len = winrt::unbox_value<cppxaml::xaml::GridLength>(cppxaml::xaml::Markup::XamlBindingHelper::ConvertValue(winrt::xaml_typename<cppxaml::xaml::GridLength>(), winrt::box_value(winrt::to_hstring(last))));
400 m_lengths.push_back(len);
401
402 if (nextDelimiter != -1) {
403 pos = nextDelimiter + 1;
404 }
405 else {
406 break;
407 }
408 }
409 }
410 };
411
412 using GridRows = GridLengths;
413 using GridColumns = GridLengths;
414
415 struct UIElementInGrid {
416 int m_row{};
417 int m_column{};
418 cppxaml::xaml::UIElement m_element{ nullptr };
419 };
420
421 } // namespace details
422
430 template<typename T>
431 IF_ASSIGNABLE_CONTROL(ContentControl)
432 MakeContentControl(winrt::Windows::Foundation::IInspectable i) {
433 cppxaml::details::Wrapper<T> t;
434 t->Content(i);
435 return t;
436 }
437
444 template<typename T>
445 IF_ASSIGNABLE_CONTROL(ContentControl)
446 MakeContentControl(winrt::hstring v) {
447 cppxaml::details::Wrapper<T> t;
448 t->Content(winrt::box_value(v));
449 return t;
450 }
451
458 template<typename T>
459 IF_ASSIGNABLE_CONTROL(Panel)
460 MakePanel(const std::initializer_list<cppxaml::xaml::UIElement>& elems) {
461 cppxaml::details::Wrapper<T> panel;
462 for (auto e : elems) {
463 panel->Children().Append(e);
464 }
465 return panel;
466 }
467
475 template<typename C>
476 auto ContentDialog(C i) {
477 return MakeContentControl<cppxaml::xaml::Controls::ContentDialog>(i);
478 }
479
485 auto StackPanel(const std::initializer_list<cppxaml::xaml::UIElement>& elems) {
486 return MakePanel<cppxaml::xaml::Controls::StackPanel>(elems);
487 }
488
508 template<typename TElements>
509 DOXY_RT(cppxaml::details::Wrapper<cppxaml::xaml::Controls::Grid>)
510 Grid(details::GridRows gr, cppxaml::details::GridColumns gc, TElements /*std::initializer_list<details::UIElementInGrid>& */&& elems) {
511 auto grid = cppxaml::details::Wrapper<cppxaml::xaml::Controls::Grid>();
512 for (auto& r : gr.m_lengths) {
513 auto rd = cppxaml::xaml::Controls::RowDefinition();
514 rd.Height(r);
515 grid->RowDefinitions().Append(rd);
516 }
517 for (auto& c : gc.m_lengths) {
518 auto cd = cppxaml::xaml::Controls::ColumnDefinition();
519 cd.Width(c);
520 grid->ColumnDefinitions().Append(cd);
521 }
522
523 for (auto& e : elems) {
524 if constexpr (std::is_assignable_v<cppxaml::details::UIElementInGrid, decltype(e)>) {
525 grid->Children().Append(e.m_element);
526 cppxaml::xaml::Controls::Grid::SetRow(e.m_element.as<cppxaml::xaml::FrameworkElement>(), e.m_row);
527 cppxaml::xaml::Controls::Grid::SetColumn(e.m_element.as<cppxaml::xaml::FrameworkElement>(), e.m_column);
528 }
529 else {
530 grid->Children().Append(e);
531 }
532 }
533 return grid;
534 }
535
541 auto TextBox(std::wstring_view text = L"") {
542 cppxaml::details::Wrapper<cppxaml::xaml::Controls::TextBox> tb;
543 tb->Text(text);
544 return tb;
545 }
546
557 template<typename T, typename TItems>
558 IF_ASSIGNABLE_CONTROL_TITEMS(ItemsControl, TItems)
559 MakeItemsControl(const TItems& /*std::initializer_list<Windows::Foundation::IInspectable>*/ items) {
560 cppxaml::details::Wrapper<T, TItems> t(items);
561 for (auto& i : items) {
562 if constexpr (std::is_assignable_v<winrt::Windows::Foundation::IInspectable, decltype(i)>) {
563 t->Items().Append(i);
564 }
565 else {
566 t->Items().Append(winrt::box_value(i));
567 }
568 }
569 return t;
570 }
571
579 template<typename TItems>
580 auto AutoSuggestBox(const TItems& /*std::initializer_list<std::wstring_view>*/ svs) {
581 return MakeItemsControl<cppxaml::xaml::Controls::AutoSuggestBox, TItems>(svs);
582 }
583
589 DOXY_RT(cppxaml::details::Wrapper<cppxaml::xaml::Controls::TextBlock>)
590 TextBlock(std::wstring_view text) {
591 cppxaml::details::Wrapper<cppxaml::xaml::Controls::TextBlock> tb;
592 tb->Text(text);
593 return tb;
594 }
595
603 template<typename C>
604 auto Button(C c) {
605 return MakeContentControl<cppxaml::xaml::Controls::Button>(c);
606 }
607
613 auto FontIcon(std::wstring_view icon) {
614 cppxaml::details::Wrapper<cppxaml::xaml::Controls::FontIcon> fi;
615 fi->Glyph(icon);
616 return fi;
617 }
618
624 auto FontIcon(uint32_t glyph) {
625 wchar_t icon[3]{};
626 icon[0] = glyph & 0xffff;
627 icon[1] = (glyph >> 16) & 0xffff;
628 return FontIcon(icon);
629 }
630
631
637 auto MenuFlyoutItem(std::wstring_view text) {
638 cppxaml::details::Wrapper<cppxaml::xaml::Controls::MenuFlyoutItem> mfi;
639 mfi->Text(text);
640 return mfi;
641 }
642
643 template<typename T, typename TArg>
644 void AddItems(T t, TArg&& first) {
645 t.Items().Append(first);
646 }
647
648 template<typename T, typename TArg, typename... TArgs>
649 void AddItems(T t, TArg&& first, TArgs&&... rest) {
650 t.Items().Append(first);
651 if constexpr (sizeof...(TArgs) > 0) {
652 AddItems(t, rest...);
653 }
654 }
655
661 template<typename... TMenuFlyoutItemBase>
662 auto MenuFlyout(TMenuFlyoutItemBase&&... items) {
663 cppxaml::details::Wrapper<cppxaml::xaml::Controls::MenuFlyout> mf;
664 AddItems(*mf, items...);
665 return mf;
666 }
667
668}
auto tolower(std::wstring_view sv)
Definition: utils.h:34
The main CppXAML namespace.
MakeItemsControl(const TItems &items)
Creates a XAML element of a type that derives from ItemsControl
Definition: Controls.h:559
MakeContentControl(winrt::Windows::Foundation::IInspectable i)
Create a XAML element of a class that derives from ContentControl
Definition: Controls.h:432
auto Button(C c)
Creates a Button.
Definition: Controls.h:604
auto ContentDialog(C i)
Creates a ContentDialog
Definition: Controls.h:476
Grid(details::GridRows gr, cppxaml::details::GridColumns gc, TElements &&elems)
Creates a Grid with the specified rows, columns, and children.
Definition: Controls.h:510
auto StackPanel(const std::initializer_list< cppxaml::xaml::UIElement > &elems)
Creates a StackPanel.
Definition: Controls.h:485
auto TextBox(std::wstring_view text=L"")
Creates a TextBox
Definition: Controls.h:541
auto MenuFlyout(TMenuFlyoutItemBase &&... items)
Creates a MenuFlyout from a list of MenuFlyoutItems
Definition: Controls.h:662
MakePanel(const std::initializer_list< cppxaml::xaml::UIElement > &elems)
Creates a XAML element of a type that is a subclass of Panel.
Definition: Controls.h:460
auto FontIcon(std::wstring_view icon)
Creates a FontIcon from a string.
Definition: Controls.h:613
auto AutoSuggestBox(const TItems &svs)
Creates an AutoSuggestBox from a list of items.
Definition: Controls.h:580
auto MenuFlyoutItem(std::wstring_view text)
Creates a MenuFlyoutItem with the specified text.
Definition: Controls.h:637
TextBlock(std::wstring_view text)
Creates a TextBlock from its text content.
Definition: Controls.h:590
auto EnableDefaultSearch(bool caseInsensitive=true)
Enables a default search experience for the AutoSuggestBox. The list of items shown gets filtered as ...
Definition: Controls.h:284
auto CentralizedHandler(const F &f)
Sets up a centralized Click handler for all the MenuFlyoutItems in the MenuFlyout.
Definition: Controls.h:345
auto Click(cppxaml::xaml::RoutedEventHandler handler)
Definition: Controls.h:327
cppxaml::xaml::Controls::IconElement IconElement() const
Definition: Controls.h:305
auto IconElement(cppxaml::xaml::Controls::IconElement icon) const
Definition: Controls.h:311
Internal wrapper type that powers builder-style programming. This type is usually constructed and de...
Definition: Controls.h:40
auto Padding(double left, double top, double right, double bottom) const
Definition: Controls.h:116
auto Name(std::wstring_view n) const
Set the element's Name.
Definition: Controls.h:60
auto Name() const
Returns the element's Name.
Definition: Controls.h:54
auto Margin() const
Definition: Controls.h:66
auto Margin(double m) const
Definition: Controls.h:78
auto Margin(double left, double top, double right, double bottom) const
Definition: Controls.h:87
auto Set(cppxaml::xaml::DependencyProperty dp, TValue &&value)
Sets a property.
Definition: Controls.h:139
auto Margin(cppxaml::xaml::Thickness t) const
Definition: Controls.h:72
auto Padding() const
Definition: Controls.h:95
auto Padding(cppxaml::xaml::Thickness t) const
Definition: Controls.h:101
auto Padding(double m) const
Definition: Controls.h:107
auto VisualStates(const std::unordered_map< std::wstring, cppxaml::xaml::VisualStateChangedEventHandler > &map)
Sets up a visual state change listeners.
Definition: Controls.h:170