Skip to content

Commit 86f2752

Browse files
committed
Add BreadcrumbBar
1 parent d8df238 commit 86f2752

File tree

9 files changed

+1003
-0
lines changed

9 files changed

+1003
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace DevWinUI;
5+
6+
public partial class BreadcrumbBar : Control
7+
{
8+
public static readonly DependencyProperty RootItemProperty =
9+
DependencyProperty.Register(
10+
nameof(RootItem),
11+
typeof(FrameworkElement),
12+
typeof(BreadcrumbBar),
13+
new PropertyMetadata(null));
14+
15+
public FrameworkElement? RootItem
16+
{
17+
get => (FrameworkElement?)GetValue(RootItemProperty);
18+
set => SetValue(RootItemProperty, value);
19+
}
20+
21+
22+
public static readonly DependencyProperty ItemsSourceProperty =
23+
DependencyProperty.Register(
24+
nameof(ItemsSource),
25+
typeof(object),
26+
typeof(BreadcrumbBar),
27+
new PropertyMetadata(null));
28+
29+
public object? ItemsSource
30+
{
31+
get => GetValue(ItemsSourceProperty);
32+
set => SetValue(ItemsSourceProperty, value);
33+
}
34+
35+
36+
public static readonly DependencyProperty ItemTemplateProperty =
37+
DependencyProperty.Register(
38+
nameof(ItemTemplate),
39+
typeof(object),
40+
typeof(BreadcrumbBar),
41+
new PropertyMetadata(null));
42+
43+
public object? ItemTemplate
44+
{
45+
get => GetValue(ItemTemplateProperty);
46+
set => SetValue(ItemTemplateProperty, value);
47+
}
48+
49+
50+
public static readonly DependencyProperty EllipsisButtonToolTipProperty =
51+
DependencyProperty.Register(
52+
nameof(EllipsisButtonToolTip),
53+
typeof(string),
54+
typeof(BreadcrumbBar),
55+
new PropertyMetadata(null));
56+
57+
public string? EllipsisButtonToolTip
58+
{
59+
get => (string?)GetValue(EllipsisButtonToolTipProperty);
60+
set => SetValue(EllipsisButtonToolTipProperty, value);
61+
}
62+
63+
64+
public static readonly DependencyProperty RootItemToolTipProperty =
65+
DependencyProperty.Register(
66+
nameof(RootItemToolTip),
67+
typeof(string),
68+
typeof(BreadcrumbBar),
69+
new PropertyMetadata(null));
70+
71+
public string? RootItemToolTip
72+
{
73+
get => (string?)GetValue(RootItemToolTipProperty);
74+
set => SetValue(RootItemToolTipProperty, value);
75+
}
76+
77+
78+
public static readonly DependencyProperty RootItemChevronToolTipProperty =
79+
DependencyProperty.Register(
80+
nameof(RootItemChevronToolTip),
81+
typeof(string),
82+
typeof(BreadcrumbBar),
83+
new PropertyMetadata(null));
84+
85+
public string? RootItemChevronToolTip
86+
{
87+
get => (string?)GetValue(RootItemChevronToolTipProperty);
88+
set => SetValue(RootItemChevronToolTipProperty, value);
89+
}
90+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.UI.Xaml.Automation;
5+
using Microsoft.UI.Xaml.Input;
6+
7+
namespace DevWinUI;
8+
9+
public partial class BreadcrumbBar : Control
10+
{
11+
// Constants
12+
13+
private const string TemplatePartName_RootBreadcrumbBarItem = "PART_RootBreadcrumbBarItem";
14+
private const string TemplatePartName_EllipsisBreadcrumbBarItem = "PART_EllipsisBreadcrumbBarItem";
15+
private const string TemplatePartName_MainItemsRepeater = "PART_MainItemsRepeater";
16+
17+
// Fields
18+
19+
private readonly BreadcrumbBarLayout _itemsRepeaterLayout;
20+
21+
private BreadcrumbBarItem? _rootBreadcrumbBarItem;
22+
private BreadcrumbBarItem? _ellipsisBreadcrumbBarItem;
23+
private BreadcrumbBarItem? _lastBreadcrumbBarItem;
24+
private ItemsRepeater? _itemsRepeater;
25+
26+
private bool _isEllipsisRendered;
27+
28+
// Properties
29+
30+
public int IndexAfterEllipsis
31+
=> _itemsRepeaterLayout.IndexAfterEllipsis;
32+
33+
// Events
34+
35+
public event TypedEventHandler<BreadcrumbBar, BreadcrumbBarItemClickedEventArgs>? ItemClicked;
36+
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutOpening;
37+
public event EventHandler<BreadcrumbBarItemDropDownFlyoutEventArgs>? ItemDropDownFlyoutClosed;
38+
39+
// Constructor
40+
41+
public BreadcrumbBar()
42+
{
43+
DefaultStyleKey = typeof(BreadcrumbBar);
44+
45+
_itemsRepeaterLayout = new(this);
46+
}
47+
48+
// Methods
49+
50+
protected override void OnApplyTemplate()
51+
{
52+
base.OnApplyTemplate();
53+
54+
_rootBreadcrumbBarItem = GetTemplateChild(TemplatePartName_RootBreadcrumbBarItem) as BreadcrumbBarItem
55+
?? throw new MissingFieldException($"Could not find {TemplatePartName_RootBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style.");
56+
_ellipsisBreadcrumbBarItem = GetTemplateChild(TemplatePartName_EllipsisBreadcrumbBarItem) as BreadcrumbBarItem
57+
?? throw new MissingFieldException($"Could not find {TemplatePartName_EllipsisBreadcrumbBarItem} in the given {nameof(BreadcrumbBar)}'s style.");
58+
_itemsRepeater = GetTemplateChild(TemplatePartName_MainItemsRepeater) as ItemsRepeater
59+
?? throw new MissingFieldException($"Could not find {TemplatePartName_MainItemsRepeater} in the given {nameof(BreadcrumbBar)}'s style.");
60+
61+
_rootBreadcrumbBarItem.SetOwner(this);
62+
_ellipsisBreadcrumbBarItem.SetOwner(this);
63+
_itemsRepeater.Layout = _itemsRepeaterLayout;
64+
65+
_itemsRepeater.ElementPrepared += ItemsRepeater_ElementPrepared;
66+
_itemsRepeater.ElementClearing += ItemsRepeater_ElementClearing;
67+
_itemsRepeater.ItemsSourceView.CollectionChanged += ItemsSourceView_CollectionChanged;
68+
}
69+
70+
internal protected virtual void RaiseItemClickedEvent(BreadcrumbBarItem item, PointerRoutedEventArgs? pointerRoutedEventArgs = null)
71+
{
72+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
73+
var eventArgs = new BreadcrumbBarItemClickedEventArgs(item, index, item == _rootBreadcrumbBarItem, pointerRoutedEventArgs);
74+
ItemClicked?.Invoke(this, eventArgs);
75+
}
76+
77+
internal protected virtual void RaiseItemDropDownFlyoutOpening(BreadcrumbBarItem item, MenuFlyout flyout)
78+
{
79+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
80+
ItemDropDownFlyoutOpening?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem));
81+
}
82+
83+
internal protected virtual void RaiseItemDropDownFlyoutClosed(BreadcrumbBarItem item, MenuFlyout flyout)
84+
{
85+
var index = _itemsRepeater?.GetElementIndex(item) ?? throw new ArgumentNullException($"{_itemsRepeater} is null.");
86+
ItemDropDownFlyoutClosed?.Invoke(this, new(flyout, item, index, item == _rootBreadcrumbBarItem));
87+
}
88+
89+
internal protected virtual void OnLayoutUpdated()
90+
{
91+
if (_itemsRepeater is null || (_itemsRepeaterLayout.IndexAfterEllipsis > _itemsRepeaterLayout.VisibleItemsCount && _isEllipsisRendered))
92+
return;
93+
94+
if (_ellipsisBreadcrumbBarItem is not null && _isEllipsisRendered != _itemsRepeaterLayout.EllipsisIsRendered)
95+
_ellipsisBreadcrumbBarItem.Visibility = _itemsRepeaterLayout.EllipsisIsRendered ? Visibility.Visible : Visibility.Collapsed;
96+
97+
_isEllipsisRendered = _itemsRepeaterLayout.EllipsisIsRendered;
98+
99+
for (int accessibilityIndex = 0, collectionIndex = _itemsRepeaterLayout.IndexAfterEllipsis;
100+
accessibilityIndex < _itemsRepeaterLayout.VisibleItemsCount;
101+
accessibilityIndex++, collectionIndex++)
102+
{
103+
if (_itemsRepeater.TryGetElement(collectionIndex) is { } element)
104+
{
105+
element.SetValue(AutomationProperties.PositionInSetProperty, accessibilityIndex);
106+
element.SetValue(AutomationProperties.SizeOfSetProperty, _itemsRepeaterLayout.VisibleItemsCount);
107+
}
108+
}
109+
}
110+
111+
internal bool TryGetElement(int index, out BreadcrumbBarItem? item)
112+
{
113+
item = null;
114+
115+
if (_itemsRepeater is null)
116+
return false;
117+
118+
item = _itemsRepeater.TryGetElement(index) as BreadcrumbBarItem;
119+
120+
return item is not null;
121+
}
122+
123+
// Event methods
124+
125+
private void ItemsRepeater_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args)
126+
{
127+
if (args.Element is not BreadcrumbBarItem item || _itemsRepeater is null)
128+
return;
129+
130+
item.IsLastItem = false;
131+
item.IsEllipsis = false;
132+
133+
if (args.Index == _itemsRepeater.ItemsSourceView.Count - 1)
134+
{
135+
_lastBreadcrumbBarItem = item;
136+
_lastBreadcrumbBarItem.IsLastItem = true;
137+
}
138+
139+
item.SetOwner(this);
140+
}
141+
142+
private void ItemsSourceView_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
143+
{
144+
if (_lastBreadcrumbBarItem is not null)
145+
_lastBreadcrumbBarItem.IsLastItem = false;
146+
147+
if (e.NewItems is not null &&
148+
e.NewItems.Count > 0 &&
149+
e.NewItems[e.NewItems.Count - 1] is BreadcrumbBarItem item)
150+
{
151+
_lastBreadcrumbBarItem = item;
152+
item.IsLastItem = true;
153+
}
154+
}
155+
156+
private void ItemsRepeater_ElementClearing(ItemsRepeater sender, ItemsRepeaterElementClearingEventArgs args)
157+
{
158+
if (args.Element is BreadcrumbBarItem item)
159+
{
160+
item.IsLastItem = false;
161+
item.IsEllipsis = false;
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)