Skip to content

Commit 4b1944e

Browse files
authored
Avoid CommandBarFlyoutCommandBar clipping after dynamic command bar element change (microsoft#5347)
* 32506205 fix - part 1 * Adding comments * Adding new regression test * Applying PR feedback * Made couple of comments more precise * Adding #ifdef _DEBUG for traces * Fixing versions <= RS3 * Fixing RS2/3 KeyboardAcceleratorTextOverrideProperty usage
1 parent c9f9fdb commit 4b1944e

File tree

7 files changed

+440
-18
lines changed

7 files changed

+440
-18
lines changed

dev/CommandBarFlyout/CommandBarFlyout.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,34 @@
1414
bool CommandBarFlyoutTrace::s_IsDebugOutputEnabled{ false };
1515
bool CommandBarFlyoutTrace::s_IsVerboseDebugOutputEnabled{ false };
1616

17+
// List of AppBarButton dependency properties being listened to for raising the CommandBarFlyoutCommandBar::OnCommandBarElementDependencyPropertyChanged notifications.
18+
// AppBarButton::IsCompact and AppBarButton::LabelPosition having no effect on an AppBarButton's rendering, when used as a secondary command, they are not present in the list.
19+
winrt::DependencyProperty CommandBarFlyout::s_appBarButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount]
20+
{
21+
winrt::AppBarButton::IconProperty(),
22+
winrt::AppBarButton::LabelProperty(),
23+
nullptr
24+
};
25+
26+
// List of AppBarToggleButton dependency properties being listened to for raising the CommandBarFlyoutCommandBar::OnCommandBarElementDependencyPropertyChanged notifications.
27+
// AppBarToggleButton::IsCompact and AppBarToggleButton::LabelPosition having no effect on an AppBarToggleButton's rendering, when used as a secondary command, they are not present in the list.
28+
winrt::DependencyProperty CommandBarFlyout::s_appBarToggleButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount]
29+
{
30+
winrt::AppBarToggleButton::IconProperty(),
31+
winrt::AppBarToggleButton::LabelProperty(),
32+
nullptr
33+
};
34+
1735
CommandBarFlyout::CommandBarFlyout()
1836
{
1937
__RP_Marker_ClassById(RuntimeProfiler::ProfId_CommandBarFlyout);
2038

39+
if (SharedHelpers::IsRS4OrHigher() && s_appBarButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount - 1] == nullptr)
40+
{
41+
s_appBarButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount - 1] = winrt::AppBarButton::KeyboardAcceleratorTextOverrideProperty();
42+
s_appBarToggleButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount - 1] = winrt::AppBarToggleButton::KeyboardAcceleratorTextOverrideProperty();
43+
}
44+
2145
if (winrt::IFlyoutBase6 thisAsFlyoutBase6 = *this)
2246
{
2347
thisAsFlyoutBase6.ShouldConstrainToRootBounds(false);
@@ -65,6 +89,17 @@ CommandBarFlyout::CommandBarFlyout()
6589
auto button = element.try_as<winrt::AppBarButton>();
6690
auto toggleButton = element.try_as<winrt::AppBarToggleButton>();
6791

92+
UnhookCommandBarElementDependencyPropertyChanges(index);
93+
94+
if (button)
95+
{
96+
HookAppBarButtonDependencyPropertyChanges(button, index);
97+
}
98+
else if (toggleButton)
99+
{
100+
HookAppBarToggleButtonDependencyPropertyChanges(toggleButton, index);
101+
}
102+
68103
if (button && !button.Flyout())
69104
{
70105
m_secondaryButtonClickRevokerByIndexMap[index] = button.Click(winrt::auto_revoke, closeFlyoutFunc);
@@ -91,6 +126,15 @@ CommandBarFlyout::CommandBarFlyout()
91126
auto button = element.try_as<winrt::AppBarButton>();
92127
auto toggleButton = element.try_as<winrt::AppBarToggleButton>();
93128

129+
if (button)
130+
{
131+
HookAppBarButtonDependencyPropertyChanges(button, index);
132+
}
133+
else if (toggleButton)
134+
{
135+
HookAppBarToggleButtonDependencyPropertyChanges(toggleButton, index);
136+
}
137+
94138
if (button && !button.Flyout())
95139
{
96140
m_secondaryButtonClickRevokerByIndexMap[index] = button.Click(winrt::auto_revoke, closeFlyoutFunc);
@@ -103,12 +147,15 @@ CommandBarFlyout::CommandBarFlyout()
103147
break;
104148
}
105149
case winrt::CollectionChange::ItemRemoved:
150+
UnhookCommandBarElementDependencyPropertyChanges(index);
151+
106152
SharedHelpers::EraseIfExists(m_secondaryButtonClickRevokerByIndexMap, index);
107153
SharedHelpers::EraseIfExists(m_secondaryToggleButtonCheckedRevokerByIndexMap, index);
108154
SharedHelpers::EraseIfExists(m_secondaryToggleButtonUncheckedRevokerByIndexMap, index);
109155
break;
110156
case winrt::CollectionChange::Reset:
111157
SetSecondaryCommandsToCloseWhenExecuted();
158+
HookAllCommandBarElementDependencyPropertyChanges();
112159
break;
113160
default:
114161
MUX_ASSERT(false);
@@ -250,6 +297,8 @@ CommandBarFlyout::~CommandBarFlyout()
250297
{
251298
m_primaryCommands.VectorChanged(m_primaryCommandsVectorChangedToken);
252299
m_secondaryCommands.VectorChanged(m_secondaryCommandsVectorChangedToken);
300+
301+
UnhookAllCommandBarElementDependencyPropertyChanges();
253302
}
254303

255304
winrt::IObservableVector<winrt::ICommandBarElement> CommandBarFlyout::PrimaryCommands()
@@ -270,6 +319,7 @@ winrt::Control CommandBarFlyout::CreatePresenter()
270319
SharedHelpers::CopyVector(m_secondaryCommands, commandBar->SecondaryCommands());
271320

272321
SetSecondaryCommandsToCloseWhenExecuted();
322+
HookAllCommandBarElementDependencyPropertyChanges();
273323

274324
winrt::FlyoutPresenter presenter;
275325
presenter.Background(nullptr);
@@ -407,3 +457,89 @@ tracker_ref<winrt::FlyoutPresenter> CommandBarFlyout::GetPresenter()
407457
{
408458
return m_presenter;
409459
}
460+
461+
void CommandBarFlyout::HookAppBarButtonDependencyPropertyChanges(winrt::AppBarButton const& appBarButton, int index)
462+
{
463+
const auto commandBarElementDependencyPropertiesCount = SharedHelpers::IsRS4OrHigher() ? s_commandBarElementDependencyPropertiesCount : s_commandBarElementDependencyPropertiesCountRS3;
464+
465+
for (int commandBarElementDependencyPropertyIndex = 0; commandBarElementDependencyPropertyIndex < commandBarElementDependencyPropertiesCount; commandBarElementDependencyPropertyIndex++)
466+
{
467+
m_propertyChangedRevokersByIndexMap[index][commandBarElementDependencyPropertyIndex] =
468+
RegisterPropertyChanged(
469+
appBarButton,
470+
s_appBarButtonDependencyProperties[commandBarElementDependencyPropertyIndex], { this, &CommandBarFlyout::OnCommandBarElementDependencyPropertyChanged });
471+
}
472+
}
473+
474+
void CommandBarFlyout::HookAppBarToggleButtonDependencyPropertyChanges(winrt::AppBarToggleButton const& appBarToggleButton, int index)
475+
{
476+
const auto commandBarElementDependencyPropertiesCount = SharedHelpers::IsRS4OrHigher() ? s_commandBarElementDependencyPropertiesCount : s_commandBarElementDependencyPropertiesCountRS3;
477+
478+
for (int commandBarElementDependencyPropertyIndex = 0; commandBarElementDependencyPropertyIndex < commandBarElementDependencyPropertiesCount; commandBarElementDependencyPropertyIndex++)
479+
{
480+
m_propertyChangedRevokersByIndexMap[index][commandBarElementDependencyPropertyIndex] =
481+
RegisterPropertyChanged(
482+
appBarToggleButton,
483+
s_appBarToggleButtonDependencyProperties[commandBarElementDependencyPropertyIndex], { this, &CommandBarFlyout::OnCommandBarElementDependencyPropertyChanged });
484+
}
485+
}
486+
487+
void CommandBarFlyout::HookAllCommandBarElementDependencyPropertyChanges()
488+
{
489+
UnhookAllCommandBarElementDependencyPropertyChanges();
490+
491+
for (uint32_t i = 0; i < SecondaryCommands().Size(); i++)
492+
{
493+
auto element = SecondaryCommands().GetAt(i);
494+
auto button = element.try_as<winrt::AppBarButton>();
495+
auto toggleButton = element.try_as<winrt::AppBarToggleButton>();
496+
497+
if (button)
498+
{
499+
HookAppBarButtonDependencyPropertyChanges(button, i);
500+
}
501+
else if (toggleButton)
502+
{
503+
HookAppBarToggleButtonDependencyPropertyChanges(toggleButton, i);
504+
}
505+
}
506+
}
507+
508+
void CommandBarFlyout::UnhookCommandBarElementDependencyPropertyChanges(int index, bool eraseRevokers)
509+
{
510+
const auto revokers = m_propertyChangedRevokersByIndexMap.find(index);
511+
if (revokers != m_propertyChangedRevokersByIndexMap.end())
512+
{
513+
const auto commandBarElementDependencyPropertiesCount = SharedHelpers::IsRS4OrHigher() ? s_commandBarElementDependencyPropertiesCount : s_commandBarElementDependencyPropertiesCountRS3;
514+
515+
for (int commandBarElementDependencyPropertyIndex = 0; commandBarElementDependencyPropertyIndex < commandBarElementDependencyPropertiesCount; commandBarElementDependencyPropertyIndex++)
516+
{
517+
m_propertyChangedRevokersByIndexMap[index][commandBarElementDependencyPropertyIndex].revoke();
518+
}
519+
520+
if (eraseRevokers)
521+
{
522+
m_propertyChangedRevokersByIndexMap.erase(revokers);
523+
}
524+
}
525+
}
526+
527+
void CommandBarFlyout::UnhookAllCommandBarElementDependencyPropertyChanges()
528+
{
529+
for (auto const& revokers : m_propertyChangedRevokersByIndexMap)
530+
{
531+
UnhookCommandBarElementDependencyPropertyChanges(revokers.first, false /*eraseRevokers*/);
532+
}
533+
m_propertyChangedRevokersByIndexMap.clear();
534+
}
535+
536+
// Let the potential CommandBarFlyoutCommandBar know of the dependency property change so it can adjust its size.
537+
void CommandBarFlyout::OnCommandBarElementDependencyPropertyChanged(winrt::DependencyObject const& dependencyObject, winrt::DependencyProperty const& dependencyProperty)
538+
{
539+
COMMANDBARFLYOUT_TRACE_VERBOSE(*this, TRACE_MSG_METH, METH_NAME, this);
540+
541+
if (auto commandBar = winrt::get_self<CommandBarFlyoutCommandBar>(m_commandBar.get()))
542+
{
543+
commandBar->OnCommandBarElementDependencyPropertyChanged();
544+
}
545+
}

dev/CommandBarFlyout/CommandBarFlyout.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,19 @@ class CommandBarFlyout :
3030
tracker_ref<winrt::CommandBarFlyoutCommandBar> m_commandBar{ this };
3131

3232
private:
33+
static constexpr int s_commandBarElementDependencyPropertiesCount{ 3 };
34+
static constexpr int s_commandBarElementDependencyPropertiesCountRS3{ 2 };
35+
36+
static winrt::DependencyProperty s_appBarButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount];
37+
static winrt::DependencyProperty s_appBarToggleButtonDependencyProperties[s_commandBarElementDependencyPropertiesCount];
38+
3339
void SetSecondaryCommandsToCloseWhenExecuted();
40+
void HookAppBarButtonDependencyPropertyChanges(winrt::AppBarButton const& appBarButton, int index);
41+
void HookAppBarToggleButtonDependencyPropertyChanges(winrt::AppBarToggleButton const& appBarToggleButton, int index);
42+
void HookAllCommandBarElementDependencyPropertyChanges();
43+
void UnhookCommandBarElementDependencyPropertyChanges(int index, bool eraseRevokers = true);
44+
void UnhookAllCommandBarElementDependencyPropertyChanges();
45+
void OnCommandBarElementDependencyPropertyChanged(winrt::DependencyObject const& dependencyObject, winrt::DependencyProperty const& dependencyProperty);
3446

3547
bool m_alwaysExpanded;
3648

@@ -50,6 +62,7 @@ class CommandBarFlyout :
5062
std::map<int, winrt::ButtonBase::Click_revoker> m_secondaryButtonClickRevokerByIndexMap;
5163
std::map<int, winrt::ToggleButton::Checked_revoker> m_secondaryToggleButtonCheckedRevokerByIndexMap;
5264
std::map<int, winrt::ToggleButton::Unchecked_revoker> m_secondaryToggleButtonUncheckedRevokerByIndexMap;
65+
std::map<int, PropertyChanged_revoker[s_commandBarElementDependencyPropertiesCount]> m_propertyChangedRevokersByIndexMap;
5366

5467
tracker_ref<winrt::FlyoutPresenter> m_presenter{ this };
5568

0 commit comments

Comments
 (0)