Skip to content

Commit 7198c4e

Browse files
author
reunion-maestro-bot
committed
Syncing content from committish release/1.4.2
1 parent 702b954 commit 7198c4e

File tree

22 files changed

+583
-52
lines changed

22 files changed

+583
-52
lines changed

controls/dev/CommandBarFlyout/CommandBarFlyout.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
#include "CommandBarFlyoutCommandBar.h"
88
#include "Vector.h"
99
#include "RuntimeProfiler.h"
10+
#include "velocity.h"
11+
#include <FrameworkUdk/Containment.h>
1012

1113
#include "CommandBarFlyout.properties.cpp"
1214

15+
// Bug 46607274: [1.4 servicing] Microsoft.UI.Xaml.Controls.dll!CommandBarFlyoutCommandBar::EnsureLocalizedControlTypes impacting context menu performance
16+
#define WINAPPSDK_CHANGEID_46607274 46607274
17+
1318
// Change to 'true' to turn on debugging outputs in Output window
1419
bool CommandBarFlyoutTrace::s_IsDebugOutputEnabled{ false };
1520
bool CommandBarFlyoutTrace::s_IsVerboseDebugOutputEnabled{ false };
@@ -307,6 +312,20 @@ winrt::Control CommandBarFlyout::CreatePresenter()
307312
{
308313
auto commandBar = winrt::make_self<CommandBarFlyoutCommandBar>();
309314

315+
if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_46607274>())
316+
{
317+
// Localized string resource lookup is more expensive on MRTCore. Do the lookup ahead of time and reuse it for all
318+
// the CommandBarFlyoutCommandBar::EnsureLocalizedControlTypes calls in response to PrimaryCommands/SecondCommands
319+
// changed events.
320+
commandBar->CacheLocalizedStringResources();
321+
}
322+
auto const scopeGuard = WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_46607274>()
323+
? wil::scope_exit(std::function<void()>([commandBar]()
324+
{
325+
commandBar->ClearLocalizedStringResourceCache();
326+
}))
327+
: wil::scope_exit(std::function<void()>([](){}));
328+
310329
SharedHelpers::CopyVector(m_primaryCommands, commandBar->PrimaryCommands());
311330
SharedHelpers::CopyVector(m_secondaryCommands, commandBar->SecondaryCommands());
312331

controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -895,17 +895,47 @@ void CommandBarFlyoutCommandBar::EnsureLocalizedControlTypes()
895895
}
896896
}
897897

898+
void CommandBarFlyoutCommandBar::CacheLocalizedStringResources()
899+
{
900+
m_localizedCommandBarFlyoutAppBarButtonControlType = ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarButtonLocalizedControlType);
901+
m_localizedCommandBarFlyoutAppBarToggleButtonControlType = ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarToggleButtonLocalizedControlType);
902+
m_areLocalizedStringResourcesCached = true;
903+
}
904+
905+
void CommandBarFlyoutCommandBar::ClearLocalizedStringResourceCache()
906+
{
907+
m_areLocalizedStringResourcesCached = false;
908+
m_localizedCommandBarFlyoutAppBarButtonControlType.clear();
909+
m_localizedCommandBarFlyoutAppBarToggleButtonControlType.clear();
910+
}
911+
898912
void CommandBarFlyoutCommandBar::SetKnownCommandLocalizedControlTypes(winrt::ICommandBarElement const& command)
899913
{
900914
COMMANDBARFLYOUT_TRACE_VERBOSE(*this, TRACE_MSG_METH, METH_NAME, this);
901915

902916
if (auto const& appBarButton = command.try_as<winrt::AppBarButton>())
903917
{
904-
winrt::AutomationProperties::SetLocalizedControlType(appBarButton, ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarButtonLocalizedControlType));
918+
if (m_areLocalizedStringResourcesCached)
919+
{
920+
MUX_ASSERT(!m_localizedCommandBarFlyoutAppBarButtonControlType.empty());
921+
winrt::AutomationProperties::SetLocalizedControlType(appBarButton, m_localizedCommandBarFlyoutAppBarButtonControlType);
922+
}
923+
else
924+
{
925+
winrt::AutomationProperties::SetLocalizedControlType(appBarButton, ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarButtonLocalizedControlType));
926+
}
905927
}
906928
else if (auto const& appBarToggleButton = command.try_as<winrt::AppBarToggleButton>())
907929
{
908-
winrt::AutomationProperties::SetLocalizedControlType(appBarToggleButton, ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarToggleButtonLocalizedControlType));
930+
if (m_areLocalizedStringResourcesCached)
931+
{
932+
MUX_ASSERT(!m_localizedCommandBarFlyoutAppBarToggleButtonControlType.empty());
933+
winrt::AutomationProperties::SetLocalizedControlType(appBarToggleButton, m_localizedCommandBarFlyoutAppBarToggleButtonControlType);
934+
}
935+
else
936+
{
937+
winrt::AutomationProperties::SetLocalizedControlType(appBarToggleButton, ResourceAccessor::GetLocalizedStringResource(SR_CommandBarFlyoutAppBarToggleButtonLocalizedControlType));
938+
}
909939
}
910940
}
911941

controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class CommandBarFlyoutCommandBar :
4646

4747
void OnPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args);
4848

49+
void CacheLocalizedStringResources();
50+
void ClearLocalizedStringResourceCache();
51+
4952
private:
5053
void AttachEventHandlers();
5154
void DetachEventHandlers();
@@ -127,4 +130,11 @@ class CommandBarFlyoutCommandBar :
127130
// The one built into Popup is too high up in the Visual tree to be animated by a custom animation.
128131
winrt::ContentExternalBackdropLink m_backdropLink{ nullptr };
129132
winrt::ContentExternalBackdropLink m_overflowPopupBackdropLink{ nullptr };
133+
134+
// Localized string caches. Looking these up from MRTCore is expensive, so we don't want to put the lookups in a
135+
// loop. Instead, look them up once, cache them, use the cached values, then clear the cache. The values in these
136+
// caches are only valid after CacheLocalizedStringResources and before ClearLocalizedStringResourceCache.
137+
bool m_areLocalizedStringResourcesCached{ false };
138+
winrt::hstring m_localizedCommandBarFlyoutAppBarButtonControlType{};
139+
winrt::hstring m_localizedCommandBarFlyoutAppBarToggleButtonControlType{};
130140
};

controls/dev/NavigationView/NavigationView.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
#include "NavigationViewItemExpandingEventArgs.h"
2929
#include "NavigationViewItemCollapsedEventArgs.h"
3030
#include "InspectingDataSource.h"
31+
#include "velocity.h"
32+
#include <FrameworkUdk/Containment.h>
33+
34+
// Bug 46697123: [1.4 servicing] - Tab/Shift+Tab Navigation is very very confusing with List Views
35+
#define WINAPPSDK_CHANGEID_46697123 46697123
3136

