diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs index 43531e71d..e45879a95 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs @@ -35,7 +35,7 @@ public partial class MainWindowViewModel : ObservableObject { Content = "Design guidance", Icon = new SymbolIcon { Symbol = SymbolRegular.DesignIdeas24 }, - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("Typography", SymbolRegular.TextFont24, typeof(TypographyPage)), new NavigationViewItem("Icons", SymbolRegular.Diversity24, typeof(IconsPage)), @@ -46,7 +46,7 @@ public partial class MainWindowViewModel : ObservableObject new NavigationViewItemSeparator(), new NavigationViewItem("Basic Input", SymbolRegular.CheckboxChecked24, typeof(BasicInputPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem(nameof(Anchor), typeof(AnchorPage)), new NavigationViewItem(nameof(Wpf.Ui.Controls.Button), typeof(ButtonPage)), @@ -68,7 +68,7 @@ public partial class MainWindowViewModel : ObservableObject Content = "Collections", Icon = new SymbolIcon { Symbol = SymbolRegular.Table24 }, TargetPageType = typeof(CollectionsPage), - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem(nameof(System.Windows.Controls.DataGrid), typeof(DataGridPage)), new NavigationViewItem(nameof(ListBox), typeof(ListBoxPage)), @@ -81,7 +81,7 @@ public partial class MainWindowViewModel : ObservableObject }, new NavigationViewItem("Date & time", SymbolRegular.CalendarClock24, typeof(DateAndTimePage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem(nameof(CalendarDatePicker), typeof(CalendarDatePickerPage)), new NavigationViewItem(nameof(System.Windows.Controls.Calendar), typeof(CalendarPage)), @@ -91,7 +91,7 @@ public partial class MainWindowViewModel : ObservableObject }, new NavigationViewItem("Dialogs & flyouts", SymbolRegular.Chat24, typeof(DialogsAndFlyoutsPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem(nameof(Snackbar), typeof(SnackbarPage)), new NavigationViewItem(nameof(ContentDialog), typeof(ContentDialogPage)), @@ -102,7 +102,7 @@ public partial class MainWindowViewModel : ObservableObject #if DEBUG new NavigationViewItem("Layout", SymbolRegular.News24, typeof(LayoutPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("Expander", typeof(ExpanderPage)), new NavigationViewItem("CardControl", typeof(CardControlPage)), @@ -115,7 +115,7 @@ public partial class MainWindowViewModel : ObservableObject Content = "Media", Icon = new SymbolIcon { Symbol = SymbolRegular.PlayCircle24 }, TargetPageType = typeof(MediaPage), - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("Image", typeof(ImagePage)), new NavigationViewItem("Canvas", typeof(CanvasPage)), @@ -125,7 +125,7 @@ public partial class MainWindowViewModel : ObservableObject }, new NavigationViewItem("Navigation", SymbolRegular.Navigation24, typeof(NavigationPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("BreadcrumbBar", typeof(BreadcrumbBarPage)), new NavigationViewItem("NavigationView", typeof(NavigationViewPage)), @@ -140,7 +140,7 @@ public partial class MainWindowViewModel : ObservableObject typeof(StatusAndInfoPage) ) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("InfoBadge", typeof(InfoBadgePage)), new NavigationViewItem("InfoBar", typeof(InfoBarPage)), @@ -151,7 +151,7 @@ public partial class MainWindowViewModel : ObservableObject }, new NavigationViewItem("Text", SymbolRegular.DrawText24, typeof(TextPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem(nameof(AutoSuggestBox), typeof(AutoSuggestBoxPage)), new NavigationViewItem(nameof(NumberBox), typeof(NumberBoxPage)), @@ -164,7 +164,7 @@ public partial class MainWindowViewModel : ObservableObject }, new NavigationViewItem("System", SymbolRegular.Desktop24, typeof(OpSystemPage)) { - MenuItems = new object[] + MenuItemsSource = new object[] { new NavigationViewItem("Clipboard", typeof(ClipboardPage)), new NavigationViewItem("FilePicker", typeof(FilePickerPage)), diff --git a/src/Wpf.Ui.Gallery/Views/Pages/Navigation/NavigationViewPage.xaml b/src/Wpf.Ui.Gallery/Views/Pages/Navigation/NavigationViewPage.xaml index 2b1d18110..a772a3279 100644 --- a/src/Wpf.Ui.Gallery/Views/Pages/Navigation/NavigationViewPage.xaml +++ b/src/Wpf.Ui.Gallery/Views/Pages/Navigation/NavigationViewPage.xaml @@ -197,7 +197,12 @@ + TargetPageType="{x:Type samples:SamplePage1}"> + + + + + { - MyTestNavigationView.MenuItems = new ObservableCollection() + MyTestNavigationView.MenuItemsSource = new ObservableCollection() { new NavigationViewItem("Home", SymbolRegular.Home24, typeof(SamplePage1)) }; diff --git a/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs b/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs index 2ef6e5b2c..3bbf1a417 100644 --- a/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs +++ b/src/Wpf.Ui/Controls/NavigationView/INavigationView.cs @@ -36,7 +36,7 @@ public interface INavigationView /// /// Gets the collection of menu items displayed in the NavigationView. /// - IList MenuItems { get; set; } + IList MenuItems { get; } /// /// Gets or sets an object source used to generate the content of the NavigationView menu. @@ -46,7 +46,7 @@ public interface INavigationView /// /// Gets the list of objects to be used as navigation items in the footer menu. /// - IList FooterMenuItems { get; set; } + IList FooterMenuItems { get; } /// /// Gets or sets the object that represents the navigation items to be used in the footer menu. diff --git a/src/Wpf.Ui/Controls/NavigationView/INavigationViewItem.cs b/src/Wpf.Ui/Controls/NavigationView/INavigationViewItem.cs index 52c078bde..a9862da19 100644 --- a/src/Wpf.Ui/Controls/NavigationView/INavigationViewItem.cs +++ b/src/Wpf.Ui/Controls/NavigationView/INavigationViewItem.cs @@ -35,7 +35,7 @@ public interface INavigationViewItem /// /// Gets the collection of menu items displayed in the NavigationView. /// - IList MenuItems { get; set; } + IList MenuItems { get; } /// /// Gets or sets an object source used to generate the content of the NavigationView menu. diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Base.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Base.cs index 26f7a1767..ec138cfa1 100644 --- a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Base.cs +++ b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Base.cs @@ -3,9 +3,6 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -// Based on Windows UI Library -// Copyright(c) Microsoft Corporation.All rights reserved. - using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; @@ -15,17 +12,15 @@ // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; -// https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationview?view=winrt-22621 +// Based on Windows UI Library https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationview?view=winrt-22621 /// /// Represents a container that enables navigation of app content. It has a header, a view for the main content, and a menu pane for navigation commands. /// -//[ToolboxItem(true)] -//[System.Drawing.ToolboxBitmap(typeof(NavigationView), "NavigationView.bmp")] public partial class NavigationView : System.Windows.Controls.Control, INavigationView { /// - /// Static constructor which overrides default property metadata. + /// Initializes static members of the class and overrides default property metadata. /// static NavigationView() { @@ -43,13 +38,18 @@ public NavigationView() { NavigationParent = this; - //It really should be here - MenuItems = new ObservableCollection(); - FooterMenuItems = new ObservableCollection(); - Loaded += OnLoaded; Unloaded += OnUnloaded; SizeChanged += OnSizeChanged; + + // Initialize MenuItems collection + var menuItems = new ObservableCollection(); + menuItems.CollectionChanged += OnMenuItems_CollectionChanged; + SetValue(MenuItemsPropertyKey, menuItems); + + var footerMenuItems = new ObservableCollection(); + footerMenuItems.CollectionChanged += OnMenuItems_CollectionChanged; + SetValue(FooterMenuItemsPropertyKey, footerMenuItems); } /// @@ -94,7 +94,7 @@ protected override void OnInitialized(EventArgs e) private void OnLoaded(object sender, RoutedEventArgs e) { // TODO: Refresh - UpdateVisualState((NavigationView)sender); + //UpdateVisualState((NavigationView)sender); } /// @@ -283,80 +283,57 @@ AutoSuggestBoxQuerySubmittedEventArgs args NavigateToMenuItemFromAutoSuggestBox(FooterMenuItems, element); } - protected virtual void AddItemsToDictionaries(IList list) + protected virtual void AddItemsToDictionaries(IEnumerable list) { - for (var i = 0; i < list.Count; i++) + foreach (NavigationViewItem singleNavigationViewItem in list.OfType()) { - var singleMenuItem = list[i]; - - if (singleMenuItem is not INavigationViewItem singleNavigationViewItem) - continue; - if (!PageIdOrTargetTagNavigationViewsDictionary.ContainsKey(singleNavigationViewItem.Id)) { - PageIdOrTargetTagNavigationViewsDictionary.Add( - singleNavigationViewItem.Id, - singleNavigationViewItem - ); + PageIdOrTargetTagNavigationViewsDictionary.Add(singleNavigationViewItem.Id, singleNavigationViewItem); } - if ( - !PageIdOrTargetTagNavigationViewsDictionary.ContainsKey( - singleNavigationViewItem.TargetPageTag - ) - ) + if (!PageIdOrTargetTagNavigationViewsDictionary.ContainsKey(singleNavigationViewItem.TargetPageTag)) { - PageIdOrTargetTagNavigationViewsDictionary.Add( - singleNavigationViewItem.TargetPageTag, - singleNavigationViewItem - ); + PageIdOrTargetTagNavigationViewsDictionary.Add(singleNavigationViewItem.TargetPageTag, singleNavigationViewItem); } - if ( - singleNavigationViewItem.TargetPageType is not null - && !PageTypeNavigationViewsDictionary.ContainsKey(singleNavigationViewItem.TargetPageType) - ) + if (singleNavigationViewItem.TargetPageType != null + && !PageTypeNavigationViewsDictionary.ContainsKey(singleNavigationViewItem.TargetPageType)) { - PageTypeNavigationViewsDictionary.Add( - singleNavigationViewItem.TargetPageType, - singleNavigationViewItem - ); + PageTypeNavigationViewsDictionary.Add(singleNavigationViewItem.TargetPageType, singleNavigationViewItem); } singleNavigationViewItem.IsMenuElement = true; - if (singleNavigationViewItem.MenuItems.Count <= 0) - continue; - - AddItemsToDictionaries(singleNavigationViewItem.MenuItems); + if (singleNavigationViewItem.HasMenuItems) + { + AddItemsToDictionaries(singleNavigationViewItem.MenuItems); + } } } + protected virtual void AddItemsToDictionaries() { AddItemsToDictionaries(MenuItems); AddItemsToDictionaries(FooterMenuItems); } - protected virtual void AddItemsToAutoSuggestBoxItems(IList list) + protected virtual void AddItemsToAutoSuggestBoxItems(IEnumerable list) { - for (var i = 0; i < list.Count; i++) + foreach (NavigationViewItem singleNavigationViewItem in list.OfType()) { - var singleMenuItem = list[i]; - - if (singleMenuItem is not NavigationViewItem singleNavigationViewItem) - continue; - if ( singleNavigationViewItem is { Content: string content, TargetPageType: { } } - && !string.IsNullOrWhiteSpace(content) - ) + && !String.IsNullOrWhiteSpace(content)) + { _autoSuggestBoxItems.Add(content); + } - if (singleNavigationViewItem.MenuItems.Count <= 0) - continue; - - AddItemsToAutoSuggestBoxItems(singleNavigationViewItem.MenuItems); + if (singleNavigationViewItem.HasMenuItems) + { + AddItemsToAutoSuggestBoxItems(singleNavigationViewItem.MenuItems); + } } } @@ -366,15 +343,10 @@ protected virtual void AddItemsToAutoSuggestBoxItems() AddItemsToAutoSuggestBoxItems(FooterMenuItems); } - protected virtual bool NavigateToMenuItemFromAutoSuggestBox(IList list, string selectedSuggestBoxItem) + protected virtual bool NavigateToMenuItemFromAutoSuggestBox(IEnumerable list, string selectedSuggestBoxItem) { - for (var i = 0; i < list.Count; i++) + foreach (NavigationViewItem singleNavigationViewItem in list.OfType()) { - var singleMenuItem = list[i]; - - if (singleMenuItem is not NavigationViewItem singleNavigationViewItem) - continue; - if (singleNavigationViewItem.Content is string content && content == selectedSuggestBoxItem) { NavigateInternal(singleNavigationViewItem); @@ -384,26 +356,28 @@ protected virtual bool NavigateToMenuItemFromAutoSuggestBox(IList list, string s return true; } - if (singleNavigationViewItem.MenuItems.Count <= 0) - continue; - - NavigateToMenuItemFromAutoSuggestBox(singleNavigationViewItem.MenuItems, selectedSuggestBoxItem); + if (NavigateToMenuItemFromAutoSuggestBox(singleNavigationViewItem.MenuItems, selectedSuggestBoxItem)) + { + return true; + } } return false; } - protected virtual void UpdateMenuItemsTemplate(IList list) + protected virtual void UpdateMenuItemsTemplate(IEnumerable list) { - for (var i = 0; i < list.Count; i++) + if (ItemTemplate == null) { - var singleMenuItem = list[i]; - - if (singleMenuItem is not NavigationViewItem singleNavigationViewItem) - continue; + return; + } - if (ItemTemplate is not null && singleNavigationViewItem.Template != ItemTemplate) + foreach (var item in list) + { + if (item is NavigationViewItem singleNavigationViewItem) + { singleNavigationViewItem.Template = ItemTemplate; + } } } @@ -432,16 +406,14 @@ protected virtual void CloseNavigationViewItemMenus() currentItem.NavigationViewItemParent?.Activate(this); } - protected void DeactivateMenuItems(IList list) + protected void DeactivateMenuItems(IEnumerable list) { - for (var i = 0; i < list.Count; i++) + foreach (var item in list) { - var singleMenuItem = list[i]; - - if (singleMenuItem is not NavigationViewItem singleNavigationViewItem) - continue; - - singleNavigationViewItem.Deactivate(this); + if (item is NavigationViewItem singleNavigationViewItem) + { + singleNavigationViewItem.Deactivate(this); + } } } diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs index 2451670be..800544bc0 100644 --- a/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs +++ b/src/Wpf.Ui/Controls/NavigationView/NavigationView.Properties.cs @@ -3,10 +3,8 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -// Based on Windows UI Library -// Copyright(c) Microsoft Corporation.All rights reserved. - using System.Collections; +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Windows.Controls; using Wpf.Ui.Animations; @@ -56,15 +54,15 @@ public partial class NavigationView new FrameworkPropertyMetadata(false) ); - /// - /// Property for . - /// - public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register( - nameof(MenuItems), - typeof(IList), - typeof(NavigationView), - new FrameworkPropertyMetadata(null, OnMenuItemsPropertyChanged) - ); + private static readonly DependencyPropertyKey MenuItemsPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(MenuItems), + typeof(ObservableCollection), + typeof(NavigationView), + new PropertyMetadata(null) + ); + + /// Identifies the dependency property. + public static readonly DependencyProperty MenuItemsProperty = MenuItemsPropertyKey.DependencyProperty; /// /// Property for . @@ -73,18 +71,18 @@ public partial class NavigationView nameof(MenuItemsSource), typeof(object), typeof(NavigationView), - new FrameworkPropertyMetadata(null, OnMenuItemsSourcePropertyChanged) + new FrameworkPropertyMetadata(null, OnMenuItemsSourceChanged) ); - /// - /// Property for . - /// - public static readonly DependencyProperty FooterMenuItemsProperty = DependencyProperty.Register( - nameof(FooterMenuItemsProperty), - typeof(IList), - typeof(NavigationView), - new FrameworkPropertyMetadata(null, OnFooterMenuItemsPropertyChanged) - ); + private static readonly DependencyPropertyKey FooterMenuItemsPropertyKey = DependencyProperty.RegisterReadOnly( + nameof(FooterMenuItems), + typeof(ObservableCollection), + typeof(NavigationView), + new PropertyMetadata(null) + ); + + /// Identifies the dependency property. + public static readonly DependencyProperty FooterMenuItemsProperty = FooterMenuItemsPropertyKey.DependencyProperty; /// /// Property for . @@ -93,7 +91,7 @@ public partial class NavigationView nameof(FooterMenuItemsSource), typeof(object), typeof(NavigationView), - new FrameworkPropertyMetadata(null, OnFooterMenuItemsSourcePropertyChanged) + new FrameworkPropertyMetadata(null, OnFooterMenuItemsSourceChanged) ); /// @@ -321,11 +319,7 @@ public bool AlwaysShowHeader } /// - public IList MenuItems - { - get => (IList)GetValue(MenuItemsProperty); - set => SetValue(MenuItemsProperty, value); - } + public IList MenuItems => (ObservableCollection)GetValue(MenuItemsProperty); /// [Bindable(true)] @@ -346,11 +340,7 @@ public object? MenuItemsSource } /// - public IList FooterMenuItems - { - get => (IList)GetValue(FooterMenuItemsProperty); - set => SetValue(FooterMenuItemsProperty, value); - } + public IList FooterMenuItems => (ObservableCollection)GetValue(FooterMenuItemsProperty); /// [Bindable(true)] @@ -505,46 +495,40 @@ public Thickness FrameMargin set => SetValue(FrameMarginProperty, value); } - private static void OnMenuItemsPropertyChanged(DependencyObject? d, DependencyPropertyChangedEventArgs e) + private void OnMenuItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - if (d is not NavigationView navigationView || e.NewValue is not IList enumerableNewValue) + if (e.NewItems is null) { return; } - if (navigationView.MenuItemsItemsControl is null) - { - navigationView.UpdateCollectionChangedEvent(e.OldValue as IList, e.NewValue as IList); - return; - } + UpdateMenuItemsTemplate(e.NewItems); + AddItemsToDictionaries(e.NewItems); + } - if (navigationView.MenuItemsItemsControl.ItemsSource.Equals(enumerableNewValue)) + private static void OnMenuItemsSourceChanged(DependencyObject? d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) { - navigationView.UpdateMenuItemsTemplate(enumerableNewValue); return; } - navigationView.MenuItemsItemsControl.ItemsSource = null; - navigationView.MenuItemsItemsControl.ItemsSource = enumerableNewValue; - navigationView.UpdateMenuItemsTemplate(enumerableNewValue); - navigationView.AddItemsToDictionaries(enumerableNewValue); - - navigationView.UpdateCollectionChangedEvent(e.OldValue as IList, e.NewValue as IList); - } + navigationView.MenuItems.Clear(); - private void UpdateCollectionChangedEvent(IList? oldMenuItems, IList? newMenuItems) - { - if (oldMenuItems is INotifyCollectionChanged notifyCollection) + if (e.NewValue is IEnumerable newItemsSource and not string) { - notifyCollection.CollectionChanged -= OnMenuItems_CollectionChanged; + foreach (var item in newItemsSource) + { + navigationView.MenuItems.Add(item); + } } - if (newMenuItems is INotifyCollectionChanged newNotifyCollection) + else if (e.NewValue != null) { - newNotifyCollection.CollectionChanged += OnMenuItems_CollectionChanged; + navigationView.MenuItems.Add(e.NewValue); } } - private void OnMenuItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + private void OnFooterMenuItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems is null) { @@ -555,58 +539,26 @@ private void OnMenuItems_CollectionChanged(object? sender, NotifyCollectionChang AddItemsToDictionaries(e.NewItems); } - private static void OnMenuItemsSourcePropertyChanged( - DependencyObject? d, - DependencyPropertyChangedEventArgs e - ) + private static void OnFooterMenuItemsSourceChanged(DependencyObject? d, DependencyPropertyChangedEventArgs e) { - if (d is not NavigationView navigationView || e.NewValue is not IList enumerableNewValue) - { - return; - } - - navigationView.MenuItems = enumerableNewValue; - } - - private static void OnFooterMenuItemsSourcePropertyChanged( - DependencyObject? d, - DependencyPropertyChangedEventArgs e - ) - { - if (d is not NavigationView navigationView || e.NewValue is not IList enumerableNewValue) + if (d is not NavigationView navigationView) { return; } - navigationView.FooterMenuItems = enumerableNewValue; - } + navigationView.FooterMenuItems.Clear(); - private static void OnFooterMenuItemsPropertyChanged( - DependencyObject? d, - DependencyPropertyChangedEventArgs e - ) - { - if (d is not NavigationView navigationView || e.NewValue is not IList enumerableNewValue) + if (e.NewValue is IEnumerable newItemsSource and not string) { - return; - } - - if (navigationView.FooterMenuItemsItemsControl is null) - { - navigationView.UpdateCollectionChangedEvent(e.OldValue as IList, e.NewValue as IList); - return; + foreach (var item in newItemsSource) + { + navigationView.FooterMenuItems.Add(item); + } } - - if (navigationView.FooterMenuItemsItemsControl.ItemsSource.Equals(enumerableNewValue)) + else if (e.NewValue != null) { - return; + navigationView.FooterMenuItems.Add(e.NewValue); } - - navigationView.FooterMenuItemsItemsControl.ItemsSource = null; - navigationView.FooterMenuItemsItemsControl.ItemsSource = enumerableNewValue; - navigationView.UpdateMenuItemsTemplate(enumerableNewValue); - navigationView.AddItemsToDictionaries(enumerableNewValue); - navigationView.UpdateCollectionChangedEvent(e.OldValue as IList, e.NewValue as IList); } private static void OnPaneDisplayModePropertyChanged( diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationViewItem.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationViewItem.cs index 8794f877e..e342b2686 100644 --- a/src/Wpf.Ui/Controls/NavigationView/NavigationViewItem.cs +++ b/src/Wpf.Ui/Controls/NavigationView/NavigationViewItem.cs @@ -3,11 +3,9 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -// Based on Windows UI Library -// Copyright(c) Microsoft Corporation.All rights reserved. - using System.Collections; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Windows.Controls; using System.Windows.Input; using Wpf.Ui.Converters; @@ -15,7 +13,7 @@ // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; -// https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitem?view=winrt-22621 +// Based on Windows UI Library https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitem?view=winrt-22621 /// /// Represents the container for an item in a NavigationView control. @@ -33,36 +31,34 @@ public class NavigationViewItem #region Static properties - /// - /// Property for . - /// - public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register( + private static readonly DependencyPropertyKey MenuItemsPropertyKey = DependencyProperty.RegisterReadOnly( nameof(MenuItems), - typeof(IList), + typeof(ObservableCollection), typeof(NavigationViewItem), - new PropertyMetadata(new ObservableCollection(), OnMenuItemsPropertyChanged) + new PropertyMetadata(null) ); - /// - /// Property for . - /// + /// Identifies the dependency property. + public static readonly DependencyProperty MenuItemsProperty = MenuItemsPropertyKey.DependencyProperty; + + /// Identifies the dependency property. public static readonly DependencyProperty MenuItemsSourceProperty = DependencyProperty.Register( nameof(MenuItemsSource), typeof(object), typeof(NavigationViewItem), - new PropertyMetadata(null, OnMenuItemsSourcePropertyChanged) + new PropertyMetadata(null, OnMenuItemsSourceChanged) ); - /// - /// Property for . - /// - public static readonly DependencyProperty HasMenuItemsProperty = DependencyProperty.Register( + internal static readonly DependencyPropertyKey HasMenuItemsPropertyKey = DependencyProperty.RegisterReadOnly( nameof(HasMenuItems), typeof(bool), typeof(NavigationViewItem), new PropertyMetadata(false) ); + /// Identifies the dependency property. + public static readonly DependencyProperty HasMenuItemsProperty = HasMenuItemsPropertyKey.DependencyProperty; + /// /// Property for . /// @@ -148,11 +144,7 @@ public class NavigationViewItem #region Properties /// - public IList MenuItems - { - get => (IList)GetValue(MenuItemsProperty); - set => SetValue(MenuItemsProperty, value); - } + public IList MenuItems => (ObservableCollection)GetValue(MenuItemsProperty); /// [Bindable(true)] @@ -161,21 +153,25 @@ public object? MenuItemsSource get => GetValue(MenuItemsSourceProperty); set { - if (value == null) + if (value is null) + { ClearValue(MenuItemsSourceProperty); + } else + { SetValue(MenuItemsSourceProperty, value); + } } } /// - /// Gets a value indicating whether the has . + /// Gets a value indicating whether MenuItems.Count > 0 /// - [Browsable(false), ReadOnly(true)] + [Browsable(false)] + [ReadOnly(true)] public bool HasMenuItems { get => (bool)GetValue(HasMenuItemsProperty); - private set => SetValue(HasMenuItemsProperty, value); } /// @@ -267,6 +263,11 @@ public NavigationViewItem() }; Loaded += (_, _) => InitializeNavigationViewEvents(); + + // Initialize the `Items` collection + var menuItems = new ObservableCollection(); + menuItems.CollectionChanged += OnMenuItems_CollectionChanged; + SetValue(MenuItemsPropertyKey, menuItems); } public NavigationViewItem(Type targetPageType) @@ -291,7 +292,7 @@ public NavigationViewItem(string name, SymbolRegular icon, Type targetPageType) public NavigationViewItem(string name, SymbolRegular icon, Type targetPageType, IList menuItems) : this(name, icon, targetPageType) { - SetValue(MenuItemsProperty, menuItems); + SetValue(MenuItemsSourceProperty, menuItems); } /// @@ -432,34 +433,36 @@ protected override void OnMouseDown(MouseButtonEventArgs e) e.Handled = true; } - private static void OnMenuItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private void OnMenuItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - if (d is not NavigationViewItem navigationViewItem) - return; + SetValue(HasMenuItemsPropertyKey, MenuItems.Count > 0); - navigationViewItem.HasMenuItems = navigationViewItem.MenuItems.Count > 0; - - foreach (var menuItem in navigationViewItem.MenuItems) + foreach (INavigationViewItem item in MenuItems.OfType()) { - if (menuItem is not INavigationViewItem item) - continue; - - item.NavigationViewItemParent = navigationViewItem; + item.NavigationViewItemParent = this; } } - private static void OnMenuItemsSourcePropertyChanged( - DependencyObject d, - DependencyPropertyChangedEventArgs e - ) + private static void OnMenuItemsSourceChanged(DependencyObject? d, DependencyPropertyChangedEventArgs e) { - if (d is not NavigationViewItem navigationViewItem || e.NewValue is not IList enumerableNewValue) + if (d is not NavigationViewItem navigationViewItem) + { return; + } - navigationViewItem.MenuItems = enumerableNewValue; + navigationViewItem.MenuItems.Clear(); - if (navigationViewItem.MenuItems.Count > 0) - navigationViewItem.HasMenuItems = true; + if (e.NewValue is IEnumerable newItemsSource and not string) + { + foreach (var item in newItemsSource) + { + navigationViewItem.MenuItems.Add(item); + } + } + else if (e.NewValue != null) + { + navigationViewItem.MenuItems.Add(e.NewValue); + } } private void InitializeNavigationViewEvents() diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationViewTop.xaml b/src/Wpf.Ui/Controls/NavigationView/NavigationViewTop.xaml index e65a08f6f..e3b03b088 100644 --- a/src/Wpf.Ui/Controls/NavigationView/NavigationViewTop.xaml +++ b/src/Wpf.Ui/Controls/NavigationView/NavigationViewTop.xaml @@ -191,7 +191,7 @@ HorizontalOffset="-12" IsOpen="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" Placement="Bottom" - PlacementTarget="{Binding ElementName=Border}" + PlacementTarget="{Binding ElementName=MainBorder}" PopupAnimation="None" StaysOpen="False" VerticalOffset="1"> @@ -200,8 +200,8 @@ x:Name="SubMenuBorder" Margin="12,0,12,18" Padding="0,3,0,3" - Background="{DynamicResource FlyoutBorderBrush}" - BorderBrush="{DynamicResource FlyoutBackground}" + Background="{DynamicResource FlyoutBackground}" + BorderBrush="{DynamicResource FlyoutBorderBrush}" BorderThickness="1" CornerRadius="8" SnapsToDevicePixels="True"> @@ -320,11 +320,11 @@ From="0.0" To="1.0" Duration="0:0:0.167" /> - + Duration="00:00:00.167" />--> @@ -338,11 +338,11 @@ To="0.0" Duration="0:0:0.167" /> - + Duration="00:00:00.167" />-->