Skip to content

Commit d3dd80d

Browse files
committed
- Changes to add accessibility context menus to TabView.
- Other fixes to fix an EngineExecutionException owing to a disposed WinProc.
1 parent 71e37d3 commit d3dd80d

8 files changed

+219
-158
lines changed

WinUIGallery/ControlPages/TabViewPage.xaml.cs

+1-78
Original file line numberDiff line numberDiff line change
@@ -262,84 +262,7 @@ private void TabViewWindowingButton_Click(object sender, Microsoft.UI.Xaml.Route
262262

263263
private void TabViewContextMenu_Opening(object sender, object e)
264264
{
265-
MenuFlyout contextMenu = (MenuFlyout)sender;
266-
contextMenu.Items.Clear();
267-
268-
var item = (TabViewItem)contextMenu.Target;
269-
ListView tabViewListView = null;
270-
TabView tabView = null;
271-
272-
DependencyObject current = item;
273-
274-
while (current != null)
275-
{
276-
DependencyObject parent = VisualTreeHelper.GetParent(current);
277-
278-
if (parent is ListView parentTabViewListView)
279-
{
280-
tabViewListView = parentTabViewListView;
281-
}
282-
else if (parent is TabView parentTabView)
283-
{
284-
tabView = parentTabView;
285-
}
286-
287-
if (tabViewListView != null && tabView != null)
288-
{
289-
break;
290-
}
291-
292-
current = parent;
293-
}
294-
295-
if (tabViewListView == null || tabView == null)
296-
{
297-
return;
298-
}
299-
300-
int index = tabViewListView.IndexFromContainer(item);
301-
302-
if (index > 0)
303-
{
304-
MenuFlyoutItem moveLeftItem = new() { Text = "Move tab left" };
305-
moveLeftItem.Click += (s, args) =>
306-
{
307-
if (tabView.TabItemsSource is IList itemsSourceList)
308-
{
309-
var item = itemsSourceList[index];
310-
itemsSourceList.RemoveAt(index);
311-
itemsSourceList.Insert(index - 1, item);
312-
}
313-
else
314-
{
315-
var item = tabView.TabItems[index];
316-
tabView.TabItems.RemoveAt(index);
317-
tabView.TabItems.Insert(index - 1, item);
318-
}
319-
};
320-
contextMenu.Items.Add(moveLeftItem);
321-
}
322-
323-
if (index < tabViewListView.Items.Count - 1)
324-
{
325-
MenuFlyoutItem moveRightItem = new() { Text = "Move tab right" };
326-
moveRightItem.Click += (s, args) =>
327-
{
328-
if (tabView.TabItemsSource is IList itemsSourceList)
329-
{
330-
var item = itemsSourceList[index];
331-
itemsSourceList.RemoveAt(index);
332-
itemsSourceList.Insert(index + 1, item);
333-
}
334-
else
335-
{
336-
var item = tabView.TabItems[index];
337-
tabView.TabItems.RemoveAt(index);
338-
tabView.TabItems.Insert(index + 1, item);
339-
}
340-
};
341-
contextMenu.Items.Add(moveRightItem);
342-
}
265+
TabViewHelper.PopulateTabViewContextMenu((MenuFlyout)sender);
343266
}
344267
}
345268
}

WinUIGallery/Helper/TabViewHelper.cs

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Microsoft.UI.Xaml;
8+
using Microsoft.UI.Xaml.Controls;
9+
using Microsoft.UI.Xaml.Media;
10+
11+
namespace WinUIGallery.Helper
12+
{
13+
public static class TabViewHelper
14+
{
15+
public static void PopulateTabViewContextMenu(MenuFlyout contextMenu)
16+
{
17+
contextMenu.Items.Clear();
18+
19+
var item = (TabViewItem)contextMenu.Target;
20+
ListView tabViewListView = null;
21+
TabView tabView = null;
22+
23+
DependencyObject current = item;
24+
25+
while (current != null)
26+
{
27+
DependencyObject parent = VisualTreeHelper.GetParent(current);
28+
29+
if (parent is ListView parentTabViewListView)
30+
{
31+
tabViewListView = parentTabViewListView;
32+
}
33+
else if (parent is TabView parentTabView)
34+
{
35+
tabView = parentTabView;
36+
}
37+
38+
if (tabViewListView != null && tabView != null)
39+
{
40+
break;
41+
}
42+
43+
current = parent;
44+
}
45+
46+
if (tabViewListView == null || tabView == null)
47+
{
48+
return;
49+
}
50+
51+
// First, if there are tabs to the left or to the right of the tab on which this context menu is opening,
52+
// then we'll include menu items to move this tab to the left or to the right.
53+
//
54+
// There are two possible cases for tab views: either they have explicitly set tab items, or they have a data item source set.
55+
// To move a tab left or right with explicitly set tab items, we'll remove and replace the tab item itself.
56+
// To move a tab left or right with a data item source set, we'll instead remove and replace the data item in the source list.
57+
int index = tabViewListView.IndexFromContainer(item);
58+
59+
if (index > 0)
60+
{
61+
MenuFlyoutItem moveLeftItem = new() { Text = "Move tab left" };
62+
moveLeftItem.Click += (s, args) =>
63+
{
64+
if (tabView.TabItemsSource is IList itemsSourceList)
65+
{
66+
var item = itemsSourceList[index];
67+
itemsSourceList.RemoveAt(index);
68+
itemsSourceList.Insert(index - 1, item);
69+
}
70+
else
71+
{
72+
var item = tabView.TabItems[index];
73+
tabView.TabItems.RemoveAt(index);
74+
tabView.TabItems.Insert(index - 1, item);
75+
}
76+
};
77+
contextMenu.Items.Add(moveLeftItem);
78+
}
79+
80+
if (index < tabViewListView.Items.Count - 1)
81+
{
82+
MenuFlyoutItem moveRightItem = new() { Text = "Move tab right" };
83+
moveRightItem.Click += (s, args) =>
84+
{
85+
if (tabView.TabItemsSource is IList itemsSourceList)
86+
{
87+
var item = itemsSourceList[index];
88+
itemsSourceList.RemoveAt(index);
89+
itemsSourceList.Insert(index + 1, item);
90+
}
91+
else
92+
{
93+
var item = tabView.TabItems[index];
94+
tabView.TabItems.RemoveAt(index);
95+
tabView.TabItems.Insert(index + 1, item);
96+
}
97+
};
98+
contextMenu.Items.Add(moveRightItem);
99+
}
100+
}
101+
}
102+
}