3237
// General items
3338
static constexpr auto c_togglePaneButtonName = L"TogglePaneButton"sv;
@@ -2885,6 +2890,53 @@ void NavigationView::OnRepeaterGettingFocus(const winrt::IInspectable& sender, c
28852890
args.Handled(true);
28862891
}
28872892
}
2893+
else if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_46697123>() &&
2894+
!isFocusOutsideCurrentRootRepeater)
2895+
{
2896+
// Tab or Shift-Tab from within the same root repeater should move focus outside it
2897+
auto navigationViewItemBase = args.NewFocusedElement().try_as<winrt::NavigationViewItemBase>();
2898+
if (navigationViewItemBase &&
2899+
(args.Direction() == winrt::FocusNavigationDirection::Previous ||
2900+
args.Direction() == winrt::FocusNavigationDirection::Next))
2901+
{
2902+
// We need to find the next tab stop outside the root repeater.
2903+
// winrt::FocusManager::FindNextElement will at first give us NavigationViewItems
2904+
// within the root repeater. This is why we need to set IsTabStop = false for
2905+
// the found the elements within the root repeater so that FindNextElement eventually
2906+
// finds an element outside the root repeater.
2907+
std::vector<winrt::NavigationViewItemBase> containersWithTabFocusOff{};
2908+
2909+
const winrt::FindNextElementOptions options;
2910+
options.SearchRoot(this->XamlRoot().Content());
2911+
2912+
auto nextElement = winrt::FocusManager::FindNextElement(args.Direction(), options);
2913+
while (auto nvib = nextElement.try_as<winrt::NavigationViewItemBase>())
2914+
{
2915+
// Verify the item is in the root repeater we are trying to leave
2916+
if (newRootItemsRepeater == GetParentRootItemsRepeaterForContainer(nvib))
2917+
{
2918+
nvib.IsTabStop(false);
2919+
// Keep track of the container so that we can re-enable tab stop once focus leaves
2920+
containersWithTabFocusOff.push_back(nvib);
2921+
nextElement = winrt::FocusManager::FindNextElement(args.Direction(), options);
2922+
}
2923+
else
2924+
{
2925+
break;
2926+
}
2927+
}
2928+
2929+
for (auto nvib : containersWithTabFocusOff)
2930+
{
2931+
nvib.IsTabStop(true);
2932+
}
2933+
2934+
if (args.TrySetNewFocusedElement(nextElement))
2935+
{
2936+
args.Handled(true);
2937+
}
2938+
}
2939+
}
28882940
}
28892941
}
28902942
}

controls/dev/NavigationView/NavigationView.xaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,10 @@
361361
<controls:ItemsRepeaterScrollHost HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
362362
<ScrollViewer x:Name="MenuItemsScrollViewer" TabNavigation="Local" VerticalScrollBarVisibility="Auto">
363363
<!-- Left nav ItemsRepeater -->
364-
<controls:ItemsRepeater x:Name="MenuItemsHost" AutomationProperties.Name="{TemplateBinding AutomationProperties.Name}" AutomationProperties.AccessibilityView="Content">
364+
<controls:ItemsRepeater
365+
x:Name="MenuItemsHost"
366+
AutomationProperties.Name="{TemplateBinding AutomationProperties.Name}"
367+
AutomationProperties.AccessibilityView="Content">
365368
<controls:ItemsRepeater.Layout>
366369
<controls:StackLayout />
367370
</controls:ItemsRepeater.Layout>

controls/dev/NavigationView/NavigationView_InteractionTests/FocusBehaviorTests.cs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,160 @@ string GetSelectedItem()
291291
}
292292
}
293293

294+
[TestMethod]
295+
public void TabNavigationHierarchicalTest()
296+
{
297+
var testScenarios = RegressionTestScenario.BuildLeftNavRegressionTestScenarios();
298+
foreach (var testScenario in testScenarios)
299+
{
300+
using (var setup = new TestSetupHelper(new[] { "NavigationView Tests", "HierarchicalNavigationView Markup Test" }))
301+
{
302+
Button togglePaneButton = new Button(FindElement.ById("TogglePaneButton"));
303+
Button getSelectItemButton = new Button(FindElement.ByName("GetSelectedItemLabelButton"));
304+
305+
VerifyTabNavigationWithoutMenuItemSelected();
306+
VerifyTabNavigationWithMenuItemSelected();
307+
VerifyTabNavigationWithSettingsItemVisible();
308+
309+
void VerifyTabNavigationWithoutMenuItemSelected()
310+
{
311+
Log.Comment("Verify Tab navigation without a selected menu item");
312+
313+
getSelectItemButton.Invoke();
314+
Wait.ForIdle();
315+
316+
Verify.AreEqual("No Item Selected", GetSelectedItem());
317+
318+
UIObject menuItem1 = FindElement.ByName("Menu Item 1");
319+
UIObject menuItem29 = FindElement.ByName("Menu Item 29 (Selectable)");
320+
321+
// Set focus on the pane's toggle button.
322+
togglePaneButton.SetFocus();
323+
Wait.ForIdle();
324+
325+
Log.Comment("Verify that pressing tab while TogglePaneButton has focus moves to Menu Item 1.");
326+
KeyboardHelper.PressKey(Key.Tab);
327+
Wait.ForIdle();
328+
Verify.IsTrue(menuItem1.HasKeyboardFocus);
329+
330+
Log.Comment("Verify that pressing tab while Menu Item 1 has focus moves to 'Get Selected Item Label' Button item");
331+
KeyboardHelper.PressKey(Key.Tab);
332+
Wait.ForIdle();
333+
Verify.IsTrue(getSelectItemButton.HasKeyboardFocus);
334+
335+
Log.Comment("Verify that pressing SHIFT+tab will move focus to Menu Item 29");
336+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
337+
Wait.ForIdle();
338+
Verify.IsTrue(menuItem29.HasKeyboardFocus);
339+
340+
Log.Comment("Verify that pressing SHIFT+tab will move focus to the TogglePaneButton");
341+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
342+
Wait.ForIdle();
343+
Verify.IsTrue(togglePaneButton.HasKeyboardFocus);
344+
}
345+
346+
void VerifyTabNavigationWithMenuItemSelected()
347+
{
348+
Log.Comment("Verify Tab navigation with a selected menu item");
349+
350+
Log.Comment("Expand Menu Item 15.");
351+
UIObject menuItem15 = FindElement.ByName("Menu Item 15");
352+
InputHelper.LeftClick(menuItem15);
353+
354+
Log.Comment("Select Menu Item 16.");
355+
UIObject menuItem16 = FindElement.ByName("Menu Item 16");
356+
InputHelper.LeftClick(menuItem16);
357+
358+
getSelectItemButton.Invoke();
359+
Wait.ForIdle();
360+
361+
Verify.AreEqual("Menu Item 16", GetSelectedItem());
362+
363+
// Set focus on the pane's toggle button.
364+
togglePaneButton.SetFocus();
365+
Wait.ForIdle();
366+
367+
Log.Comment("Verify that pressing tab while TogglePaneButton has focus moves to Menu Item 16.");
368+
KeyboardHelper.PressKey(Key.Tab);
369+
Wait.ForIdle();
370+
Verify.IsTrue(menuItem16.HasKeyboardFocus);
371+
372+
Log.Comment("Verify that pressing tab while Menu Item 16 has focus moves to 'Get Selected Item Label' Button item");
373+
KeyboardHelper.PressKey(Key.Tab);
374+
Wait.ForIdle();
375+
Verify.IsTrue(getSelectItemButton.HasKeyboardFocus);
376+
377+
Log.Comment("Verify that pressing SHIFT+tab will move focus to Menu Item 16");
378+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
379+
Wait.ForIdle();
380+
Verify.IsTrue(menuItem16.HasKeyboardFocus);
381+
382+
Log.Comment("Verify that pressing SHIFT+tab will move focus to the TogglePaneButton");
383+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
384+
Wait.ForIdle();
385+
Verify.IsTrue(togglePaneButton.HasKeyboardFocus);
386+
387+
Log.Comment("Verify that pressing tab from parent of item will move focus to 'Get Selected Item Label' Button item");
388+
KeyboardHelper.PressKey(Key.Down, ModifierKey.None, 3);
389+
Wait.ForIdle();
390+
Verify.IsTrue(menuItem15.HasKeyboardFocus);
391+
392+
KeyboardHelper.PressKey(Key.Tab);
393+
Wait.ForIdle();
394+
Verify.IsTrue(getSelectItemButton.HasKeyboardFocus);
395+
}
396+
397+
void VerifyTabNavigationWithSettingsItemVisible()
398+
{
399+
Log.Comment("Verify tab navigation with settings item visible.");
400+
401+
Log.Comment("Check IsSettingsVisible");
402+
CheckBox checkBoxIsSettingsVisible = FindElement.ByName<CheckBox>("Settings Item Visibility");
403+
checkBoxIsSettingsVisible.Check();
404+
Wait.ForIdle();
405+
406+
UIObject settingsItem = FindElement.ByName("Settings");
407+
408+
getSelectItemButton.Invoke();
409+
Wait.ForIdle();
410+
411+
Verify.AreEqual("Menu Item 16", GetSelectedItem());
412+
413+
UIObject menuItem16 = FindElement.ByName("Menu Item 16");
414+
415+
// Set focus on the pane's toggle button.
416+
togglePaneButton.SetFocus();
417+
Wait.ForIdle();
418+
419+
Log.Comment("Verify that pressing tab while TogglePaneButton has focus moves to Menu Item 16.");
420+
KeyboardHelper.PressKey(Key.Tab);
421+
Wait.ForIdle();
422+
Verify.IsTrue(menuItem16.HasKeyboardFocus);
423+
424+
Log.Comment("Verify that pressing tab while Menu Item 16 has focus moves to Settings item");
425+
KeyboardHelper.PressKey(Key.Tab);
426+
Wait.ForIdle();
427+
Verify.IsTrue(settingsItem.HasKeyboardFocus);
428+
429+
Log.Comment("Verify that pressing SHIFT+tab will move focus to Menu Item 16");
430+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
431+
Wait.ForIdle();
432+
Verify.IsTrue(menuItem16.HasKeyboardFocus);
433+
434+
Log.Comment("Verify that pressing SHIFT+tab will move focus to the TogglePaneButton");
435+
KeyboardHelper.PressKey(Key.Tab, ModifierKey.Shift, 1);
436+
Wait.ForIdle();
437+
Verify.IsTrue(togglePaneButton.HasKeyboardFocus);
438+
}
439+
}
440+
441+
string GetSelectedItem()
442+
{
443+
return new TextBlock(FindElement.ByName("SelectedItemLabel")).DocumentText;
444+
}
445+
}
446+
}
447+
294448
[TestMethod]
295449
public void CanDoSelectionChangedOfItemTemplate()
296450
{

controls/dev/NavigationView/TestUI/Hierarchical/HierarchicalNavigationViewDataBinding.xaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@
1717
</Page.Resources>
1818

1919
<Grid>
20-
<muxcontrols:NavigationView x:Name="navview" MenuItemsSource="{x:Bind categories, Mode=OneWay}" MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
21-
ItemInvoked="{x:Bind ClickedItem}" Expanding="OnItemExpanding" Collapsed="OnItemCollapsed" SelectionChanged="OnSelectionChanged" PaneDisplayMode="Left">
20+
<muxcontrols:NavigationView
21+
x:Name="navview"
22+
MenuItemsSource="{x:Bind categories, Mode=OneWay}"
23+
MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
24+
ItemInvoked="{x:Bind ClickedItem}"
25+
Expanding="OnItemExpanding"
26+
Collapsed="OnItemCollapsed"
27+
SelectionChanged="OnSelectionChanged"
28+
PaneDisplayMode="Left">
2229
<StackPanel Margin="10,10,0,0" Spacing="5">
2330
<TextBlock x:Name="SelectedItemLabel" AutomationProperties.Name="SelectedItemLabel" Text="uninitialized"/>
2431
<Button Content="Get Selected Item Label" x:Name="GetSelectedItemLabelButton" AutomationProperties.Name="GetSelectedItemLabelButton" Click="PrintSelectedItem"/>

controls/dev/NavigationView/TestUI/Hierarchical/HierarchicalNavigationViewMarkup.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<TextBlock x:Name="IsChildSelectedLabel" AutomationProperties.Name="IsChildSelectedLabel" Text="uninitialized"/>
9090
<Button AutomationProperties.Name="PrintTopLevelIsChildSelectedItemsButton" Content="Print TopLevel IsChildSelected Items" Click="PrintTopLevelIsChildSelectedItems"/>
9191
<Button Content="Select Second Item" Click="SelectSecondItem"/>
92+
<CheckBox Content="Settings Item Visibility" x:Name="CheckBoxIsSettingsVisible" Checked="OnCheckedIsSettingsVisible" Unchecked="OnUncheckedIsSettingsVisible"/>
9293
<!-- Components that get updated when item is expanded or collapsed -->
9394
<StackPanel x:Name="ExpandCollapseRelatedComponents" Margin="0,10,0,10">
9495
<Grid x:Name="ExpandingEventStates">

0 commit comments

Comments
 (0)