WinUIGallery/Helper/UIHelper.cs

+17
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,22 @@ static public void AnnounceActionForAccessibility(UIElement ue, string annouceme
6363
peer.RaiseNotificationEvent(AutomationNotificationKind.ActionCompleted,
6464
AutomationNotificationProcessing.ImportantMostRecent, annoucement, activityID);
6565
}
66+
67+
public static T GetParent<T>(DependencyObject child) where T : DependencyObject
68+
{
69+
DependencyObject current = child;
70+
71+
while (current != null)
72+
{
73+
if (current is T parent)
74+
{
75+
return parent;
76+
}
77+
78+
current = VisualTreeHelper.GetParent(current);
79+
}
80+
81+
return null;
82+
}
6683
}
6784
}

WinUIGallery/Helper/Win32WindowHelper.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ namespace WinUIGallery.Helper
77
{
88
internal class Win32WindowHelper
99
{
10-
private static WinProc newWndProc = null;
11-
private static nint oldWndProc = nint.Zero;
10+
private WinProc newWndProc = null;
11+
private nint oldWndProc = nint.Zero;
1212

1313
private POINT? minWindowSize = null;
1414
private POINT? maxWindowSize = null;

WinUIGallery/TabViewPages/MyTabContentControl.xaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<TextBlock Text="{Binding}" Style="{ThemeResource TitleTextBlockStyle}" />
1414
<TextBlock Text="Drag the Tab outside of the window to spawn a new window." Style="{ThemeResource SubtitleTextBlockStyle}" />
1515
<TextBlock Text="Notice that the state of the Tab is maintained in the new window. For example, if you toggle the ToggleSwitch ON, it will remain ON in the new window." Style="{ThemeResource BodyTextBlockStyle}" />
16-
<ToggleSwitch x:Name="ControlToggle" Header="Turn on ProgressRing" Margin="0,8" />
17-
<ProgressRing IsActive="{x:Bind ControlToggle.IsOn, Mode=OneWay}" HorizontalAlignment="Left" />
16+
<ToggleSwitch x:Name="ControlToggle" Header="Turn on ProgressRing" Margin="0,8" IsOn="{x:Bind IsInProgress, Mode=TwoWay}" />
17+
<ProgressRing IsActive="{x:Bind IsInProgress, Mode=OneWay}" HorizontalAlignment="Left" />
1818
</StackPanel>
1919
</UserControl>

WinUIGallery/TabViewPages/MyTabContentControl.xaml.cs

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ namespace WinUIGallery.TabViewPages
77
{
88
public sealed partial class MyTabContentControl : UserControl
99
{
10+
public bool IsInProgress
11+
{
12+
get { return (bool)GetValue(IsInProgressProperty); }
13+
set { SetValue(IsInProgressProperty, value); }
14+
}
15+
16+
public static readonly DependencyProperty IsInProgressProperty = DependencyProperty.Register("IsInProgress", typeof(bool), typeof(MyTabContentControl), new PropertyMetadata(false));
17+
1018
public MyTabContentControl()
1119
{
1220
this.InitializeComponent();

WinUIGallery/TabViewPages/TabViewWindowingSamplePage.xaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<TabViewItem.IconSource>
3535
<SymbolIconSource Symbol="Placeholder" />
3636
</TabViewItem.IconSource>
37-
<local:MyTabContentControl DataContext="{x:Bind Content}" />
37+
<local:MyTabContentControl DataContext="{x:Bind Content}" IsInProgress="{x:Bind IsInProgress, Mode=TwoWay}" />
3838
</TabViewItem>
3939
</DataTemplate>
4040
</TabView.TabItemTemplate>

0 commit comments

Comments
 (0)