diff --git a/src/Wpf.Ui.Demo.Mvvm/ViewModels/ContainerViewModel.cs b/src/Wpf.Ui.Demo.Mvvm/ViewModels/ContainerViewModel.cs index 7cb5f6546..d05142699 100644 --- a/src/Wpf.Ui.Demo.Mvvm/ViewModels/ContainerViewModel.cs +++ b/src/Wpf.Ui.Demo.Mvvm/ViewModels/ContainerViewModel.cs @@ -8,7 +8,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Wpf.Ui.Common; using Wpf.Ui.Controls; -using Wpf.Ui.Controls.Interfaces; +using Wpf.Ui.Controls.Navigation; using Wpf.Ui.Mvvm.Contracts; namespace Wpf.Ui.Demo.Mvvm.ViewModels; @@ -21,10 +21,10 @@ public partial class ContainerViewModel : ObservableObject private string _applicationTitle = String.Empty; [ObservableProperty] - private ObservableCollection _navigationItems = new(); + private ObservableCollection _navigationItems = new(); [ObservableProperty] - private ObservableCollection _navigationFooter = new(); + private ObservableCollection _navigationFooter = new(); [ObservableProperty] private ObservableCollection _trayMenuItems = new(); @@ -37,34 +37,31 @@ public ContainerViewModel(INavigationService navigationService) private void InitializeViewModel() { - ApplicationTitle = "WPF UI - Wpf.Ui.Demo.Mvvm"; + ApplicationTitle = "WPF UI - MVVM Demo"; - NavigationItems = new ObservableCollection + NavigationItems = new ObservableCollection { - new NavigationItem() + new NavigationViewItem() { Content = "Home", - PageTag = "dashboard", Icon = SymbolRegular.Home24, - PageType = typeof(Views.Pages.DashboardPage) + TargetPageType = typeof(Views.Pages.DashboardPage) }, - new NavigationItem() + new NavigationViewItem() { Content = "Data", - PageTag = "data", Icon = SymbolRegular.DataHistogram24, - PageType = typeof(Views.Pages.DataPage) + TargetPageType = typeof(Views.Pages.DataPage) } }; - NavigationFooter = new ObservableCollection + NavigationFooter = new ObservableCollection { - new NavigationItem() + new NavigationViewItem() { Content = "Settings", - PageTag = "settings", Icon = SymbolRegular.Settings24, - PageType = typeof(Views.Pages.SettingsPage) + TargetPageType = typeof(Views.Pages.SettingsPage) } }; diff --git a/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml b/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml index b08b7e357..ced986aa1 100644 --- a/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml +++ b/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml @@ -18,58 +18,46 @@ WindowStartupLocation="CenterScreen" mc:Ignorable="d"> - - - - + + + + - + Grid.Row="1" + FooterMenuItemsSource="{Binding ViewModel.NavigationFooter, Mode=OneWay}" + MenuItemsSource="{Binding ViewModel.NavigationItems, Mode=OneWay}"> + + + + + + + + TooltipText="WPF UI - MVVM Demo"> - - - - - - - - - - - - - diff --git a/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml.cs b/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml.cs index 50d574143..2191de25e 100644 --- a/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml.cs +++ b/src/Wpf.Ui.Demo.Mvvm/Views/Container.xaml.cs @@ -5,7 +5,6 @@ using System; using System.Windows; -using System.Windows.Controls; using Wpf.Ui.Controls.Interfaces; using Wpf.Ui.Mvvm.Contracts; @@ -36,17 +35,14 @@ public Container(ViewModels.ContainerViewModel viewModel, IPageService pageServi #region INavigationWindow methods - public Frame GetFrame() - => RootFrame; - - public INavigation GetNavigation() + public INavigationView GetNavigation() => RootNavigation; public bool Navigate(Type pageType) => RootNavigation.Navigate(pageType); public void SetPageService(IPageService pageService) - => RootNavigation.PageService = pageService; + => RootNavigation.SetPageService(pageService); public void ShowWindow() => Show(); @@ -66,4 +62,14 @@ protected override void OnClosed(EventArgs e) // Make sure that closing this window will begin the process of closing the application. Application.Current.Shutdown(); } + + INavigationView INavigationWindow.GetNavigation() + { + throw new NotImplementedException(); + } + + public void SetServiceProvider(IServiceProvider serviceProvider) + { + throw new NotImplementedException(); + } } diff --git a/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DashboardPage.xaml b/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DashboardPage.xaml index 679e82960..1cb5856b7 100644 --- a/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DashboardPage.xaml +++ b/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DashboardPage.xaml @@ -14,7 +14,7 @@ Foreground="{DynamicResource TextFillColorPrimaryBrush}" mc:Ignorable="d"> - + diff --git a/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DataPage.xaml b/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DataPage.xaml index 5070ca287..27c4ff82a 100644 --- a/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DataPage.xaml +++ b/src/Wpf.Ui.Demo.Mvvm/Views/Pages/DataPage.xaml @@ -14,7 +14,7 @@ d:DesignWidth="800" mc:Ignorable="d"> - + - + - - - - + + + + - - - + + + + + + + + - + - - - + + + - - + TargetPageType="{x:Type pages:SettingsPage}" /> + + - - - - - - - - - - - - - diff --git a/src/Wpf.Ui.Demo.Simple/Views/Pages/DashboardPage.xaml b/src/Wpf.Ui.Demo.Simple/Views/Pages/DashboardPage.xaml index 58142f7c6..106d464c1 100644 --- a/src/Wpf.Ui.Demo.Simple/Views/Pages/DashboardPage.xaml +++ b/src/Wpf.Ui.Demo.Simple/Views/Pages/DashboardPage.xaml @@ -12,7 +12,7 @@ Foreground="{DynamicResource TextFillColorPrimaryBrush}" mc:Ignorable="d"> - + diff --git a/src/Wpf.Ui.Demo.Simple/Views/Pages/DataPage.xaml b/src/Wpf.Ui.Demo.Simple/Views/Pages/DataPage.xaml index bf648966a..c448307c4 100644 --- a/src/Wpf.Ui.Demo.Simple/Views/Pages/DataPage.xaml +++ b/src/Wpf.Ui.Demo.Simple/Views/Pages/DataPage.xaml @@ -12,7 +12,7 @@ d:DesignWidth="800" mc:Ignorable="d"> - + - + public static class Transitions { - private const double DecelerationRatio = 0.7; + private const double DecelerationRatio = 0.7D; /// /// Attempts to apply an animation effect while adding content to the frame. diff --git a/src/Wpf.Ui/Common/Interfaces/INavigableView.cs b/src/Wpf.Ui/Common/Interfaces/INavigableView.cs index cae5e1420..b3ad0d26b 100644 --- a/src/Wpf.Ui/Common/Interfaces/INavigableView.cs +++ b/src/Wpf.Ui/Common/Interfaces/INavigableView.cs @@ -8,13 +8,13 @@ namespace Wpf.Ui.Common.Interfaces; /// -/// A view whose ViewModel is separate from the DataContext and can be navigated by . +/// A component whose ViewModel is separate from the DataContext and can be navigated by . /// public interface INavigableView { /// /// ViewModel used by the view. - /// Optionally, it may implement and be navigated by . + /// Optionally, it may implement and be navigated by . /// T ViewModel { get; } } diff --git a/src/Wpf.Ui/Common/NavigationType.cs b/src/Wpf.Ui/Common/NavigationType.cs deleted file mode 100644 index 8876e9b7b..000000000 --- a/src/Wpf.Ui/Common/NavigationType.cs +++ /dev/null @@ -1,13 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -namespace Wpf.Ui.Common; - -public enum NavigationType -{ - Fluent, - Compact, - Store -} diff --git a/src/Wpf.Ui/Common/RoutedNavigationEvent.cs b/src/Wpf.Ui/Common/RoutedNavigationEvent.cs deleted file mode 100644 index bdaace6fd..000000000 --- a/src/Wpf.Ui/Common/RoutedNavigationEvent.cs +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.Diagnostics.CodeAnalysis; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Common; - -/// -/// Event triggered on successful navigation. -/// -/// Current navigation instance. -#if NET5_0_OR_GREATER -public delegate void RoutedNavigationEvent([NotNull] INavigation sender, RoutedNavigationEventArgs e); -#else -public delegate void RoutedNavigationEvent(INavigation sender, RoutedNavigationEventArgs e); -#endif diff --git a/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs b/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs deleted file mode 100644 index ae124110c..000000000 --- a/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs +++ /dev/null @@ -1,32 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.Windows; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Common; - -/// -/// with additional . -/// -public class RoutedNavigationEventArgs : RoutedEventArgs -{ - /// - /// Currently displayed page. - /// - public INavigationItem CurrentPage { get; set; } - - /// - /// Constructor for . - /// - /// The new value that the SourceProperty is being set to. - /// The new value that the Property is being set to. - /// Currently displayed page. - public RoutedNavigationEventArgs(RoutedEvent routedEvent, object source, INavigationItem currentPage) : base( - routedEvent, source) - { - CurrentPage = currentPage; - } -} diff --git a/src/Wpf.Ui/Controls/AutoSuggestBox.cs b/src/Wpf.Ui/Controls/AutoSuggestBox.cs index 806bc938d..dd03430d0 100644 --- a/src/Wpf.Ui/Controls/AutoSuggestBox.cs +++ b/src/Wpf.Ui/Controls/AutoSuggestBox.cs @@ -183,6 +183,11 @@ public event RoutedEventHandler SuggestionChosen remove => RemoveHandler(SuggestionChosenEvent, value); } + /// + /// Gets the suggested result that the user chose. + /// + public string ChosenSuggestion { get; protected set; } = String.Empty; + /// /// Invoked whenever application code or an internal process, /// such as a rebuilding layout pass, calls the ApplyTemplate method. @@ -274,10 +279,10 @@ protected virtual void OnSuggestionsPresenterSelectionChanged(object sender, Sel listView.UnselectAll(); - _currentText = selected?.ToString() ?? String.Empty; + ChosenSuggestion = selected?.ToString() ?? String.Empty; - Text = _currentText; - CaretIndex = _currentText.Length; + Text = ChosenSuggestion; + CaretIndex = ChosenSuggestion.Length; IsSuggestionListOpen = false; Focus(); diff --git a/src/Wpf.Ui/Controls/Breadcrumb.cs b/src/Wpf.Ui/Controls/Breadcrumb.cs deleted file mode 100644 index cb0b089ef..000000000 --- a/src/Wpf.Ui/Controls/Breadcrumb.cs +++ /dev/null @@ -1,80 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.Windows; -using Wpf.Ui.Common; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Controls; - -/// -/// Displays the name of the current and it's parents that can be navigated using . -/// -public class Breadcrumb : System.Windows.Controls.Control -{ - /// - /// Property for . - /// - public static readonly DependencyProperty CurrentProperty = DependencyProperty.Register(nameof(Current), - typeof(string), typeof(Breadcrumb), new PropertyMetadata(String.Empty)); - - /// - /// Property for . - /// - public static readonly DependencyProperty NavigationProperty = DependencyProperty.Register(nameof(Navigation), - typeof(INavigation), typeof(Breadcrumb), - new PropertyMetadata(null, OnNavigationChanged)); - - /// - /// based on which displays the titles. - /// - public string Current - { - get => (string)GetValue(CurrentProperty); - set => SetValue(CurrentProperty, value); - } - - /// - /// based on which displays the titles. - /// - public INavigation Navigation - { - get => GetValue(NavigationProperty) as INavigation; - set => SetValue(NavigationProperty, value); - } - - protected virtual void OnNavigated(INavigation sender, RoutedNavigationEventArgs e) - { -#if DEBUG - System.Diagnostics.Debug.WriteLine($"INFO | {typeof(Breadcrumb)} builded, current nav: {Navigation.GetType()}", "Wpf.Ui.Breadcrumb"); -#endif - - //TODO: Navigate with previous levels - - if (Navigation?.Current is not INavigationItem item) - return; - - var pageName = item.Content as string; - - if (String.IsNullOrEmpty(pageName)) - return; - - Current = pageName; - } - - protected virtual void OnNavigationChanged() - { - Navigation.Navigated += OnNavigated; - } - - private static void OnNavigationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not Breadcrumb breadcrumb) - return; - - breadcrumb.OnNavigationChanged(); - } -} diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigation.cs b/src/Wpf.Ui/Controls/Interfaces/INavigation.cs deleted file mode 100644 index 3e2592e12..000000000 --- a/src/Wpf.Ui/Controls/Interfaces/INavigation.cs +++ /dev/null @@ -1,218 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Windows.Controls; -using Wpf.Ui.Animations; -using Wpf.Ui.Common; -using Wpf.Ui.Mvvm.Contracts; - -namespace Wpf.Ui.Controls.Interfaces; - -/// -/// Represents navigation class. -/// -public interface INavigation -{ - /// - /// Service providing views. - /// - IPageService PageService { get; set; } - - /// - /// Navigation item ID of the current page. - /// If set to a value less than , no will be loaded during initialization. - /// - int SelectedPageIndex { get; set; } - - /// - /// Navigation item ID of the previous page. - /// - int PreviousPageIndex { get; } - - /// - /// Creates an instance of all pages defined with after the is loaded. - /// - bool Precache { get; set; } - - /// - /// Indicates the possibility of navigation back - /// - bool CanGoBack { get; } - - /// - /// Currently used item like . - /// - INavigationItem Current { get; } - - /// - /// Gets or sets the in which the will be loaded after navigation. - /// - [Bindable(true)] - Frame Frame { get; set; } - - /// - /// Gets or sets the list of that will be displayed on the navigation. - /// - [Bindable(true)] - ObservableCollection Items { get; set; } - - /// - /// Gets or sets the list of which will be displayed at the bottom of the navigation and will not be scrolled. - /// - [Bindable(true)] - ObservableCollection Footer { get; set; } - - /// - /// Specifies dimension of children stacking. - /// - Orientation Orientation { get; set; } - - /// - /// Gets or sets the that will be triggered during navigation. - /// - [Category("Behavior")] - event RoutedNavigationEvent Navigated; - - /// - /// Gets or sets the that will be triggered during forward navigation. - /// - [Category("Behavior")] - event RoutedNavigationEvent NavigatedForward; - - /// - /// Gets or sets the that will be triggered during backward navigation. - /// - [Category("Behavior")] - event RoutedNavigationEvent NavigatedBackward; - - /// - /// Gets or sets a value deciding how long the effect of the transition between the pages should take. - /// - [Bindable(true), Category("Appearance")] - int TransitionDuration { get; set; } - - /// - /// Gets or sets type of transitions during navigation. - /// - [Bindable(true), Category("Appearance")] - TransitionType TransitionType { get; set; } - - /// - /// Clears all navigation items. - /// - void Flush(); - - /// - /// Clears all initialized instances of the pages. - /// - void ClearCache(); - - /// - /// Navigates to the previous page using the . - /// - /// - bool NavigateBack(); - - /// - /// Navigates to the page using the . - /// - /// Type of the page to navigate. - /// if the operation was successful. - bool Navigate(Type pageType); - - /// - /// Navigates to the page using the . - /// - /// Type of the page to navigate. - /// When an DataContext changes, all data-bound properties (on this element or any other element) whose Bindings use this DataContext will change to reflect the new value. - /// if the operation was successful. - bool Navigate(Type pageType, object dataContext); - - /// - /// Loads a instance into based on the tag of . - /// - /// ID of the page to be loaded. - /// if the operation was successful. - bool Navigate(int pageIndex); - - /// - /// Loads a instance into based on the tag of . - /// - /// ID of the page to be loaded. - /// When an DataContext changes, all data-bound properties (on this element or any other element) whose Bindings use this DataContext will change to reflect the new value. - /// if the operation was successful. - bool Navigate(int pageIndex, object dataContext); - - /// - /// Loads a instance into based on the tag of . - /// - /// to be loaded. - /// if the operation was successful. - bool Navigate(string pageTag); - - /// - /// Loads a instance into based on the tag of . - /// - /// to be loaded. - /// When an DataContext changes, all data-bound properties (on this element or any other element) whose Bindings use this DataContext will change to reflect the new value. - bool Navigate(string pageTag, object dataContext); - - /// - /// Navigate to the given object that is outside the current navigation. - /// - /// The element you want to navigate to a that is not in the or pool. - /// if the operation was successful. - bool NavigateExternal(object frameworkElement); - - /// - /// Navigate to the given object that is outside the current navigation. - /// - /// The element you want to navigate to a that is not in the or pool. - /// Context of the data for data binding. - /// if the operation was successful. - bool NavigateExternal(object frameworkElement, object dataContext); - - /// - /// Navigate to the given that is outside the current navigation. - /// - /// to the element you want to navigate to a that is not in the or pool. - /// if the operation was successful. - bool NavigateExternal(Uri absolutePageUri); - - /// - /// Navigate to the given that is outside the current navigation. - /// - /// to the element you want to navigate to a that is not in the or pool. - /// Context of the data for data binding. - /// if the operation was successful. - bool NavigateExternal(Uri absolutePageUri, object dataContext); - - /// - /// Sets of the page. - /// If the page is not in the Cache, and is defined based on , its object will be created and then its DataContext will be defined. - /// - /// Id of the page from or . - /// Context of the data for data binding. - /// if the operation was successful. - bool SetContext(int pageId, object dataContext); - - /// - /// Sets of the page. - /// If the page is not in the Cache, and is defined based on , its object will be created and then its DataContext will be defined. - /// - /// Tag of the page from or . - /// Context of the data for data binding. - /// if the operation was successful. - bool SetContext(string pageTag, object dataContext); - - /// - /// Tires to set the DataContext for the currently displayed page. - /// - /// Data context to be set. - void SetCurrentContext(object dataContext); -} diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigationControl.cs b/src/Wpf.Ui/Controls/Interfaces/INavigationControl.cs deleted file mode 100644 index 3c89b39b4..000000000 --- a/src/Wpf.Ui/Controls/Interfaces/INavigationControl.cs +++ /dev/null @@ -1,13 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -namespace Wpf.Ui.Controls.Interfaces; - -/// -/// An interface element that can be part of a navigation list. -/// -public interface INavigationControl -{ -} diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs b/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs deleted file mode 100644 index 216171173..000000000 --- a/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs +++ /dev/null @@ -1,58 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.ComponentModel; -using System.Windows; -using System.Windows.Controls; - -namespace Wpf.Ui.Controls.Interfaces; - -/// -/// Navigation element. -/// -public interface INavigationItem -{ - /// - /// Represents a text page identifier that can be navigated with . - /// - string PageTag { get; set; } - - /// - /// Content is the data used to generate the child elements of this control. - /// - object Content { get; } - - /// - /// Gets information whether the current element is active. - /// - bool IsActive { get; set; } - - /// - /// Determines whether an should be cached. - /// - bool Cache { get; set; } - - /// - /// URI of the application or content being navigated to. - /// - Uri PageSource { get; set; } - - /// - /// A inherited from that defines page of the item. - /// - Type PageType { get; set; } - - /// - /// Absolute path to the XAML template based on and . - /// - Uri AbsolutePageSource { get; } - - /// - /// Add / Remove ClickEvent handler - /// - [Category("Behavior")] - event RoutedEventHandler Click; -} diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigationView.cs b/src/Wpf.Ui/Controls/Interfaces/INavigationView.cs new file mode 100644 index 000000000..355480d16 --- /dev/null +++ b/src/Wpf.Ui/Controls/Interfaces/INavigationView.cs @@ -0,0 +1,236 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Collections; +using System.Windows; +using System.Windows.Controls; +using Wpf.Ui.Animations; +using Wpf.Ui.Controls.Navigation; +using Wpf.Ui.Mvvm.Contracts; + +namespace Wpf.Ui.Controls.Interfaces; + +/// +/// 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. +/// +public interface INavigationView +{ + /// + /// Gets or sets the header content. + /// + object Header { get; set; } + + /// + /// Gets or sets the visibility. + /// + Visibility HeaderVisibility { get; set; } + + /// + /// Gets or sets a value that indicates whether the header is always visible. + /// + bool AlwaysShowHeader { get; set; } + + /// + /// Gets the collection of menu items displayed in the NavigationView. + /// + IList MenuItems { get; set; } + + /// + /// Gets or sets an object source used to generate the content of the NavigationView menu. + /// + object MenuItemsSource { get; set; } + + /// + /// Gets the list of objects to be used as navigation items in the footer menu. + /// + IList FooterMenuItems { get; set; } + + /// + /// Gets or sets the object that represents the navigation items to be used in the footer menu. + /// + object FooterMenuItemsSource { get; set; } + + /// + /// Gets the selected item. + /// + INavigationViewItem SelectedItem { get; } + + /// + /// Gets or sets a UI element that is shown at the top of the control, below the pane if PaneDisplayMode is Top. + /// + object ContentOverlay { get; set; } + + /// + /// Gets a value that indicates whether the back button is enabled or disabled. + /// + bool IsBackEnabled { get; } + + /// + /// Gets or sets a value that indicates whether the back button is visible or not. + /// Default value is "Auto", which indicates that button visibility depends on the DisplayMode setting of the NavigationView. + /// + NavigationViewBackButtonVisible IsBackButtonVisible { get; set; } + + /// + /// Gets or sets a value that indicates whether the toggle button is visible. + /// + bool IsPaneToggleVisible { get; set; } + + /// + /// Gets or sets a value that specifies whether the NavigationView pane is expanded to its full width. + /// + bool IsPaneOpen { get; set; } + + /// + /// Gets or sets a value that determines whether the pane is shown. + /// + bool IsPaneVisible { get; set; } + + /// + /// Gets or sets the width of the NavigationView pane when it's fully expanded. + /// + double OpenPaneLength { get; set; } + + /// + /// Gets or sets the content for the pane header. + /// + object PaneHeader { get; set; } + + /// + /// Gets or sets the content for the pane footer. + /// + object PaneFooter { get; set; } + + /// + /// Gets a value that specifies how the pane and content areas of a NavigationView are being shown. + /// It is not the same PaneDisplayMode as in WinUi. + /// + NavigationViewPaneDisplayMode PaneDisplayMode { get; set; } + + /// + /// Gets or sets an AutoSuggestBox to be displayed in the NavigationView. + /// + AutoSuggestBox AutoSuggestBox { get; set; } + + /// + /// Gets or sets an TitleBar to be displayed in the NavigationView. + /// + TitleBar TitleBar { get; set; } + + /// + /// Template Property for and . + /// + ControlTemplate ItemTemplate { get; set; } + + /// + /// Gets or sets a value deciding how long the effect of the transition between the pages should take. + /// + int TransitionDuration { get; set; } + + /// + /// Gets or sets type of transitions during navigation. + /// + TransitionType TransitionType { get; set; } + + /// + /// Occurs when the NavigationView pane is opened. + /// + event NavigationViewEvent PaneOpened; + + /// + /// Occurs when the NavigationView pane is closed. + /// + event NavigationViewEvent PaneClosed; + + /// + /// Occurs when the currently selected item changes. + /// + event NavigationViewEvent SelectionChanged; + + /// + /// Occurs when an item in the menu receives an interaction such as a click or tap. + /// + event NavigationViewEvent ItemInvoked; + + /// + /// Occurs when the back button receives an interaction such as a click or tap. + /// + event NavigationViewEvent BackRequested; + + /// + /// Gets a value that indicates whether there is at least one entry in back navigation history. + /// + bool CanGoBack { get; } + + /// + /// This method synchronously navigates this Frame to the + /// given Element. + /// + bool Navigate(Type pageType); + + /// + /// This method synchronously navigates this Frame to the + /// given Element. + /// + bool Navigate(Type pageType, object dataContext); + + /// + /// This method synchronously navigates this Frame to the + /// given Element. + /// + bool Navigate(string pageIdOrTargetTag); + + /// + /// This method synchronously navigates this Frame to the + /// given Element. + /// + bool Navigate(string pageIdOrTargetTag, object dataContext); + + /// + /// Replaces the contents of the navigation frame, without changing the currently selected item or triggering an . + /// + bool ReplaceContent(Type pageTypeToEmbed); + + /// + /// Replaces the contents of the navigation frame, without changing the currently selected item or triggering an . + /// + bool ReplaceContent(UIElement pageInstanceToEmbed); + + /// + /// Replaces the contents of the navigation frame, without changing the currently selected item or triggering an . + /// + bool ReplaceContent(UIElement pageInstanceToEmbed, object dataContext); + + /// + /// Navigates the NavigationView to the next journal entry. + /// + /// if successfully navigated forward, otherwise . + bool GoForward(); + + /// + /// Navigates the NavigationView to the previous journal entry. + /// + /// if successfully navigated backward, otherwise . + bool GoBack(); + + /// + /// Clears the NavigationView history. + /// + void ClearJournal(); + + /// + /// Allows you to assign to the NavigationView a special service responsible for retrieving the page instances. + /// + void SetPageService(IPageService pageService); + + /// + /// Allows you to assign a general to the NavigationView that will be used to retrieve page instances and view models. + /// + void SetServiceProvider(IServiceProvider serviceProvider); +} diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigationViewItem.cs b/src/Wpf.Ui/Controls/Interfaces/INavigationViewItem.cs new file mode 100644 index 000000000..45293f544 --- /dev/null +++ b/src/Wpf.Ui/Controls/Interfaces/INavigationViewItem.cs @@ -0,0 +1,68 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Collections; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; + +namespace Wpf.Ui.Controls.Interfaces; + +/// +/// Represents the container for an item in a NavigationView control. +/// +public interface INavigationViewItem +{ + /// + /// Unique identifier that allows the item to be located in the navigation. + /// + string Id { get; } + + /// + /// Gets the collection of menu items displayed in the NavigationView. + /// + IList MenuItems { get; set; } + + /// + /// Gets or sets an object source used to generate the content of the NavigationView menu. + /// + object MenuItemsSource { get; set; } + + /// + /// Gets information whether the current element is active. + /// + bool IsActive { get; } + + /// + /// Gets information whether the sub- are expanded. + /// + bool IsExpanded { get; } + + /// + /// A unique tag used by the parent navigation system for the purpose of searching and navigating. + /// + public string TargetPageTag { get; set; } + + /// + /// The type of the page to be navigated. (Should be derived from ). + /// + public Type TargetPageType { get; set; } + + /// + /// Template Property + /// + public ControlTemplate Template { get; set; } + + /// + /// Add / Remove ClickEvent handler. + /// + [Category("Behavior")] + public event RoutedEventHandler Click; +} + diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs b/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs deleted file mode 100644 index d1ffefb16..000000000 --- a/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs +++ /dev/null @@ -1,39 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -#nullable enable - -using System.ComponentModel; -using System.Windows; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Controls.Navigation; - -/// -/// Inherited from the , used to navigate backwards inside the . -/// -public class NavigationBackButton : System.Windows.Controls.Button -{ - /// - /// Property for . - /// - public static readonly DependencyProperty NavigationProperty = DependencyProperty.Register(nameof(Navigation), - typeof(INavigation), typeof(NavigationBackButton), new PropertyMetadata(null)); - - /// - /// Parent control. - /// - [Bindable(true), Category("Behavior")] - public INavigation? Navigation - { - get => (INavigation)GetValue(NavigationProperty); - set => SetValue(NavigationProperty, value); - } - - public NavigationBackButton() - { - SetValue(CommandProperty, new Common.RelayCommand(_ => Navigation?.NavigateBack(), () => Navigation is not null && Navigation.CanGoBack)); - } -} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs b/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs deleted file mode 100644 index bc8ea838c..000000000 --- a/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs +++ /dev/null @@ -1,722 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -#nullable enable -#pragma warning disable CS8600 -#pragma warning disable CS8603 - -using System; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using Wpf.Ui.Common; -using Wpf.Ui.Controls.Interfaces; -using Wpf.Ui.Mvvm.Contracts; -using Wpf.Ui.Mvvm.Interfaces; - -namespace Wpf.Ui.Controls.Navigation; - -/// -/// Base implementation for the navigation view. -/// -public abstract class NavigationBase : System.Windows.Controls.Control, INavigation -{ - /// - /// Service used for navigation purposes. - /// - private readonly Services.Internal.NavigationService? _navigationService; - - /// - /// Property for . - /// - public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(nameof(Items), - typeof(ObservableCollection), typeof(NavigationBase), - new PropertyMetadata((ObservableCollection)null, OnItemsChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(nameof(Footer), - typeof(ObservableCollection), typeof(NavigationBase), - new PropertyMetadata((ObservableCollection)null, OnFooterChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(nameof(Orientation), - typeof(Orientation), typeof(NavigationBase), - new FrameworkPropertyMetadata(Orientation.Vertical, - FrameworkPropertyMetadataOptions.AffectsMeasure)); - - /// - /// Property for . - /// - public static readonly DependencyProperty FrameProperty = DependencyProperty.Register(nameof(Frame), - typeof(Frame), typeof(NavigationBase), - new PropertyMetadata((Frame)null, OnFrameChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register( - nameof(TransitionDuration), - typeof(int), typeof(NavigationBase), - new PropertyMetadata(300, OnTransitionDurationChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty TransitionTypeProperty = DependencyProperty.Register( - nameof(TransitionType), - typeof(Animations.TransitionType), typeof(NavigationBase), - new PropertyMetadata(Animations.TransitionType.FadeInWithSlide, OnTransitionTypeChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty SelectedPageIndexProperty = DependencyProperty.Register( - nameof(SelectedPageIndex), - typeof(int), typeof(NavigationBase), - new PropertyMetadata(-1)); - - /// - /// Property for . - /// - public static readonly DependencyProperty PrecacheProperty = DependencyProperty.Register( - nameof(Precache), - typeof(bool), typeof(NavigationBase), - new PropertyMetadata(false)); - - /// - /// Attached property for 's to get its parent. - /// - internal static readonly DependencyProperty NavigationParentProperty = DependencyProperty.RegisterAttached( - nameof(NavigationParent), typeof(INavigation), typeof(NavigationBase), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits)); - - /// - public ObservableCollection Items - { - get => GetValue(ItemsProperty) as ObservableCollection; - set => SetValue(ItemsProperty, value); - } - - /// - public ObservableCollection Footer - { - get => GetValue(FooterProperty) as ObservableCollection; - set => SetValue(FooterProperty, value); - } - - /// - [Obsolete("Work in progress.")] - public Orientation Orientation - { - get => (Orientation)GetValue(OrientationProperty); - set => SetValue(OrientationProperty, value); - } - - /// - public int TransitionDuration - { - get => (int)GetValue(TransitionDurationProperty); - set => SetValue(TransitionDurationProperty, value); - } - - /// - public Animations.TransitionType TransitionType - { - get => (Animations.TransitionType)GetValue(TransitionTypeProperty); - set => SetValue(TransitionTypeProperty, value); - } - - /// - public Frame? Frame - { - get => GetValue(FrameProperty) as Frame; - set => SetValue(FrameProperty, value); - } - - /// - public int SelectedPageIndex - { - get => (int)GetValue(SelectedPageIndexProperty); - set => SetValue(SelectedPageIndexProperty, value); - } - - /// - public bool Precache - { - get => (bool)GetValue(PrecacheProperty); - set => SetValue(PrecacheProperty, value); - } - - internal INavigation NavigationParent - { - get => (INavigation)GetValue(NavigationParentProperty); - private set => SetValue(NavigationParentProperty, value); - } - - #region Events - - /// - /// Event triggered when navigate to page. - /// - public static readonly RoutedEvent NavigatedEvent = EventManager.RegisterRoutedEvent(nameof(Navigated), - RoutingStrategy.Bubble, typeof(RoutedNavigationEvent), typeof(NavigationBase)); - - /// - public event RoutedNavigationEvent Navigated - { - add => AddHandler(NavigatedEvent, value); - remove => RemoveHandler(NavigatedEvent, value); - } - - /// - /// Event triggered when navigate to the next page. - /// - public static readonly RoutedEvent NavigatedForwardEvent = - EventManager.RegisterRoutedEvent(nameof(NavigatedForward), RoutingStrategy.Bubble, - typeof(RoutedNavigationEvent), typeof(NavigationBase)); - - /// - public event RoutedNavigationEvent NavigatedForward - { - add => AddHandler(NavigatedForwardEvent, value); - remove => RemoveHandler(NavigatedForwardEvent, value); - } - - /// - /// Event triggered when navigate to the previous page. - /// - public static readonly RoutedEvent NavigatedBackwardEvent = - EventManager.RegisterRoutedEvent(nameof(NavigatedBackward), RoutingStrategy.Bubble, - typeof(RoutedNavigationEvent), typeof(NavigationBase)); - - /// - public event RoutedNavigationEvent NavigatedBackward - { - add => AddHandler(NavigatedBackwardEvent, value); - remove => RemoveHandler(NavigatedBackwardEvent, value); - } - - #endregion - - /// - public IPageService? PageService - { - get => _navigationService?.GetService(); - set => _navigationService?.SetService(value); - } - - /// - public int PreviousPageIndex => _navigationService?.GetPreviousId() ?? 0; - - /// - public bool CanGoBack => _navigationService is not null && _navigationService.CanGoBack; - - /// - public INavigationItem? Current { get; internal set; } - - /// - /// Static constructor overriding default properties. - /// - static NavigationBase() - { - KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata( - typeof(NavigationBase), - new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained)); - - KeyboardNavigation.TabNavigationProperty.OverrideMetadata( - typeof(NavigationBase), - new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); - } - - /// - /// Prepares base navigation properties. - /// - protected NavigationBase() - { - Current = (INavigationItem)null; - - // Prepare individual collections for this navigation - Items ??= new ObservableCollection(); - Footer ??= new ObservableCollection(); - - _navigationService = new Wpf.Ui.Services.Internal.NavigationService(); - _navigationService.TransitionDuration = TransitionDuration; - _navigationService.TransitionType = TransitionType; - - if (Frame != null) - _navigationService.SetFrame(Frame); - - // Let the NavigationItem children be able to get me. - NavigationParent = this; - - // Loaded does not have override - Loaded += OnLoaded; - } - - public bool NavigateBack() - { - if (_navigationService is null) return false; - - if (!_navigationService.NavigateBack()) - return false; - - NavigateInternal(0, true); - - return true; - } - - /// - public bool Navigate(Type pageType) - { - return Navigate(pageType, null); - } - - /// - public bool Navigate(Type pageType, object? dataContext) - { - if (!_navigationService.Navigate(pageType, dataContext)) - return false; - - NavigateInternal(0, true); - - return true; - } - - /// - public bool Navigate(string pageTag) - { - return Navigate(pageTag, null); - } - - /// - public bool Navigate(string pageTag, object? dataContext) - { - if (!_navigationService.Navigate(pageTag, dataContext)) - return false; - - NavigateInternal(0, true); - - return true; - } - - - /// - public bool Navigate(int pageId) - { - return Navigate(pageId, null); - } - - /// - public bool Navigate(int pageId, object? dataContext) - { - if (_navigationService != null) - if (!_navigationService.Navigate(pageId, dataContext)) - return false; - - NavigateInternal(-1, true); - - return true; - } - - /// - public bool NavigateExternal(object frameworkElement) - { - return NavigateExternal(frameworkElement, null); - } - - /// - public bool NavigateExternal(object frameworkElement, object? dataContext) - { - if (_navigationService != null) - if (!_navigationService.NavigateExternal(frameworkElement, dataContext)) - return false; - - NavigateInternal(-1, true); - - return true; - } - - /// - public bool NavigateExternal(Uri absolutePageUri) - { - return NavigateExternal(absolutePageUri, null); - } - - /// - public bool NavigateExternal(Uri absolutePageUri, object? dataContext) - { - if (_navigationService != null) - if (!_navigationService.NavigateExternal(absolutePageUri, dataContext)) - return false; - - NavigateInternal(-1, false); - - return true; - } - - /// - public void SetCurrentContext(object dataContext) - { - if (Frame?.Content is not FrameworkElement) - return; - - ((FrameworkElement)Frame.Content).DataContext = dataContext; - - if (dataContext is IViewModel) - ((IViewModel)dataContext).OnMounted(((FrameworkElement)Frame.Content)); - } - - /// - public bool SetContext(string pageTag, object dataContext) - { - if (_navigationService == null) - return false; - - return _navigationService.SetContext(pageTag, dataContext); - } - - /// - public bool SetContext(int pageId, object dataContext) - { - if (_navigationService == null) - return false; - - return _navigationService.SetContext(pageId, dataContext); - } - - /// - public void Flush() - { - Items.Clear(); - Footer.Clear(); - - Current = (INavigationItem)null; - } - - /// - public void ClearCache() - { - if (_navigationService == null) - return; - - _navigationService.ClearCache(); - } - - /// - /// Updates property and modifies Active attribute of navigation items. - /// - private void UpdateItems() - { - var currentTag = _navigationService?.GetCurrentTag() ?? String.Empty; - - foreach (var singleNavigationControl in Items) - { - if (singleNavigationControl is not INavigationItem) - continue; - - if (((INavigationItem)singleNavigationControl).PageTag == currentTag) - { - ((INavigationItem)singleNavigationControl).IsActive = true; - Current = (INavigationItem)singleNavigationControl; - } - else - { - ((INavigationItem)singleNavigationControl).IsActive = false; - } - } - - foreach (var singleNavigationControl in Footer) - { - if (singleNavigationControl is not INavigationItem) - continue; - - if (((INavigationItem)singleNavigationControl).PageTag == currentTag) - { - ((INavigationItem)singleNavigationControl).IsActive = true; - Current = (INavigationItem)singleNavigationControl; - } - else - { - ((INavigationItem)singleNavigationControl).IsActive = false; - } - } - } - - /// - /// This virtual method is called when is loaded. - /// - protected virtual void OnLoaded(object sender, RoutedEventArgs e) - { - UpdateServiceItems(); - - if (PageService == null && Frame != null && SelectedPageIndex > -1) - Navigate(SelectedPageIndex); - - // If we are using the MVVM model, do not use the cache. - if (Precache) - { - if (PageService != null) - throw new InvalidOperationException("The cache cannot be used if you are using IPageService."); - - // TODO: Precache - //await PrecacheInstances(); - } - } - - /// - protected override void OnKeyDown(KeyEventArgs e) - { - // We handle Left/Up/Right/Down keys for keyboard navigation only, - // so no modifiers are needed. - if (Keyboard.Modifiers is not ModifierKeys.None) - return; - - // For most cases, this method do nothing because it does not receive focus by default. - // But if someone set focus to it, the key handling can move the focus to its navigation children. - switch (e.Key) - { - // We use Direction Left/Up/Right/Down instead of Previous/Next to make sure - // that the KeyboardNavigation.DirectionalNavigation property works correctly. - case Key.Left: - MoveFocus(this, FocusNavigationDirection.Left); - e.Handled = true; - break; - - case Key.Up: - MoveFocus(this, FocusNavigationDirection.Up); - e.Handled = true; - break; - - case Key.Right: - MoveFocus(this, FocusNavigationDirection.Right); - e.Handled = true; - break; - - case Key.Down: - MoveFocus(this, FocusNavigationDirection.Down); - e.Handled = true; - break; - } - - if (!e.Handled) - base.OnKeyDown(e); - - static void MoveFocus(FrameworkElement element, FocusNavigationDirection direction) - { - var request = new TraversalRequest(direction); - element.MoveFocus(request); - } - } - - /// - /// This virtual method is called during any navigation and it raises the . - /// - protected virtual void OnNavigated() - { - var newEvent = new RoutedNavigationEventArgs(NavigatedEvent, this, Current); - RaiseEvent(newEvent); - } - - /// - /// This virtual method is called during forward navigation and it raises the . - /// - protected virtual void OnNavigatedForward() - { - var newEvent = new RoutedNavigationEventArgs(NavigatedForwardEvent, this, Current); - RaiseEvent(newEvent); - } - - /// - /// This virtual method is called during backward navigation and it raises the . - /// - protected virtual void OnNavigatedBackward() - { - var newEvent = new RoutedNavigationEventArgs(NavigatedBackwardEvent, this, Current); - RaiseEvent(newEvent); - } - - /// - /// This virtual method is called when one of the navigation items is clicked. - /// - protected virtual void OnNavigationItemClicked(object sender, RoutedEventArgs e) - { - if (sender is not INavigationItem navigationItem) - return; - - if (navigationItem.AbsolutePageSource == null && navigationItem.PageType == null) - return; - - if (PageService == null) - { - Navigate(navigationItem.PageTag); - - return; - } - - if (navigationItem.PageType == null) - throw new InvalidOperationException("When navigating through the IPageService, the navigated page type must be defined the INavigationItem.PageType."); - - Navigate(navigationItem.PageType); - } - - /// - /// This virtual method is called when something is added, deleted or changed in or . - /// - protected virtual void OnNavigationCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - if (IsLoaded) - UpdateServiceItems(); - - if (e.NewItems != null) - foreach (var addedItem in e.NewItems) - if (addedItem is INavigationItem) - { - ((INavigationItem)addedItem).Click -= OnNavigationItemClicked; // Unsafe - Remove duplicates - ((INavigationItem)addedItem).Click += OnNavigationItemClicked; - } - - if (e.OldItems == null) - return; - - foreach (var deletedItem in e.OldItems) - ((INavigationItem)deletedItem).Click -= OnNavigationItemClicked; - } - - /// - /// Triggered when is changed. - /// - private static void OnItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationBase navigationBase) - return; - - navigationBase.InitializeServiceItems(); - - if (e.NewValue is not ObservableCollection itemsCollection) - return; - - itemsCollection.CollectionChanged += navigationBase.OnNavigationCollectionChanged; - } - - /// - /// Triggered when is changed. - /// - private static void OnFooterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationBase navigationBase) - return; - - navigationBase.InitializeServiceItems(); - - if (e.NewValue is not ObservableCollection itemsCollection) - return; - - itemsCollection.CollectionChanged += navigationBase.OnNavigationCollectionChanged; - } - - /// - /// This virtual method is called when one of the navigation items is clicked. - /// - protected virtual void OnFrameChanged(Frame frame) - { - _navigationService?.SetFrame(frame); - } - - /// - /// Triggered when is changed. - /// - private static void OnFrameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationBase navigationBase || e.NewValue is not Frame frame) - return; - - navigationBase.OnFrameChanged(frame); - } - - private static void OnTransitionDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationBase navigation) - return; - - if (navigation._navigationService == null) - return; - - navigation._navigationService.TransitionDuration = (int)e.NewValue; - } - - private static void OnTransitionTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationBase navigation) - return; - - if (navigation._navigationService == null) - return; - - navigation._navigationService.TransitionType = (Animations.TransitionType)e.NewValue; - } - - /// - /// Gets the parent view for its children. - /// - /// - /// - internal static NavigationBase? GetNavigationParent(T navigationItem) - where T : DependencyObject, INavigationItem - { - return (NavigationBase?)navigationItem.GetValue(NavigationParentProperty); - } - - private void InitializeServiceItems() - { - var navigationItems = GetValue(ItemsProperty) as ObservableCollection ?? new ObservableCollection { }; - var navigationFooter = GetValue(FooterProperty) as ObservableCollection ?? new ObservableCollection { }; - - foreach (var addedItem in navigationItems) - if (addedItem is INavigationItem) - { - ((INavigationItem)addedItem).Click -= OnNavigationItemClicked; // Unsafe - Remove duplicates - ((INavigationItem)addedItem).Click += OnNavigationItemClicked; - } - - foreach (var addedItem in navigationFooter) - if (addedItem is INavigationItem) - { - ((INavigationItem)addedItem).Click -= OnNavigationItemClicked; // Unsafe - Remove duplicates - ((INavigationItem)addedItem).Click += OnNavigationItemClicked; - } - - UpdateServiceItems(); - } - - private void UpdateServiceItems() - { - var navigationItems = GetValue(ItemsProperty) as ObservableCollection ?? new ObservableCollection { }; - var navigationFooter = GetValue(FooterProperty) as ObservableCollection ?? new ObservableCollection { }; - - if (_navigationService != null) - _navigationService.UpdateItems(navigationItems, navigationFooter); - } - - private void NavigateInternal(int arg, bool updateItems) - { - SelectedPageIndex = _navigationService?.GetCurrentId() ?? +arg; - - if (updateItems) - UpdateItems(); - - OnNavigated(); - - if (SelectedPageIndex > (_navigationService?.GetPreviousId() ?? +arg)) - OnNavigatedForward(); - else - OnNavigatedBackward(); - } -} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationHeader.bmp b/src/Wpf.Ui/Controls/Navigation/NavigationHeader.bmp deleted file mode 100644 index 82af3f66e..000000000 Binary files a/src/Wpf.Ui/Controls/Navigation/NavigationHeader.bmp and /dev/null differ diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.bmp b/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.bmp deleted file mode 100644 index a2d86c514..000000000 Binary files a/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.bmp and /dev/null differ diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.cs b/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.cs deleted file mode 100644 index 3f498c6fe..000000000 --- a/src/Wpf.Ui/Controls/Navigation/NavigationSeparator.cs +++ /dev/null @@ -1,16 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.ComponentModel; -using System.Drawing; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Controls.Navigation; - -[ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationSeparator), "NavigationSeparator.bmp")] -public class NavigationSeparator : System.Windows.Controls.Separator, INavigationControl -{ -} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.Base.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.Base.cs new file mode 100644 index 000000000..d3d5bf6f5 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.Base.cs @@ -0,0 +1,296 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows; +using Wpf.Ui.Controls.Interfaces; + +namespace Wpf.Ui.Controls.Navigation; + +// 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 +{ + private ObservableCollection _autoSuggestBoxItems; + + /// + public INavigationViewItem SelectedItem { get; private set; } + + /// + /// Static constructor which overrides default property metadata. + /// + static NavigationView() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NavigationView), new FrameworkPropertyMetadata(typeof(NavigationView))); + } + + public NavigationView() + { + SelectedItem = null; + NavigationParent = this; + + SetValue(MenuItemsProperty, + new ObservableCollection()); + + SetValue(FooterMenuItemsProperty, + new ObservableCollection()); + + Loaded += OnLoaded; + Unloaded += OnUnloaded; + SizeChanged += OnSizeChanged; + } + + /// + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + + if (ItemTemplate != null) + UpdateMenuItemsTemplate(); + + if (Header is NavigationViewBreadcrumb navigationViewBreadcrumb) + navigationViewBreadcrumb.NavigationView = this; + + if (MenuItems?.Count > 0) + OnMenuItemsChanged(); + + if (MenuItemsSource is IList menuItemsSourceList) + { + if (MenuItems != null) + MenuItems = null; + + MenuItems = menuItemsSourceList; + } + + if (FooterMenuItems?.Count > 0) + OnFooterMenuItemsChanged(); + + UpdateAutoSuggestBoxSuggestions(); + + UpdateSelectionForMenuItems(); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + // TODO: Refresh + } + + /// + /// This virtual method is called when this element is detached form a loaded tree. + /// + protected virtual void OnUnloaded(object sender, RoutedEventArgs e) + { + if (MenuItems is INotifyCollectionChanged menuItemsCollection) + menuItemsCollection.CollectionChanged -= OnMenuItemsCollectionChanged; + + if (FooterMenuItems is INotifyCollectionChanged footerMenuItemsCollection) + footerMenuItemsCollection.CollectionChanged -= OnFooterMenuItemsCollectionChanged; + } + + /// + /// This virtual method is called when ActualWidth or ActualHeight (or both) changed. + /// + protected virtual void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + // TODO: Update reveal + } + + /// + /// This virtual method is called when is clicked. + /// + protected virtual void OnBackButtonClick(object sender, RoutedEventArgs e) + { + GoBack(); + } + + /// + /// This virtual method is called when is clicked. + /// + protected virtual void OnToggleButtonClick(object sender, RoutedEventArgs e) + { +#if DEBUG + System.Diagnostics.Debug.WriteLine("Toggle"); +#endif + } + + /// + /// This virtual method is called when source of the menu items is changed. + /// + protected virtual void OnMenuItemsChanged() + { + InvalidateArrange(); + InvalidateVisual(); + UpdateLayout(); + + UpdateAutoSuggestBoxSuggestions(); + + UpdateSelectionForMenuItems(); + } + + /// + /// This virtual method is called when source of the footer menu items is changed. + /// + protected virtual void OnFooterMenuItemsChanged() + { + UpdateAutoSuggestBoxSuggestions(); + + UpdateSelectionForMenuItems(); + } + + /// + /// This virtual method is called when is changed. + /// + protected virtual void OnPaneDisplayModeChanged() + { + switch (PaneDisplayMode) + { + case NavigationViewPaneDisplayMode.LeftFluent: + IsBackButtonVisible = NavigationViewBackButtonVisible.Collapsed; + IsPaneToggleVisible = false; + break; + } + } + + /// + /// This virtual method is called when is changed. + /// + protected virtual void OnItemTemplateChanged() + { + UpdateMenuItemsTemplate(); + } + + internal void OnNavigationViewItemClick(NavigationViewItem navigationViewItem) + { + OnItemInvoked(); + + NavigateInternal(navigationViewItem, null, true); + } + + private void UpdateMenuItemsTemplate() + { + if (MenuItems is IEnumerable enumerableItemsSource) + foreach (var singleMenuItem in enumerableItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (ItemTemplate != null && singleNavigationViewItem.Template != ItemTemplate) + singleNavigationViewItem.Template = ItemTemplate; + + if (FooterMenuItems is IEnumerable enumerableFooterItemsSource) + foreach (var singleMenuItem in enumerableFooterItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (ItemTemplate != null && singleNavigationViewItem.Template != ItemTemplate) + singleNavigationViewItem.Template = ItemTemplate; + } + + private void UpdateAutoSuggestBoxSuggestions() + { + if (AutoSuggestBox == null) + return; + + _autoSuggestBoxItems = new ObservableCollection(); + + if (MenuItems is IEnumerable enumerableItemsSource) + foreach (var singleMenuItem in enumerableItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + { + if (singleNavigationViewItem.Content is string content) + _autoSuggestBoxItems.Add(content); + + if (singleNavigationViewItem.MenuItems?.Count > 0) + foreach (var subMenuItem in singleNavigationViewItem.MenuItems) + if (subMenuItem is NavigationViewItem { Content: string subContent, TargetPageType: not null }) + _autoSuggestBoxItems.Add(subContent); + } + + if (FooterMenuItems is IEnumerable enumerableFooterItemsSource) + foreach (var singleMenuItem in enumerableFooterItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + { + if (singleNavigationViewItem.Content is string content) + _autoSuggestBoxItems.Add(content); + + if (singleNavigationViewItem.MenuItems?.Count > 0) + foreach (var subMenuItem in singleNavigationViewItem.MenuItems) + if (subMenuItem is NavigationViewItem { Content: string subContent, TargetPageType: not null }) + _autoSuggestBoxItems.Add(subContent); + } + + AutoSuggestBox.ItemsSource = _autoSuggestBoxItems; + + AutoSuggestBox.SuggestionChosen -= AutoSuggestBoxOnSuggestionChosen; + AutoSuggestBox.SuggestionChosen += AutoSuggestBoxOnSuggestionChosen; + } + + /// + /// Navigate to the page after its name is selected in . + /// + private void AutoSuggestBoxOnSuggestionChosen(object sender, RoutedEventArgs e) + { + if (sender is not Controls.AutoSuggestBox autoSuggestBox) + return; + + var selectedSuggestBoxItem = autoSuggestBox.ChosenSuggestion; + + if (selectedSuggestBoxItem == String.Empty) + return; + + if (MenuItems is IEnumerable enumerableItemsSource) + foreach (var singleMenuItem in enumerableItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + { + if (singleNavigationViewItem.Content is string content && content == selectedSuggestBoxItem) + { + NavigateInternal(singleNavigationViewItem, null, true); + Focus(); + + return; + } + + if (singleNavigationViewItem.MenuItems?.Count > 0) + foreach (var subMenuItem in singleNavigationViewItem.MenuItems) + if (subMenuItem is NavigationViewItem { Content: string subContent } subMenuNavigationViewItem && subContent == selectedSuggestBoxItem) + { + NavigateInternal(subMenuNavigationViewItem, null, true); + Focus(); + + return; + } + } + + if (FooterMenuItems is IEnumerable enumerableFooterItemsSource) + foreach (var singleMenuItem in enumerableFooterItemsSource) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + { + if (singleNavigationViewItem.Content is string content && content == selectedSuggestBoxItem) + { + NavigateInternal(singleNavigationViewItem, null, true); + Focus(); + + return; + } + + if (singleNavigationViewItem.MenuItems?.Count > 0) + foreach (var subMenuItem in singleNavigationViewItem.MenuItems) + if (subMenuItem is NavigationViewItem { Content: string subContent } subMenuNavigationViewItem && subContent == selectedSuggestBoxItem) + { + NavigateInternal(subMenuNavigationViewItem, null, true); + Focus(); + + return; + } + } + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.Events.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.Events.cs new file mode 100644 index 000000000..07e01bc64 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.Events.cs @@ -0,0 +1,119 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows; + +namespace Wpf.Ui.Controls.Navigation; + +public partial class NavigationView +{ + /// + /// Property for . + /// + public static readonly RoutedEvent PaneOpenedEvent = EventManager.RegisterRoutedEvent(nameof(PaneOpened), + RoutingStrategy.Bubble, typeof(NavigationViewEvent), typeof(NavigationView)); + + /// + /// Property for . + /// + public static readonly RoutedEvent PaneClosedEvent = EventManager.RegisterRoutedEvent(nameof(PaneClosed), + RoutingStrategy.Bubble, typeof(NavigationViewEvent), typeof(NavigationView)); + + /// + /// Property for . + /// + public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(nameof(SelectionChanged), + RoutingStrategy.Bubble, typeof(NavigationViewEvent), typeof(NavigationView)); + + /// + /// Property for . + /// + public static readonly RoutedEvent ItemInvokedEvent = EventManager.RegisterRoutedEvent(nameof(ItemInvoked), + RoutingStrategy.Bubble, typeof(NavigationViewEvent), typeof(NavigationView)); + + /// + /// Property for . + /// + public static readonly RoutedEvent BackRequestedEvent = EventManager.RegisterRoutedEvent(nameof(BackRequested), + RoutingStrategy.Bubble, typeof(NavigationViewEvent), typeof(NavigationView)); + + /// + public event NavigationViewEvent PaneOpened + { + add => AddHandler(PaneOpenedEvent, value); + remove => RemoveHandler(PaneOpenedEvent, value); + } + + /// + public event NavigationViewEvent PaneClosed + { + add => AddHandler(PaneClosedEvent, value); + remove => RemoveHandler(PaneClosedEvent, value); + } + + /// + public event NavigationViewEvent SelectionChanged + { + add => AddHandler(SelectionChangedEvent, value); + remove => RemoveHandler(SelectionChangedEvent, value); + } + + /// + public event NavigationViewEvent ItemInvoked + { + add => AddHandler(ItemInvokedEvent, value); + remove => RemoveHandler(ItemInvokedEvent, value); + } + + /// + public event NavigationViewEvent BackRequested + { + add => AddHandler(BackRequestedEvent, value); + remove => RemoveHandler(BackRequestedEvent, value); + } + + /// + /// Raises the pane opened event. + /// + protected virtual void OnPaneOpened() + { + RaiseEvent(new RoutedEventArgs(PaneOpenedEvent)); + } + + /// + /// Raises the pane closed event. + /// + protected virtual void OnPaneClosed() + { + RaiseEvent(new RoutedEventArgs(PaneClosedEvent)); + } + + /// + /// Raises the selection changed event. + /// + protected virtual void OnSelectionChanged() + { + RaiseEvent(new RoutedEventArgs(SelectionChangedEvent)); + } + + /// + /// Raises the item invoked event. + /// + protected virtual void OnItemInvoked() + { + RaiseEvent(new RoutedEventArgs(ItemInvokedEvent)); + } + + /// + /// Raises the back requested event. + /// + protected virtual void OnBackRequested() + { + RaiseEvent(new RoutedEventArgs(BackRequestedEvent)); + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.Navigation.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.Navigation.cs new file mode 100644 index 000000000..042b64495 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.Navigation.cs @@ -0,0 +1,352 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Navigation; +using Wpf.Ui.Animations; +using Wpf.Ui.Common.Interfaces; +using Wpf.Ui.Controls.Interfaces; +using Wpf.Ui.Mvvm.Contracts; + +namespace Wpf.Ui.Controls.Navigation; + +public partial class NavigationView +{ + private IServiceProvider? _serviceProvider = null; + + private IPageService? _pageService = null; + + private readonly List _journal = new(); + + private int _currentIndexInJournal = 0; + + /// + public bool CanGoBack + => _journal.Count > 1; + + /// + public void SetPageService(IPageService pageService) + => _pageService = pageService; + + /// + public void SetServiceProvider(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider; + + /// + public bool Navigate(Type pageType) + => Navigate(pageType, null!); + + /// + public bool Navigate(Type pageType, object dataContext) + { + if (MenuItems is IEnumerable enumerableMenuItems) + foreach (var singleMenuItem in enumerableMenuItems) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (singleNavigationViewItem.TargetPageType != null && singleNavigationViewItem.TargetPageType == pageType) + return NavigateInternal(singleNavigationViewItem, dataContext, true); + + if (FooterMenuItems is IEnumerable enumerableFooterMenuItems) + foreach (var singleMenuItem in enumerableFooterMenuItems) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (singleNavigationViewItem.TargetPageType != null && singleNavigationViewItem.TargetPageType == pageType) + return NavigateInternal(singleNavigationViewItem, dataContext, true); + + return false; + } + + /// + public bool Navigate(string pageIdOrTargetTag) + => Navigate(pageIdOrTargetTag, null!); + + /// + public bool Navigate(string pageIdOrTargetTag, object dataContext) + { + if (MenuItems is IEnumerable enumerableMenuItems) + foreach (var singleMenuItem in enumerableMenuItems) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (singleNavigationViewItem.Id == pageIdOrTargetTag || singleNavigationViewItem?.TargetPageTag == pageIdOrTargetTag) + return NavigateInternal(singleNavigationViewItem, dataContext, true); + + if (FooterMenuItems is IEnumerable enumerableFooterMenuItems) + foreach (var singleMenuItem in enumerableFooterMenuItems) + if (singleMenuItem is NavigationViewItem singleNavigationViewItem) + if (singleNavigationViewItem.Id == pageIdOrTargetTag || + singleNavigationViewItem?.TargetPageTag == pageIdOrTargetTag) + return NavigateInternal(singleNavigationViewItem, dataContext, true); + + return false; + } + + /// + public bool ReplaceContent(Type pageTypeToEmbed) + { + if (pageTypeToEmbed == null) + return false; + + if (_serviceProvider != null) + { + UpdateContent(_serviceProvider.GetService(pageTypeToEmbed) ?? null!, null!); + + return true; + } + + if (_pageService == null) + return false; + + UpdateContent(_pageService.GetPage(pageTypeToEmbed) ?? null!, null!); + + return true; + } + + /// + public bool ReplaceContent(UIElement pageInstanceToEmbed) + { + return ReplaceContent(pageInstanceToEmbed, null!); + } + + /// + public bool ReplaceContent(UIElement pageInstanceToEmbed, object dataContext) + { + UpdateContent(pageInstanceToEmbed, dataContext); + + return true; + } + + /// + public bool GoForward() + { + if (_journal.Count <= 1) + return false; + + _currentIndexInJournal += 1; + + if (_currentIndexInJournal > _journal.Count - 1) + return false; + + return Navigate(_journal[_currentIndexInJournal], null!); + } + + /// + public bool GoBack() + { + if (_journal.Count <= 1) + return false; + + _currentIndexInJournal -= 1; + + if (_currentIndexInJournal < 0) + return false; + + return Navigate(_journal[_currentIndexInJournal], null!); + } + + /// + public void ClearJournal() + { + _journal.Clear(); + _currentIndexInJournal = 0; + } + + private bool NavigateInternal(INavigationViewItem viewItem, object? dataContext, bool notifyAboutUpdate) + { + if (viewItem == SelectedItem) + return false; + + UpdateJournal(viewItem); + + IsBackEnabled = _journal.Count > 0; + +#if DEBUG + System.Diagnostics.Debug.WriteLine($"DEBUG | {viewItem.Id} - {viewItem.TargetPageTag ?? "NO_TAG"} | NAVIGATED"); +#endif + + RenderSelectedItemContent(viewItem, dataContext); + + if (!notifyAboutUpdate) + return true; + + SelectedItem = viewItem; + + UpdateSelectionForMenuItems(); + OnSelectionChanged(); + + return true; + } + + private void UpdateJournal(INavigationViewItem viewItem) + { +#if DEBUG + System.Diagnostics.Debug.WriteLine($"JOURNAL INDEX {_currentIndexInJournal}"); + if (_journal.Count > 0) + System.Diagnostics.Debug.WriteLine($"JOURNAL LAST ELEMENT {_journal[_journal.Count - 1]}"); +#endif + + if (_journal.Count == 0) + { + _currentIndexInJournal = 0; + _journal.Add(viewItem.Id); + + return; + } + + // TODO: Fix at last position + + if (_currentIndexInJournal == _journal.Count - 1) + { + _journal.Add(viewItem.Id); + + _currentIndexInJournal++; + } + + if (_journal.Count > 20) + _journal.RemoveAt(0); + } + + private void RenderSelectedItemContent(INavigationViewItem viewItem, object? dataContext) + { + if (_serviceProvider != null) + { + if (viewItem.TargetPageType == null) + return; + + UpdateContent(_serviceProvider.GetService(viewItem.TargetPageType) ?? null!, dataContext); + + return; + } + + if (_pageService != null) + { + if (viewItem.TargetPageType == null) + return; + + UpdateContent(_pageService.GetPage(viewItem.TargetPageType) ?? null!, dataContext); + + return; + } + + if (viewItem.TargetPageType == null) + return; + + var pageInstance = NavigationViewActivator.CreateInstance(viewItem.TargetPageType); + + if (pageInstance == null) + return; + + UpdateContent(pageInstance, dataContext); + } + + private void UpdateContent(object? content, object? dataContext) + { + if (NavigationViewContentPresenter == null) + return; + + NotifyContentAboutNavigatingFrom(NavigationViewContentPresenter?.Content ?? null); + + if (dataContext != null && content is FrameworkElement frameworkViewContent) + frameworkViewContent.DataContext = dataContext; + + NavigationViewContentPresenter!.Navigate(content); + } + + protected virtual void OnNavigationViewContentPresenterNavigated(object sender, NavigationEventArgs e) + { + if (sender is not NavigationViewContentPresenter contentPresenter) + return; + + NotifyContentAboutNavigatingTo(contentPresenter?.Content ?? null); + + ApplyTransitionEffectToNavigatedPage(contentPresenter); + } + + private void NotifyContentAboutNavigatingFrom(object? content) + { + if (content is INavigationAware navigationAwareNavigationContent) + navigationAwareNavigationContent.OnNavigatedFrom(); + + if (content is INavigableView navigableView && navigableView.ViewModel is INavigationAware navigationAwareNavigableViewViewModel) + navigationAwareNavigableViewViewModel.OnNavigatedFrom(); + + if (content is FrameworkElement { DataContext: INavigationAware navigationAwareCurrentContent }) + navigationAwareCurrentContent.OnNavigatedFrom(); + } + + private void NotifyContentAboutNavigatingTo(object? content) + { + if (content is INavigationAware navigationAwareNavigationContent) + navigationAwareNavigationContent.OnNavigatedTo(); + + if (content is INavigableView navigableView && navigableView.ViewModel is INavigationAware navigationAwareNavigableViewViewModel) + navigationAwareNavigableViewViewModel.OnNavigatedTo(); + + if (content is FrameworkElement { DataContext: INavigationAware navigationAwareCurrentContent }) + navigationAwareCurrentContent.OnNavigatedTo(); + } + + private void ApplyTransitionEffectToNavigatedPage(NavigationViewContentPresenter contentPresenter) + { + if (TransitionDuration < 1) + return; + + if (contentPresenter.Content == null) + return; + + Transitions.ApplyTransition(contentPresenter.Content, TransitionType, TransitionDuration); + } + + private void UpdateSelectionForMenuItems() + { + if (MenuItems is IEnumerable enumerableMenuItems) + { + foreach (var singleMenuItem in enumerableMenuItems) + { + if (singleMenuItem is not NavigationViewItem navigationViewItem) + continue; + + navigationViewItem.IsActive = navigationViewItem == SelectedItem; + + if (navigationViewItem.MenuItems is IEnumerable enumerableSubMenuItems) + { + foreach (var singleSubMenuItem in enumerableSubMenuItems) + { + if (singleSubMenuItem is not NavigationViewItem navigationViewSubItem) + continue; + + navigationViewSubItem.IsActive = navigationViewSubItem == SelectedItem; + } + } + } + } + + if (FooterMenuItems is IEnumerable enumerableFooterMenuItems) + { + foreach (var singleFooterMenuItem in enumerableFooterMenuItems) + { + if (singleFooterMenuItem is not NavigationViewItem navigationViewItem) + continue; + + navigationViewItem.IsActive = navigationViewItem == SelectedItem; + + if (navigationViewItem.MenuItems is IEnumerable enumerableSubMenuItems) + { + foreach (var singleSubMenuItem in enumerableSubMenuItems) + { + if (singleSubMenuItem is not NavigationViewItem navigationViewSubItem) + continue; + + navigationViewSubItem.IsActive = navigationViewSubItem == SelectedItem; + } + } + } + } + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.Parent.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.Parent.cs new file mode 100644 index 000000000..b31f439f3 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.Parent.cs @@ -0,0 +1,45 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows; +using Wpf.Ui.Controls.Interfaces; + +namespace Wpf.Ui.Controls.Navigation; + +public partial class NavigationView +{ + /// + /// Attached property for 's to get its parent. + /// + internal static readonly DependencyProperty NavigationParentProperty = DependencyProperty.RegisterAttached( + nameof(NavigationParent), typeof(INavigationView), typeof(INavigationView), + new FrameworkPropertyMetadata(((INavigationView)null!), FrameworkPropertyMetadataOptions.Inherits)); + + /// + /// + /// + internal INavigationView NavigationParent + { + get => (INavigationView)GetValue(NavigationParentProperty); + private set => SetValue(NavigationParentProperty, value); + } + + /// + /// Gets the parent view for its children. + /// + /// + /// Instance of the or . + internal static NavigationView GetNavigationParent(T navigationItem) + where T : DependencyObject, INavigationViewItem + { + if (navigationItem.GetValue(NavigationParentProperty) is NavigationView navigationView) + return navigationView; + + return null; + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.Properties.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.Properties.cs new file mode 100644 index 000000000..f58e20317 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.Properties.cs @@ -0,0 +1,425 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using Wpf.Ui.Animations; + +namespace Wpf.Ui.Controls.Navigation; + +public partial class NavigationView +{ + /// + /// Property for . + /// + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(nameof(Header), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty HeaderVisibilityProperty = DependencyProperty.Register(nameof(HeaderVisibility), + typeof(Visibility), typeof(NavigationView), + new FrameworkPropertyMetadata(System.Windows.Visibility.Visible)); + + /// + /// Property for . + /// + public static readonly DependencyProperty AlwaysShowHeaderProperty = DependencyProperty.Register(nameof(AlwaysShowHeader), + typeof(bool), typeof(NavigationView), + new FrameworkPropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(nameof(MenuItems), + typeof(IList), typeof(NavigationView), + new PropertyMetadata(OnMenuItemsPropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty MenuItemsSourceProperty = DependencyProperty.Register(nameof(MenuItemsSource), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!), OnMenuItemsSourcePropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty FooterMenuItemsProperty = DependencyProperty.Register(nameof(FooterMenuItemsProperty), + typeof(IList), typeof(NavigationView), + new PropertyMetadata(OnFooterMenuItemsPropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty FooterMenuItemsSourceProperty = DependencyProperty.Register(nameof(FooterMenuItemsSource), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!), OnFooterMenuItemsSourcePropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty ContentOverlayProperty = DependencyProperty.Register(nameof(ContentOverlay), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsBackEnabledProperty = DependencyProperty.Register(nameof(IsBackEnabled), + typeof(bool), typeof(NavigationView), + new FrameworkPropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), + typeof(NavigationViewBackButtonVisible), typeof(NavigationView), + new FrameworkPropertyMetadata(NavigationViewBackButtonVisible.Auto)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsPaneToggleVisibleProperty = DependencyProperty.Register(nameof(IsPaneToggleVisible), + typeof(bool), typeof(NavigationView), + new FrameworkPropertyMetadata(true)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsPaneOpenProperty = DependencyProperty.Register(nameof(IsPaneOpen), + typeof(bool), typeof(NavigationView), + new FrameworkPropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsPaneVisibleProperty = DependencyProperty.Register(nameof(IsPaneVisible), + typeof(bool), typeof(NavigationView), + new FrameworkPropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), + typeof(double), typeof(NavigationView), + new FrameworkPropertyMetadata(0D)); + + /// + /// Property for . + /// + public static readonly DependencyProperty PaneHeaderProperty = DependencyProperty.Register(nameof(PaneHeader), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty PaneFooterProperty = DependencyProperty.Register(nameof(PaneFooter), + typeof(object), typeof(NavigationView), + new FrameworkPropertyMetadata(((object)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty PaneDisplayModeProperty = DependencyProperty.Register(nameof(PaneDisplayMode), + typeof(NavigationViewPaneDisplayMode), typeof(NavigationView), + new FrameworkPropertyMetadata(NavigationViewPaneDisplayMode.Left, OnPaneDisplayModePropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty AutoSuggestBoxProperty = DependencyProperty.Register(nameof(AutoSuggestBox), + typeof(AutoSuggestBox), typeof(NavigationView), + new FrameworkPropertyMetadata(((AutoSuggestBox)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty TitleBarProperty = DependencyProperty.Register(nameof(TitleBar), + typeof(TitleBar), typeof(NavigationView), + new FrameworkPropertyMetadata(((TitleBar)null!))); + + /// + /// Property for . + /// + public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(nameof(ItemTemplate), + typeof(ControlTemplate), typeof(NavigationView), + new FrameworkPropertyMetadata( + (ControlTemplate)null!, // default value + FrameworkPropertyMetadataOptions.AffectsMeasure, + new PropertyChangedCallback(OnItemTemplatePropertyChanged))); + + /// + /// Property for . + /// + public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.Register(nameof(TransitionDuration), + typeof(int), typeof(NavigationView), + new FrameworkPropertyMetadata(200)); + + /// + /// Property for . + /// + public static readonly DependencyProperty TransitionTypeProperty = DependencyProperty.Register(nameof(TransitionType), + typeof(TransitionType), typeof(NavigationView), + new FrameworkPropertyMetadata(TransitionType.FadeInWithSlide)); + + /// + public object Header + { + get => GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + /// + public Visibility HeaderVisibility + { + get => (Visibility)GetValue(HeaderVisibilityProperty); + set => SetValue(HeaderVisibilityProperty, value); + } + + /// + public bool AlwaysShowHeader + { + get => (bool)GetValue(AlwaysShowHeaderProperty); + set => SetValue(AlwaysShowHeaderProperty, value); + } + + /// + public IList MenuItems + { + get => (IList)GetValue(MenuItemsProperty); + set => SetValue(MenuItemsProperty, value); + } + + /// + [Bindable(true)] + public object MenuItemsSource + { + get => GetValue(MenuItemsSourceProperty); + set + { + if (value == null) + ClearValue(MenuItemsSourceProperty); + else + SetValue(MenuItemsSourceProperty, value); + } + } + + /// + public IList FooterMenuItems + { + get => (IList)GetValue(FooterMenuItemsProperty); + set => SetValue(FooterMenuItemsProperty, value); + } + + /// + [Bindable(true)] + public object FooterMenuItemsSource + { + get => GetValue(FooterMenuItemsSourceProperty); + set + { + if (value == null) + ClearValue(FooterMenuItemsSourceProperty); + else + SetValue(FooterMenuItemsSourceProperty, value); + } + } + + /// + public object ContentOverlay + { + get => GetValue(ContentOverlayProperty); + set => SetValue(ContentOverlayProperty, value); + } + + /// + public bool IsBackEnabled + { + get => (bool)GetValue(IsBackEnabledProperty); + protected set => SetValue(IsBackEnabledProperty, value); + } + + /// + public NavigationViewBackButtonVisible IsBackButtonVisible + { + get => (NavigationViewBackButtonVisible)GetValue(IsBackButtonVisibleProperty); + set => SetValue(IsBackButtonVisibleProperty, value); + } + + /// + public bool IsPaneToggleVisible + { + get => (bool)GetValue(IsPaneToggleVisibleProperty); + set => SetValue(IsPaneToggleVisibleProperty, value); + } + + /// + public bool IsPaneOpen + { + get => (bool)GetValue(IsPaneOpenProperty); + set => SetValue(IsPaneOpenProperty, value); + } + + /// + public bool IsPaneVisible + { + get => (bool)GetValue(IsPaneVisibleProperty); + set => SetValue(IsPaneVisibleProperty, value); + } + + /// + public double OpenPaneLength + { + get => (double)GetValue(OpenPaneLengthProperty); + set => SetValue(OpenPaneLengthProperty, value); + } + + /// + public object PaneHeader + { + get => GetValue(PaneHeaderProperty); + set => SetValue(PaneHeaderProperty, value); + } + + /// + public object PaneFooter + { + get => GetValue(PaneFooterProperty); + set => SetValue(PaneFooterProperty, value); + } + + /// + public NavigationViewPaneDisplayMode PaneDisplayMode + { + get => (NavigationViewPaneDisplayMode)GetValue(PaneDisplayModeProperty); + set => SetValue(PaneDisplayModeProperty, value); + } + + /// + public AutoSuggestBox AutoSuggestBox + { + get => (AutoSuggestBox)GetValue(AutoSuggestBoxProperty); + set => SetValue(AutoSuggestBoxProperty, value); + } + + /// + public TitleBar TitleBar + { + get => (TitleBar)GetValue(TitleBarProperty); + set => SetValue(TitleBarProperty, value); + } + + /// + public ControlTemplate ItemTemplate + { + get => (ControlTemplate)GetValue(ItemTemplateProperty); + set => SetValue(ItemTemplateProperty, value); + } + + /// + [Bindable(true), Category("Appearance")] + public int TransitionDuration + { + get => (int)GetValue(TransitionDurationProperty); + set => SetValue(TransitionDurationProperty, value); + } + + /// + public TransitionType TransitionType + { + get => (TransitionType)GetValue(TransitionTypeProperty); + set => SetValue(TransitionTypeProperty, value); + } + + private static void OnMenuItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + if (e.OldValue is INotifyCollectionChanged oldMenuItemsCollection) + oldMenuItemsCollection.CollectionChanged -= navigationView.OnMenuItemsCollectionChanged; + + if (e.NewValue is INotifyCollectionChanged newMenuItemsCollection) + newMenuItemsCollection.CollectionChanged += navigationView.OnMenuItemsCollectionChanged; + + navigationView.OnMenuItemsChanged(); + } + + protected virtual void OnMenuItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + } + + private static void OnMenuItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + if (e.NewValue is not IList enumerableNewValue) + return; + + if (navigationView.MenuItems != null) + navigationView.MenuItems = null; + + navigationView.MenuItems = enumerableNewValue; + } + + private static void OnFooterMenuItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + if (e.OldValue is INotifyCollectionChanged oldMenuItemsCollection) + oldMenuItemsCollection.CollectionChanged -= navigationView.OnFooterMenuItemsCollectionChanged; + + if (e.NewValue is INotifyCollectionChanged newMenuItemsCollection) + newMenuItemsCollection.CollectionChanged += navigationView.OnFooterMenuItemsCollectionChanged; + + navigationView.OnFooterMenuItemsChanged(); + } + + private static void OnFooterMenuItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + if (e.NewValue is not IList enumerableNewValue) + return; + + if (navigationView.FooterMenuItems != null) + navigationView.FooterMenuItems = null; + + navigationView.FooterMenuItems = enumerableNewValue; + } + + protected virtual void OnFooterMenuItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + } + + private static void OnPaneDisplayModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + navigationView.OnPaneDisplayModeChanged(); + } + + private static void OnItemTemplatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationView navigationView) + return; + + navigationView.OnItemTemplateChanged(); + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationView.TemplateParts.cs b/src/Wpf.Ui/Controls/Navigation/NavigationView.TemplateParts.cs new file mode 100644 index 000000000..84efd5bad --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationView.TemplateParts.cs @@ -0,0 +1,111 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows; + +namespace Wpf.Ui.Controls.Navigation; + +[TemplatePart(Name = "PART_NavigationViewContentPresenter", Type = typeof(NavigationViewContentPresenter))] +[TemplatePart(Name = "PART_MenuItemsItemsControl", Type = typeof(System.Windows.Controls.ItemsControl))] +[TemplatePart(Name = "PART_FooterMenuItemsItemsControl", Type = typeof(System.Windows.Controls.ItemsControl))] +[TemplatePart(Name = "PART_BackButton", Type = typeof(System.Windows.Controls.Button))] +[TemplatePart(Name = "PART_ToggleButton", Type = typeof(System.Windows.Controls.Button))] +public partial class NavigationView +{ + /// + /// Template element represented by the PART_MenuItemsItemsControl name. + /// + private const string TemplateElementNavigationViewContentPresenter = "PART_NavigationViewContentPresenter"; + + /// + /// Template element represented by the PART_MenuItemsItemsControl name. + /// + private const string TemplateElementMenuItemsItemsControl = "PART_MenuItemsItemsControl"; + + /// + /// Template element represented by the PART_FooterMenuItemsItemsControl name. + /// + private const string TemplateElementFooterMenuItemsItemsControl = "PART_FooterMenuItemsItemsControl"; + + /// + /// Template element represented by the PART_BackButton name. + /// + private const string TemplateElementBackButton = "PART_BackButton"; + + /// + /// Template element represented by the PART_ToggleButton name. + /// + private const string TemplateElementToggleButton = "PART_ToggleButton"; + + /// + /// Control responsible for rendering the content. + /// + protected NavigationViewContentPresenter NavigationViewContentPresenter; + + /// + /// Control located at the top of the pane with left arrow icon. + /// + protected System.Windows.Controls.ItemsControl MenuItemsItemsControl; + + /// + /// Control located at the top of the pane with hamburger icon. + /// + protected System.Windows.Controls.ItemsControl FooterMenuItemsItemsControl; + + /// + /// Control located at the top of the pane with left arrow icon. + /// + protected System.Windows.Controls.Button BackButton; + + /// + /// Control located at the top of the pane with hamburger icon. + /// + protected System.Windows.Controls.Button ToggleButton; + + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (GetTemplateChild(TemplateElementNavigationViewContentPresenter) is NavigationViewContentPresenter navigationViewContentPresenter) + NavigationViewContentPresenter = navigationViewContentPresenter; + + if (GetTemplateChild(TemplateElementMenuItemsItemsControl) is System.Windows.Controls.ItemsControl menuItemsItemsControl) + MenuItemsItemsControl = menuItemsItemsControl; + + if (GetTemplateChild(TemplateElementFooterMenuItemsItemsControl) is System.Windows.Controls.ItemsControl footerMenuItemsItemsControl) + FooterMenuItemsItemsControl = footerMenuItemsItemsControl; + + if (GetTemplateChild(TemplateElementBackButton) is System.Windows.Controls.Button backButton) + BackButton = backButton; + + if (GetTemplateChild(TemplateElementToggleButton) is System.Windows.Controls.Button toggleButton) + ToggleButton = toggleButton; + + if (NavigationViewContentPresenter != null) + NavigationViewContentPresenter.Navigated += OnNavigationViewContentPresenterNavigated; + + if (MenuItemsItemsControl != null) + MenuItemsItemsControl.ItemsSource = MenuItems; + + if (FooterMenuItemsItemsControl != null) + FooterMenuItemsItemsControl.ItemsSource = FooterMenuItems; + + if (BackButton != null) + { + BackButton.Click -= OnBackButtonClick; + BackButton.Click += OnBackButtonClick; + } + + if (ToggleButton != null) + { + ToggleButton.Click -= OnToggleButtonClick; + ToggleButton.Click += OnToggleButtonClick; + } + } +} diff --git a/src/Wpf.Ui/Controls/NavigationCompact.bmp b/src/Wpf.Ui/Controls/Navigation/NavigationView.bmp similarity index 100% rename from src/Wpf.Ui/Controls/NavigationCompact.bmp rename to src/Wpf.Ui/Controls/Navigation/NavigationView.bmp diff --git a/src/Wpf.Ui/Services/Internal/NavigationServiceActivator.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewActivator.cs similarity index 95% rename from src/Wpf.Ui/Services/Internal/NavigationServiceActivator.cs rename to src/Wpf.Ui/Controls/Navigation/NavigationViewActivator.cs index 3bc46b9da..d4617bf67 100644 --- a/src/Wpf.Ui/Services/Internal/NavigationServiceActivator.cs +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewActivator.cs @@ -5,19 +5,18 @@ using System; using System.Linq; +using System.Reflection; using System.Windows; using System.Windows.Controls; -using System.Reflection; using Wpf.Ui.Common; -using Wpf.Ui.Controls; using Wpf.Ui.Controls.Interfaces; -namespace Wpf.Ui.Services.Internal; +namespace Wpf.Ui.Controls.Navigation; /// -/// Internal activator for navigation purposes. +/// Internal activator for creating content instances of the navigation view items. /// -internal static class NavigationServiceActivator +internal static class NavigationViewActivator { /// /// Creates new instance of type derived from . @@ -39,7 +38,7 @@ public static FrameworkElement CreateInstance(Type pageType, object dataContext) { if (!typeof(FrameworkElement).IsAssignableFrom(pageType)) throw new InvalidCastException( - $"PageType of the ${typeof(INavigationItem)} must be derived from {typeof(FrameworkElement)}. {pageType} is not."); + $"PageType of the ${typeof(INavigationViewItem)} must be derived from {typeof(FrameworkElement)}. {pageType} is not."); if (DesignerHelper.IsInDesignMode) return new Page { Content = new TextBlock { Text = "Pages are not rendered while using the Designer. Edit the page template directly." } }; diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewBackButtonVisible.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewBackButtonVisible.cs new file mode 100644 index 000000000..6867deeb5 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewBackButtonVisible.cs @@ -0,0 +1,30 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +namespace Wpf.Ui.Controls.Navigation; + +/// +/// Defines constants that specify whether the back button is visible in NavigationView. +/// +public enum NavigationViewBackButtonVisible +{ + /// + /// Do not display the back button in NavigationView, and do not reserve space for it in layout. + /// + Collapsed, + + /// + /// Display the back button in NavigationView. + /// + Visible, + + /// + /// The system chooses whether or not to display the back button, depending on the device/form factor. On phones, tablets, desktops, and hubs, the back button is visible. On Xbox/TV, the back button is collapsed. + /// + Auto +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewBreadcrumb.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewBreadcrumb.cs new file mode 100644 index 000000000..bf18ea5f3 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewBreadcrumb.cs @@ -0,0 +1,100 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + + +using System; +using System.Windows; +using Wpf.Ui.Controls.Interfaces; + +namespace Wpf.Ui.Controls.Navigation; + +/// +/// Breadcrumb control allows you to keep track and maintain awareness of your locations within . +/// +public class NavigationViewBreadcrumb : System.Windows.Controls.Control +{ + /// + /// Property for . + /// + public static readonly DependencyProperty NavigationViewProperty = DependencyProperty.Register(nameof(NavigationView), + typeof(INavigationView), typeof(NavigationViewBreadcrumb), + new PropertyMetadata(((NavigationViewBreadcrumb)null!), OnNavigationViewPropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), + typeof(string), typeof(NavigationViewBreadcrumb), + new PropertyMetadata(String.Empty)); + + + /// + /// Pinned navigation control. + /// + public INavigationView NavigationView + { + get => (INavigationView)GetValue(NavigationViewProperty); + set => SetValue(NavigationViewProperty, value); + } + + /// + /// Primary breadcrumb navigation element. + /// + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public NavigationViewBreadcrumb() + { + if (NavigationView != null) + UpdateRenderedItems(); + } + + /// + /// This virtual method is called when the is changed. + /// + protected virtual void OnNavigationViewChanged() + { + if (NavigationView == null) + return; + + NavigationView.SelectionChanged -= OnNavigationViewSelectionChanged; + NavigationView.SelectionChanged += OnNavigationViewSelectionChanged; + } + + /// + /// This virtual method is called when the of the is changed. + /// + protected virtual void OnNavigationViewSelectionChanged(object sender, RoutedEventArgs e) + { + UpdateRenderedItems(); + } + + private static void OnNavigationViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationViewBreadcrumb breadcrumb) + return; + + breadcrumb.OnNavigationViewChanged(); + } + + private void UpdateRenderedItems() + { + if (NavigationView.SelectedItem is not NavigationViewItem navigationViewItem) + { + Text = String.Empty; + + return; + } + + // TODO: Multilevel + Text = navigationViewItem.Content?.ToString() ?? String.Empty; + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewContentPresenter.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewContentPresenter.cs new file mode 100644 index 000000000..3fd80966c --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewContentPresenter.cs @@ -0,0 +1,35 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Navigation; + +namespace Wpf.Ui.Controls.Navigation; + +public class NavigationViewContentPresenter : Frame +{ + static NavigationViewContentPresenter() + { + DefaultStyleKeyProperty.OverrideMetadata( + typeof(NavigationViewContentPresenter), + new FrameworkPropertyMetadata(typeof(NavigationViewContentPresenter))); + + NavigationUIVisibilityProperty.OverrideMetadata( + typeof(NavigationViewContentPresenter), + new FrameworkPropertyMetadata(NavigationUIVisibility.Hidden)); + + SandboxExternalContentProperty.OverrideMetadata( + typeof(NavigationViewContentPresenter), + new FrameworkPropertyMetadata(true)); + + JournalOwnershipProperty.OverrideMetadata( + typeof(NavigationViewContentPresenter), + new FrameworkPropertyMetadata(JournalOwnership.UsesParentJournal)); + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewEvent.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewEvent.cs new file mode 100644 index 000000000..eacce83bf --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewEvent.cs @@ -0,0 +1,22 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Diagnostics.CodeAnalysis; +using System.Windows; + +namespace Wpf.Ui.Controls.Navigation; + +/// +/// Event triggered by NavigationView. +/// +/// Current navigation instance. +#if NET5_0_OR_GREATER +public delegate void NavigationViewEvent([NotNull] object sender, RoutedEventArgs e); +#else +public delegate void NavigationViewEvent(object sender, RoutedEventArgs e); +#endif diff --git a/src/Wpf.Ui/Controls/NavigationFluent.bmp b/src/Wpf.Ui/Controls/Navigation/NavigationViewItem.bmp similarity index 100% rename from src/Wpf.Ui/Controls/NavigationFluent.bmp rename to src/Wpf.Ui/Controls/Navigation/NavigationViewItem.bmp diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewItem.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewItem.cs new file mode 100644 index 000000000..19d81c4b7 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewItem.cs @@ -0,0 +1,249 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows; +using System.Windows.Media; +using Wpf.Ui.Controls.Interfaces; + +namespace Wpf.Ui.Controls.Navigation; + +// 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. +/// When needed, it can be used as a normal button with a action. +/// +[ToolboxItem(true)] +[System.Drawing.ToolboxBitmap(typeof(NavigationViewItem), "NavigationViewItem.bmp")] +public class NavigationViewItem : System.Windows.Controls.Primitives.ButtonBase, INavigationViewItem, IIconControl +{ + /// + /// Property for . + /// + public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(nameof(MenuItems), + typeof(IList), typeof(NavigationViewItem), + new PropertyMetadata(OnMenuItemsPropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty MenuItemsSourceProperty = DependencyProperty.Register(nameof(MenuItemsSource), + typeof(object), typeof(NavigationViewItem), + new PropertyMetadata(((object)null!), OnMenuItemsSourcePropertyChanged)); + + /// + /// Property for . + /// + public static readonly DependencyProperty HasMenuItemsProperty = DependencyProperty.Register(nameof(HasMenuItems), + typeof(bool), typeof(NavigationViewItem), new PropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(nameof(IsActive), + typeof(bool), typeof(NavigationViewItem), new PropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(nameof(IsExpanded), + typeof(bool), typeof(NavigationViewItem), new PropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), + typeof(Common.SymbolRegular), typeof(NavigationViewItem), + new PropertyMetadata(Common.SymbolRegular.Empty)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IconFilledProperty = DependencyProperty.Register(nameof(IconFilled), + typeof(bool), typeof(NavigationViewItem), new PropertyMetadata(false)); + + /// + /// Property for . + /// + public static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register(nameof(IconForeground), + typeof(Brush), typeof(NavigationViewItem), new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, + FrameworkPropertyMetadataOptions.Inherits)); + + /// + /// Property for . + /// + public static readonly DependencyProperty TargetPageTagProperty = DependencyProperty.Register(nameof(TargetPageTag), + typeof(string), typeof(NavigationViewItem), new PropertyMetadata(String.Empty)); + + /// + /// Property for . + /// + public static readonly DependencyProperty TargetPageTypeProperty = DependencyProperty.Register(nameof(TargetPageType), + typeof(Type), typeof(NavigationViewItem), new PropertyMetadata((Type)null!)); + + /// + public IList MenuItems + { + get => (IList)GetValue(MenuItemsProperty); + set => SetValue(MenuItemsProperty, value); + } + + /// + [Bindable(true)] + public object MenuItemsSource + { + get => GetValue(MenuItemsSourceProperty); + set + { + if (value == null) + ClearValue(MenuItemsSourceProperty); + else + SetValue(MenuItemsSourceProperty, value); + } + } + + /// + /// Gets a value indicating whether the has . + /// + [Browsable(false), ReadOnly(true)] + public bool HasMenuItems + { + get => (bool)GetValue(HasMenuItemsProperty); + private set => SetValue(HasMenuItemsProperty, value); + } + + /// + [Browsable(false), ReadOnly(true)] + public bool IsActive + { + get => (bool)GetValue(IsActiveProperty); + internal set => SetValue(IsActiveProperty, value); + } + + /// + [Browsable(false), ReadOnly(true)] + public bool IsExpanded + { + get => (bool)GetValue(IsExpandedProperty); + internal set => SetValue(IsExpandedProperty, value); + } + + /// + [Bindable(true), Category("Appearance")] + public Common.SymbolRegular Icon + { + get => (Common.SymbolRegular)GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + /// + [Bindable(true), Category("Appearance")] + public bool IconFilled + { + get => (bool)GetValue(IconFilledProperty); + set => SetValue(IconFilledProperty, value); + } + + /// + [Bindable(true), Category("Appearance")] + public Brush IconForeground + { + get => (Brush)GetValue(IconForegroundProperty); + set => SetValue(IconForegroundProperty, value); + } + + /// + public string TargetPageTag + { + get => (string)GetValue(TargetPageTagProperty); + set => SetValue(TargetPageTagProperty, value); + } + + /// + public Type TargetPageType + { + get => (Type)GetValue(TargetPageTypeProperty); + set => SetValue(TargetPageTypeProperty, value); + } + + /// + public string Id { get; } + + static NavigationViewItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(NavigationViewItem), new FrameworkPropertyMetadata(typeof(NavigationViewItem))); + } + + public NavigationViewItem() + { + Id = Guid.NewGuid().ToString("n"); + + SetValue(MenuItemsProperty, + new ObservableCollection()); + } + + /// + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + + if (MenuItems?.Count > 0) + HasMenuItems = true; + + if (MenuItemsSource is IList menuItemsSourceList) + { + if (MenuItems != null) + MenuItems = null; + + MenuItems = menuItemsSourceList; + } + + if (String.IsNullOrWhiteSpace(TargetPageTag)) + TargetPageTag = Content?.ToString()!.ToLower()!.Trim() ?? String.Empty; + } + + /// + protected override void OnClick() + { + if (HasMenuItems) + IsExpanded = !IsExpanded; + + // TODO: If clicked on chevron, do not call parent event + + if (TargetPageType != null && NavigationView.GetNavigationParent(this) is { } navigationView) + navigationView.OnNavigationViewItemClick(this); + + base.OnClick(); + } + + private static void OnMenuItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationViewItem navigationViewItem) + return; + + navigationViewItem.HasMenuItems = navigationViewItem.MenuItems.Count > 0; + } + + private static void OnMenuItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NavigationViewItem navigationViewItem) + return; + + if (e.NewValue is not IList enumerableNewValue) + return; + + if (navigationViewItem.MenuItems != null) + navigationViewItem.MenuItems = null; + + navigationViewItem.MenuItems = enumerableNewValue; + } +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationHeader.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewItemHeader.cs similarity index 72% rename from src/Wpf.Ui/Controls/Navigation/NavigationHeader.cs rename to src/Wpf.Ui/Controls/Navigation/NavigationViewItemHeader.cs index a4a1f36a7..0d41aef07 100644 --- a/src/Wpf.Ui/Controls/Navigation/NavigationHeader.cs +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewItemHeader.cs @@ -1,53 +1,59 @@ -// This Source Code Form is subject to the terms of the MIT License. +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. using System.ComponentModel; -using System.Drawing; using System.Windows; +using System.Windows.Media; using Wpf.Ui.Controls.Interfaces; -using Brush = System.Windows.Media.Brush; -using SystemColors = System.Windows.SystemColors; namespace Wpf.Ui.Controls.Navigation; +// https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitemheader?view=winrt-22621 + +/// +/// Represents a header for a group of menu items in a NavigationMenu. +/// [ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationHeader), "NavigationHeader.bmp")] -public class NavigationHeader : System.Windows.Controls.Control, INavigationControl, IIconControl +[System.Drawing.ToolboxBitmap(typeof(NavigationViewItemHeader), "NavigationViewItemHeader.bmp")] +public class NavigationViewItemHeader : System.Windows.Controls.Control, IIconControl { /// /// Property for . /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), - typeof(string), typeof(NavigationHeader), + typeof(string), typeof(NavigationViewItemHeader), new PropertyMetadata(string.Empty)); /// /// Property for . /// public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), - typeof(Common.SymbolRegular), typeof(NavigationHeader), + typeof(Common.SymbolRegular), typeof(NavigationViewItemHeader), new PropertyMetadata(Common.SymbolRegular.Empty)); /// /// Property for . /// public static readonly DependencyProperty IconFilledProperty = DependencyProperty.Register(nameof(IconFilled), - typeof(bool), typeof(NavigationHeader), new PropertyMetadata(false)); + typeof(bool), typeof(NavigationViewItemHeader), new PropertyMetadata(false)); /// /// Property for . /// public static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register(nameof(IconForeground), - typeof(Brush), typeof(NavigationHeader), new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, + typeof(Brush), typeof(NavigationViewItemHeader), new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, FrameworkPropertyMetadataOptions.Inherits)); /// /// Property for . /// public static readonly DependencyProperty IconSizeProperty = DependencyProperty.Register(nameof(IconSize), - typeof(double), typeof(NavigationHeader), new FrameworkPropertyMetadata(13d)); + typeof(double), typeof(NavigationViewItemHeader), new FrameworkPropertyMetadata(13d)); /// /// Text presented in the header element. diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewItemSeparator.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewItemSeparator.cs new file mode 100644 index 000000000..bc1a86687 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewItemSeparator.cs @@ -0,0 +1,22 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.ComponentModel; + +namespace Wpf.Ui.Controls.Navigation; + +// https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewitemseparator?view=winrt-22621 + +/// +/// Represents a line that separates menu items in a NavigationMenu. +/// +[ToolboxItem(true)] +[System.Drawing.ToolboxBitmap(typeof(NavigationViewItemSeparator), "NavigationViewItemSeparator.bmp")] +public class NavigationViewItemSeparator : System.Windows.Controls.Separator +{ +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewList.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewList.cs new file mode 100644 index 000000000..0166bef9b --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewList.cs @@ -0,0 +1,20 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Collections.ObjectModel; + +namespace Wpf.Ui.Controls.Navigation; + +// https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.navigationviewlist?view=winrt-22621 + +/// +/// Represents a control that displays menu items in a NavigationView control. +/// +public class NavigationViewList : ObservableCollection +{ +} diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationViewPaneDisplayMode.cs b/src/Wpf.Ui/Controls/Navigation/NavigationViewPaneDisplayMode.cs new file mode 100644 index 000000000..0415a5430 --- /dev/null +++ b/src/Wpf.Ui/Controls/Navigation/NavigationViewPaneDisplayMode.cs @@ -0,0 +1,37 @@ +// Based on Windows UI Library +// Copyright(c) Microsoft Corporation.All rights reserved. + +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +namespace Wpf.Ui.Controls.Navigation; + +/// +/// Defines constants that specify how and where the NavigationView pane is shown. +/// +public enum NavigationViewPaneDisplayMode +{ + /// + /// The pane is shown on the left side of the control. + /// + Left, + + /// + /// The pane is shown on the left side of the control. Only the pane icons are shown. + /// + LeftMinimal, + + /// + /// The pane is shown on the left side of the control. Large icons with titles underneath are the only display option. Does not support . + /// Similar to the Windows Store (2022) app. + /// + LeftFluent, + + /// + /// The pane is shown at the top of the control. + /// + Top +} + diff --git a/src/Wpf.Ui/Controls/NavigationCompact.cs b/src/Wpf.Ui/Controls/NavigationCompact.cs deleted file mode 100644 index e81ea10c1..000000000 --- a/src/Wpf.Ui/Controls/NavigationCompact.cs +++ /dev/null @@ -1,68 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Windows; - -namespace Wpf.Ui.Controls; - -/// -/// Modern navigation styled similar to the Task Manager in Windows 11. -/// -[ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationCompact), "NavigationCompact.bmp")] -public class NavigationCompact : Wpf.Ui.Controls.Navigation.NavigationBase -{ - /// - /// Property for . - /// - public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( - nameof(IsExpanded), - typeof(bool), typeof(NavigationCompact), - new PropertyMetadata(false)); - - /// - /// Property for . - /// - public static readonly DependencyProperty TemplateButtonCommandProperty = - DependencyProperty.Register(nameof(TemplateButtonCommand), - typeof(Common.IRelayCommand), typeof(NavigationCompact), new PropertyMetadata(null)); - - /// - /// Gets or sets a value indicating whether the menu is expanded. - /// - public bool IsExpanded - { - get => (bool)GetValue(IsExpandedProperty); - set => SetValue(IsExpandedProperty, value); - } - - /// - /// Command triggered after clicking the button. - /// - public Common.IRelayCommand TemplateButtonCommand => (Common.IRelayCommand)GetValue(TemplateButtonCommandProperty); - - /// - /// Creates new instance and sets default . - /// - public NavigationCompact() : base() => - SetValue(TemplateButtonCommandProperty, new Common.RelayCommand(o => Button_OnClick(this, o))); - - private void Button_OnClick(object sender, object parameter) - { - if (parameter == null) - return; - - string param = parameter as string ?? String.Empty; - -#if DEBUG - System.Diagnostics.Debug.WriteLine($"INFO: {typeof(NavigationCompact)} button clicked with param: {param}", "Wpf.Ui.NavigationCompact"); -#endif - if (param == "hamburger") - IsExpanded = !IsExpanded; - } -} diff --git a/src/Wpf.Ui/Controls/NavigationFluent.cs b/src/Wpf.Ui/Controls/NavigationFluent.cs deleted file mode 100644 index eb7c88a87..000000000 --- a/src/Wpf.Ui/Controls/NavigationFluent.cs +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.ComponentModel; -using System.Drawing; - -namespace Wpf.Ui.Controls; - -/// -/// Modern navigation styled according to the principles of Fluent Design for Windows 11. -/// -[ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationFluent), "NavigationFluent.bmp")] -public class NavigationFluent : Wpf.Ui.Controls.Navigation.NavigationBase -{ - // XAML -} diff --git a/src/Wpf.Ui/Controls/NavigationItem.bmp b/src/Wpf.Ui/Controls/NavigationItem.bmp deleted file mode 100644 index 930c735ad..000000000 Binary files a/src/Wpf.Ui/Controls/NavigationItem.bmp and /dev/null differ diff --git a/src/Wpf.Ui/Controls/NavigationItem.cs b/src/Wpf.Ui/Controls/NavigationItem.cs deleted file mode 100644 index 36fc11fb3..000000000 --- a/src/Wpf.Ui/Controls/NavigationItem.cs +++ /dev/null @@ -1,386 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Windows; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using Wpf.Ui.Common; -using Wpf.Ui.Controls.Interfaces; -using Wpf.Ui.Controls.Navigation; -using Wpf.Ui.Extensions; -using Brush = System.Windows.Media.Brush; -using SystemColors = System.Windows.SystemColors; - -namespace Wpf.Ui.Controls; - -/// -/// Navigation element. -/// -[ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationItem), "NavigationItem.bmp")] -public class NavigationItem : System.Windows.Controls.Primitives.ButtonBase, IUriContext, INavigationItem, INavigationControl, IIconControl -{ - /// - /// Property for . - /// - public static readonly DependencyProperty PageTagProperty = DependencyProperty.Register(nameof(PageTag), - typeof(string), typeof(NavigationItem), new PropertyMetadata(String.Empty)); - - /// - /// Property for . - /// - public static readonly DependencyProperty PageSourceProperty = DependencyProperty.Register(nameof(PageSource), - typeof(Uri), typeof(NavigationItem), new PropertyMetadata((Uri)null, OnPageSourceChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty PageTypeProperty = DependencyProperty.Register(nameof(PageType), - typeof(Type), typeof(NavigationItem), new PropertyMetadata((Type)null, OnPageTypeChanged)); - - /// - /// Property for . - /// - public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(nameof(IsActive), - typeof(bool), typeof(NavigationItem), new PropertyMetadata(false)); - - /// - /// Property for . - /// - public static readonly DependencyProperty CacheProperty = DependencyProperty.Register(nameof(Cache), - typeof(bool), typeof(NavigationItem), new PropertyMetadata(true)); - - /// - /// Property for . - /// - public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), - typeof(Common.SymbolRegular), typeof(NavigationItem), - new PropertyMetadata(Common.SymbolRegular.Empty)); - - /// - /// Property for . - /// - public static readonly DependencyProperty IconSizeProperty = DependencyProperty.Register(nameof(IconSize), - typeof(double), typeof(NavigationItem), - new PropertyMetadata(18d)); - - /// - /// Property for . - /// - public static readonly DependencyProperty IconFilledProperty = DependencyProperty.Register(nameof(IconFilled), - typeof(bool), typeof(NavigationItem), new PropertyMetadata(false)); - - /// - /// Property for . - /// - public static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register(nameof(IconForeground), - typeof(Brush), typeof(NavigationItem), new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, - FrameworkPropertyMetadataOptions.Inherits)); - - /// - /// Property for . - /// - public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), - typeof(BitmapSource), typeof(NavigationItem), - new PropertyMetadata(null)); - - /// - /// Routed event for . - /// - public static readonly RoutedEvent ActivatedEvent = EventManager.RegisterRoutedEvent( - nameof(Activated), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(NavigationItem)); - - /// - /// Routed event for . - /// - public static readonly RoutedEvent DeactivatedEvent = EventManager.RegisterRoutedEvent( - nameof(Deactivated), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(NavigationItem)); - - /// - public string PageTag - { - get => (string)GetValue(PageTagProperty); - set => SetValue(PageTagProperty, value); - } - - /// - public Uri PageSource - { - get => (Uri)GetValue(PageSourceProperty); - set => SetValue(PageSourceProperty, value); - - } - - /// - public Type PageType - { - get => (Type)GetValue(PageTypeProperty); - set => SetValue(PageTypeProperty, value); - } - - /// - public bool IsActive - { - get => (bool)GetValue(IsActiveProperty); - set - { - if (value == IsActive) - return; - - RaiseEvent(value - ? new RoutedEventArgs(ActivatedEvent, this) - : new RoutedEventArgs(DeactivatedEvent, this)); - - SetValue(IsActiveProperty, value); - } - } - - /// - public bool Cache - { - get => (bool)GetValue(CacheProperty); - set => SetValue(CacheProperty, value); - } - - /// - [Bindable(true), Category("Appearance")] - [Localizability(LocalizationCategory.None)] - public Common.SymbolRegular Icon - { - get => (Common.SymbolRegular)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - /// - [Bindable(true), Category("Appearance")] - [Localizability(LocalizationCategory.None)] - public bool IconFilled - { - get => (bool)GetValue(IconFilledProperty); - set => SetValue(IconFilledProperty, value); - } - - /// - /// Size of the . - /// - [TypeConverter(typeof(FontSizeConverter))] - [Bindable(true), Category("Appearance")] - [Localizability(LocalizationCategory.None)] - public double IconSize - { - get => (double)GetValue(IconSizeProperty); - set => SetValue(IconSizeProperty, value); - } - - /// - /// Foreground of the . - /// - [Bindable(true), Category("Appearance")] - public Brush IconForeground - { - get => (Brush)GetValue(IconForegroundProperty); - set => SetValue(IconForegroundProperty, value); - } - - /// - /// Gets or sets image displayed next to the card name instead of the icon. - /// - [Bindable(true), Category("Appearance")] - public BitmapSource Image - { - get => GetValue(ImageProperty) as BitmapSource; - set => SetValue(ImageProperty, value); - } - - /// - /// Occurs when is activated via . - /// - public event RoutedEventHandler Activated - { - add => AddHandler(ActivatedEvent, value); - remove => RemoveHandler(ActivatedEvent, value); - } - - /// - /// Occurs when is deactivated via . - /// - public event RoutedEventHandler Deactivated - { - add => AddHandler(DeactivatedEvent, value); - remove => RemoveHandler(DeactivatedEvent, value); - } - - /// - [Bindable(false)] - public Uri AbsolutePageSource { get; internal set; } - - /// - Uri IUriContext.BaseUri - { - get => BaseUri; - set => BaseUri = value; - } - - /// - /// Implementation for BaseUri. - /// - protected virtual Uri BaseUri - { - get => (Uri)GetValue(BaseUriHelper.BaseUriProperty); - set => SetValue(BaseUriHelper.BaseUriProperty, value); - } - - /// - protected override void OnContentChanged(object oldContent, object newContent) - { - base.OnContentChanged(oldContent, newContent); - - if (newContent is string && String.IsNullOrEmpty(PageTag)) - PageTag = newContent?.ToString()?.ToLower()?.Trim() ?? String.Empty; - } - - /// - protected override void OnKeyDown(KeyEventArgs e) - { - if (Keyboard.Modifiers is not ModifierKeys.None) - { - // We handle Left/Up/Right/Down keys for keyboard navigation only, - // so no modifiers are needed. - return; - } - - switch (e.Key) - { - // We use Direction Left/Up/Right/Down instead of Previous/Next to make sure - // that the KeyboardNavigation.DirectionalNavigation property works correctly. - case Key.Left: - MoveFocus(this, FocusNavigationDirection.Left); - e.Handled = true; - break; - - case Key.Up: - MoveFocus(this, FocusNavigationDirection.Up); - e.Handled = true; - break; - - case Key.Right: - MoveFocus(this, FocusNavigationDirection.Right); - e.Handled = true; - break; - - case Key.Down: - MoveFocus(this, FocusNavigationDirection.Down); - e.Handled = true; - break; - - case Key.Space: - case Key.Enter: - - // Item doesn't define a page, skip navigation. - if (PageSource == null && PageType == null) - break; - - if (NavigationBase.GetNavigationParent(this) is { } navigation - && PageTag is { } pageTag - && !String.IsNullOrEmpty(pageTag)) - { - e.Handled = true; - - navigation.Navigate(pageTag); - } - break; - } - - // If it is simply treated as a button, pass the information about the click on. - if (!e.Handled) - base.OnKeyDown(e); - - static void MoveFocus(FrameworkElement element, FocusNavigationDirection direction) - { - var request = new TraversalRequest(direction); - element.MoveFocus(request); - } - } - - /// - /// This virtual method is called when of the selected page is changed. - /// - protected virtual void OnPageSourceChanged(Uri pageUri) - { - if (!pageUri.OriginalString.EndsWith(".xaml")) - throw new ArgumentException($"URI in {typeof(NavigationItem)} must point to the XAML Page."); - - AbsolutePageSource = ResolvePageUri(pageUri); - } - - private static void OnPageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationItem navigationItem) - return; - - navigationItem.OnPageSourceChanged(e.NewValue as Uri); - } - - /// - /// This virtual method is called when of the selected page is changed. - /// - protected virtual void OnPageTypeChanged(Type pageType) - { - if (pageType == null) - return; - - if (!typeof(System.Windows.FrameworkElement).IsAssignableFrom(pageType)) - throw new ArgumentException($"{pageType} is not inherited from {typeof(System.Windows.FrameworkElement)}."); - } - - private static void OnPageTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NavigationItem navigationItem) - return; - - navigationItem.OnPageTypeChanged(e.NewValue as Type); - } - - /// - /// Tries to resolve absolute path to the Page template. - /// - private Uri ResolvePageUri(Uri pageUri) - { - if (pageUri == null || pageUri.IsAbsoluteUri) - return pageUri; - - if (!pageUri.EndsWith(".xaml")) - throw new ArgumentException("PageSource must point to the .xaml file."); - - var baseUri = BaseUri; - - if (baseUri == null) - { - // TODO: Force extracting BaseUri for Designer - // This is a hackery solution that needs to be refined. - - if (!DesignerHelper.IsInDesignMode) - throw new UriFormatException("Unable to resolve absolute URI for selected page"); - - // The navigation simply prints a blank page during the design process. - PageType = typeof(System.Windows.Controls.Page); - - return null; - } - - if (!baseUri.IsAbsoluteUri) - throw new ApplicationException("Unable to resolve base URI for selected page"); - - if (baseUri.EndsWith(".xaml")) - baseUri = baseUri.TrimLastSegment(); - - return baseUri.Append(pageUri); - } -} diff --git a/src/Wpf.Ui/Controls/NavigationStore.bmp b/src/Wpf.Ui/Controls/NavigationStore.bmp deleted file mode 100644 index 930c735ad..000000000 Binary files a/src/Wpf.Ui/Controls/NavigationStore.bmp and /dev/null differ diff --git a/src/Wpf.Ui/Controls/NavigationStore.cs b/src/Wpf.Ui/Controls/NavigationStore.cs deleted file mode 100644 index 21d8a2415..000000000 --- a/src/Wpf.Ui/Controls/NavigationStore.cs +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.ComponentModel; -using System.Drawing; - -namespace Wpf.Ui.Controls; - -/// -/// Modern navigation styled according to the principles of Fluent Design for Windows 11. -/// -[ToolboxItem(true)] -[ToolboxBitmap(typeof(NavigationStore), "NavigationStore.bmp")] -public class NavigationStore : Wpf.Ui.Controls.Navigation.NavigationBase -{ - // XAML -} diff --git a/src/Wpf.Ui/Controls/NavigationView.cs b/src/Wpf.Ui/Controls/NavigationView.cs deleted file mode 100644 index 855dddee4..000000000 --- a/src/Wpf.Ui/Controls/NavigationView.cs +++ /dev/null @@ -1,70 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.Windows; -using System.Windows.Controls; -using Wpf.Ui.Common; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Controls; - -/// -/// Ready navigation that includes a control, and . -/// -[TemplatePart(Name = "PART_Navigation", Type = typeof(Wpf.Ui.Controls.Navigation.NavigationBase))] -[TemplatePart(Name = "PART_Breadcrumb", Type = typeof(Wpf.Ui.Controls.Breadcrumb))] -[TemplatePart(Name = "PART_Frame", Type = typeof(System.Windows.Controls.Frame))] -public class NavigationView : System.Windows.Controls.Control -{ - /// - /// Template element represented by the PART_Popup name. - /// - private const string ElementNavigation = "PART_Navigation"; - - /// - /// Template element represented by the PART_Popup name. - /// - private const string ElementBreadcrumb = "PART_Breadcrumb"; - - /// - /// Template element represented by the PART_Popup name. - /// - private const string ElementFrame = "PART_Frame"; - - public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(nameof(Type), - typeof(NavigationType), typeof(NavigationView), - new PropertyMetadata(NavigationType.Compact)); - - /// - /// Navigation control. - /// - public INavigation Navigation { get; protected set; } - - /// - /// Navigation breadcrumb. - /// - public Breadcrumb Breadcrumb { get; protected set; } - - /// - /// Navigation frame - /// - public Frame Frame { get; protected set; } - - public NavigationType Type - { - get => (NavigationType)GetValue(TypeProperty); - set => SetValue(TypeProperty, value); - } - - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - Navigation = GetTemplateChild(ElementNavigation) as INavigation; - Breadcrumb = GetTemplateChild(ElementBreadcrumb) as Breadcrumb; - Frame = GetTemplateChild(ElementFrame) as Frame; - } -} diff --git a/src/Wpf.Ui/Converters/BackButtonVisibilityToVisibilityConverter.cs b/src/Wpf.Ui/Converters/BackButtonVisibilityToVisibilityConverter.cs new file mode 100644 index 000000000..5a91534bc --- /dev/null +++ b/src/Wpf.Ui/Converters/BackButtonVisibilityToVisibilityConverter.cs @@ -0,0 +1,44 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Windows; +using System.Windows.Data; +using System.Windows.Media; +using Wpf.Ui.Controls.Navigation; + +namespace Wpf.Ui.Converters; + +/// +/// Converts to . +/// +internal class BackButtonVisibilityToVisibilityConverter : IValueConverter +{ + /// + /// Converts to . + /// + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is not NavigationViewBackButtonVisible backButtonVisibility) + return Visibility.Collapsed; + + switch (backButtonVisibility) + { + case NavigationViewBackButtonVisible.Collapsed: + return Visibility.Collapsed; + default: + return Visibility.Visible; + } + } + + /// + /// Not Implemented. + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Wpf.Ui/Converters/BoolToVisibilityConverter.cs b/src/Wpf.Ui/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 000000000..cf03473f5 --- /dev/null +++ b/src/Wpf.Ui/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System; +using System.Windows; +using System.Windows.Data; +using System.Windows.Media; + +namespace Wpf.Ui.Converters; + +/// +/// Converts to . +/// +internal class BoolToVisibilityConverter : IValueConverter +{ + /// + /// Converts to . + /// + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value is true ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Not Implemented. + /// + /// + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Wpf.Ui/License - Microsoft.Ui.Xaml.txt b/src/Wpf.Ui/License - Microsoft.Ui.Xaml.txt new file mode 100644 index 000000000..d1ca00f20 --- /dev/null +++ b/src/Wpf.Ui/License - Microsoft.Ui.Xaml.txt @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE \ No newline at end of file diff --git a/src/Wpf.Ui/License - Windows Presentation Foundation.txt b/src/Wpf.Ui/License - Windows Presentation Foundation.txt new file mode 100644 index 000000000..a616ed188 --- /dev/null +++ b/src/Wpf.Ui/License - Windows Presentation Foundation.txt @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/Wpf.Ui/Mvvm/Contracts/INavigationService.cs b/src/Wpf.Ui/Mvvm/Contracts/INavigationService.cs index 1914de8c0..a13bc720b 100644 --- a/src/Wpf.Ui/Mvvm/Contracts/INavigationService.cs +++ b/src/Wpf.Ui/Mvvm/Contracts/INavigationService.cs @@ -4,7 +4,6 @@ // All Rights Reserved. using System; -using System.Windows.Controls; using Wpf.Ui.Controls.Interfaces; namespace Wpf.Ui.Mvvm.Contracts; @@ -15,34 +14,22 @@ namespace Wpf.Ui.Mvvm.Contracts; /// public interface INavigationService { - /// - /// Provides direct access to the used in navigation. - /// - /// Instance of the control. - Frame GetFrame(); - - /// - /// Sets the used by . - /// - /// Frame to set. - void SetFrame(Frame frame); - /// /// Provides direct access to the control responsible for navigation. /// /// Instance of the control. - INavigation GetNavigationControl(); + INavigationView GetNavigationControl(); /// /// Lets you attach the control that represents the . /// /// Instance of the . - void SetNavigationControl(INavigation navigation); + void SetNavigationControl(INavigationView navigation); /// /// Lets you attach the service that delivers page instances to . /// - /// Instance of the with attached service provider. + /// Instance of the . void SetPageService(IPageService pageService); /// @@ -51,15 +38,9 @@ public interface INavigationService /// of the page. bool Navigate(Type pageType); - /// - /// Lets you navigate to the selected page based on it's id. Should be used with . - /// - /// Id of the page. - bool Navigate(int pageId); - /// /// Lets you navigate to the selected page based on it's tag. Should be used with . /// - /// Tag of the page. - bool Navigate(string pageTag); + /// Id or tag of the page. + bool Navigate(string pageIdOrTargetTag); } diff --git a/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs b/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs index 6d46035a2..a8f031ab6 100644 --- a/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs +++ b/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs @@ -4,7 +4,6 @@ // All Rights Reserved. using System; -using System.Windows.Controls; using Wpf.Ui.Controls.Interfaces; namespace Wpf.Ui.Mvvm.Contracts; @@ -15,17 +14,11 @@ namespace Wpf.Ui.Mvvm.Contracts; /// public interface INavigationWindow { - /// - /// Provides direct access to the used in navigation. - /// - /// Instance of the control. - Frame GetFrame(); - /// /// Provides direct access to the control responsible for navigation. /// /// Instance of the control. - INavigation GetNavigation(); + INavigationView GetNavigation(); /// /// Lets you navigate to the selected page based on it's type. Should be used with . @@ -34,7 +27,13 @@ public interface INavigationWindow bool Navigate(Type pageType); /// - /// Lets you attach the service that delivers page instances to . + /// Lets you attach the service provider that delivers page instances to . + /// + /// Instance of the . + void SetServiceProvider(IServiceProvider serviceProvider); + + /// + /// Lets you attach the service that delivers page instances to . /// /// Instance of the with attached service provider. void SetPageService(IPageService pageService); diff --git a/src/Wpf.Ui/Mvvm/Interfaces/IViewModel.cs b/src/Wpf.Ui/Mvvm/Interfaces/IViewModel.cs deleted file mode 100644 index 30d4c0f7a..000000000 --- a/src/Wpf.Ui/Mvvm/Interfaces/IViewModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.Windows; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Mvvm.Interfaces; - -/// -/// Represents a model used in views. -/// -public interface IViewModel -{ - /// - /// Triggered when the model is mounted to the view using the . - /// - /// - public void OnMounted(FrameworkElement parentElement); -} diff --git a/src/Wpf.Ui/Mvvm/Services/NavigationService.cs b/src/Wpf.Ui/Mvvm/Services/NavigationService.cs index 4818c102d..6cbad8c27 100644 --- a/src/Wpf.Ui/Mvvm/Services/NavigationService.cs +++ b/src/Wpf.Ui/Mvvm/Services/NavigationService.cs @@ -4,7 +4,6 @@ // All Rights Reserved. using System; -using System.Windows.Controls; using Wpf.Ui.Controls.Interfaces; using Wpf.Ui.Mvvm.Contracts; @@ -15,6 +14,11 @@ namespace Wpf.Ui.Mvvm.Services; /// public partial class NavigationService : INavigationService { + /// + /// Locally attached service provider. + /// + private readonly IServiceProvider _serviceProvider; + /// /// Locally attached page service. /// @@ -23,36 +27,32 @@ public partial class NavigationService : INavigationService /// /// Control representing navigation. /// - protected INavigation NavigationControl; - - /// - public Frame GetFrame() - { - return NavigationControl?.Frame; - } + protected INavigationView NavigationControl; - /// - public void SetFrame(Frame frame) + public NavigationService(IServiceProvider serviceProvider) { - if (NavigationControl == null) - return; - - NavigationControl.Frame = frame; + _serviceProvider = serviceProvider; } /// - public INavigation GetNavigationControl() + public INavigationView GetNavigationControl() { return NavigationControl; } /// - public void SetNavigationControl(INavigation navigation) + public void SetNavigationControl(INavigationView navigation) { NavigationControl = navigation; if (_pageService != null) - NavigationControl.PageService = _pageService; + { + NavigationControl.SetPageService(_pageService); + + return; + } + + NavigationControl.SetServiceProvider(_serviceProvider); } /// @@ -65,7 +65,7 @@ public void SetPageService(IPageService pageService) return; } - NavigationControl.PageService = pageService; + NavigationControl.SetPageService(_pageService); } /// @@ -77,15 +77,6 @@ public bool Navigate(Type pageType) return NavigationControl.Navigate(pageType); } - /// - public bool Navigate(int pageId) - { - if (NavigationControl == null) - return false; - - return NavigationControl.Navigate(pageId); - } - /// public bool Navigate(string pageTag) { diff --git a/src/Wpf.Ui/Services/Internal/NavigationService.cs b/src/Wpf.Ui/Services/Internal/NavigationService.cs deleted file mode 100644 index 2bb01717c..000000000 --- a/src/Wpf.Ui/Services/Internal/NavigationService.cs +++ /dev/null @@ -1,865 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -#nullable enable - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Navigation; -using Wpf.Ui.Animations; -using Wpf.Ui.Common; -using Wpf.Ui.Common.Interfaces; -using Wpf.Ui.Controls.Interfaces; -using Wpf.Ui.Mvvm.Contracts; -using Wpf.Ui.Mvvm.Interfaces; - -namespace Wpf.Ui.Services.Internal; - -// NOTE: -// This class is taped combining many weird tricks -// and workarounds. Please don't judge me, -// I'm just a student with a bit of free time - -/// -/// Internal navigation service. -/// -internal sealed class NavigationService : IDisposable -{ - #region Private properties - - /// - /// Whether the current class is disposed. - /// - private bool _disposed = false; - - /// - /// Currently navigated page index. - /// - private int _currentPageIndex = -1; - - /// - /// Previously navigated page index. - /// - private int _previousPageIndex = -1; - - /// - /// Current frame. - /// - private Frame? _frame; - - /// - /// MVVM page service. - /// - private IPageService? _pageService; - - /// - /// Current . - /// - private long _currentActionIdentifier { get; set; } - - /// - /// Identifies current Frame process. - /// - private readonly EventIdentifier _eventIdentifier; - - /// - /// 's mirror with cached page contents. - /// - private NavigationServiceItem[] _navigationServiceItems; - - /// - /// - /// - private readonly List _history; - - private bool _isBackNavigated; - - #endregion Private properties - - #region Public properties - - /// - /// Whether to precache instances after rebuilding. - /// - public bool Precache { get; set; } = false; - - /// - /// Transition duration. - /// - public int TransitionDuration { get; set; } - - /// - /// Transition type. - /// - public TransitionType TransitionType { get; set; } - - /// - /// Indicates the possibility of navigation back - /// - public bool CanGoBack => _history.Count > 1; - - #endregion Public properties - - #region Constructors - - /// - /// Creates new instance and prepares internal properties. - /// - public NavigationService() - { - _eventIdentifier = new EventIdentifier(); - _navigationServiceItems = new NavigationServiceItem[] { }; - _history = new List(); - } - - /// - /// Control finalizer. - /// - ~NavigationService() - { - Dispose(false); - } - - #endregion Constructors - - #region Public methods - - public bool NavigateBack() - { - if (_history.Count <= 1) - return false; - - _isBackNavigated = true; - - return NavigateInternal(_history[_history.Count - 2], null!); - } - - /// - /// Navigates the based on provided item Id. - /// - /// Id of the selected page. - /// Additional . - /// - public bool Navigate(int pageId, object dataContext) - { - return NavigateInternal(pageId, dataContext); - } - - /// - /// Navigates the based on provided item . - /// - /// of the selected page. - /// Additional . - /// - public bool Navigate(Type pageType, object dataContext) - { - var selectedIndex = -1; - - for (var i = 0; i < _navigationServiceItems.Length; i++) - { - if (_navigationServiceItems[i].Type != pageType) - continue; - - selectedIndex = i; - - break; - } - - if (selectedIndex >= 0) - return NavigateInternal(selectedIndex, dataContext); - - if (_pageService == null) - return false; - - var servicePageInstance = _pageService.GetPage(pageType); - - if (servicePageInstance == null) - throw new InvalidOperationException($"The {pageType} has not been registered in the {typeof(IPageService)} service."); - - _previousPageIndex = _currentPageIndex; - _currentPageIndex = -1; - - _currentActionIdentifier = _eventIdentifier.GetNext(); - - _frame?.Navigate(servicePageInstance); - - return true; - } - - /// - /// Navigates the based on provided item tag. - /// - /// Tag of the page. - /// Additional . - /// - public bool Navigate(string pageTag, object dataContext) - { - var selectedIndex = -1; - - for (var i = 0; i < _navigationServiceItems.Length; i++) - { - if (_navigationServiceItems[i].Tag != pageTag) - continue; - - selectedIndex = i; - - break; - } - - if (selectedIndex < 0) - return false; - - return NavigateInternal(selectedIndex, dataContext); - } - - /// - /// Navigates statically outside of the current navigation scope. - /// - /// to navigate. - /// Additional . - public bool NavigateExternal(object frameworkElement, object dataContext) - { - if (_frame == null) - return false; - - if (frameworkElement is not FrameworkElement) - throw new InvalidOperationException($"Only class derived {typeof(FrameworkElement)} can be used for navigation."); - - _previousPageIndex = _currentPageIndex; - _currentPageIndex = -1; - - _currentActionIdentifier = _eventIdentifier.GetNext(); - - _frame.Navigate( - frameworkElement, - new NavigationServiceExtraData - { - PageId = -1, - Cache = false, - DataContext = dataContext - }); - - return true; - } - - /// - /// Navigates statically outside of the current navigation scope. - /// - /// Uri of the to navigate. - /// Additional . - public bool NavigateExternal(Uri frameworkElementUri, object dataContext) - { - if (_frame == null) - return false; - - if (!frameworkElementUri.IsAbsoluteUri) - throw new InvalidOperationException($"Navigation Uri must be absolute Uri pointing to an element derived from {typeof(FrameworkElement)}."); - - _previousPageIndex = _currentPageIndex; - _currentPageIndex = -1; - - _currentActionIdentifier = _eventIdentifier.GetNext(); - - _frame.Navigate( - frameworkElementUri, - new NavigationServiceExtraData - { - PageId = -1, - Cache = false, - DataContext = dataContext - }); - - return true; - } - - /// - /// Sets DataContext for the selected instance. - /// - /// Tag of the page. - /// Context to set. - public bool SetContext(string pageTag, object dataContext) - { - for (var i = 0; i < _navigationServiceItems.Length; i++) - { - if (_navigationServiceItems[i].Tag != pageTag) - continue; - - if (_navigationServiceItems[i].Instance is not FrameworkElement) - return false; - - ((FrameworkElement)_navigationServiceItems[i].Instance).DataContext = dataContext; - - return true; - } - - return false; - } - - /// - /// Sets DataContext for the selected instance. - /// - /// Selected page Id. - /// Context to set. - public bool SetContext(int serviceItemId, object dataContext) - { - if (_navigationServiceItems.Length - 1 < serviceItemId) - return false; - - if (_navigationServiceItems[serviceItemId].Instance is not FrameworkElement) - return false; - - ((FrameworkElement)_navigationServiceItems[serviceItemId].Instance).DataContext = dataContext; - - return true; - } - - /// - /// Creates mirror of based on provided collection of 's. - /// - public void UpdateItems(IEnumerable? mainItems, IEnumerable? additionalItems) - { - var serviceItemCollection = new List { }; - - if (mainItems != null) - foreach (var singleNavigationControl in mainItems) - { - if (singleNavigationControl is not INavigationItem navigationItem) - continue; - - serviceItemCollection.Add(NavigationServiceItem.Create(navigationItem)); - } - - if (additionalItems != null) - foreach (var singleNavigationControl in additionalItems) - { - if (singleNavigationControl is not INavigationItem navigationItem) - continue; - - serviceItemCollection.Add(NavigationServiceItem.Create(navigationItem)); - } - - _navigationServiceItems = serviceItemCollection.ToArray(); - - // Should we precache here? It can be intensive cause the update can be fired multiple times during initialization - //if (Precache) - //{ - //} - } - - /// - /// Clears cache stored inside service items. - /// - public void ClearCache() - { - foreach (var singleServiceItem in _navigationServiceItems) - singleServiceItem.Instance = null; - } - - /// - /// Sets currently used . - /// - /// Frame to set. - public void SetFrame(Frame frame) - { - if (frame == null) - return; - - _frame = frame; - - _frame.NavigationUIVisibility = NavigationUIVisibility.Hidden; - - _frame.Navigating -= OnFrameNavigating; // Unsafe, but doubling can be catastrophic - _frame.Navigating += OnFrameNavigating; - - _frame.Navigated -= OnFrameNavigated; // Unsafe, but doubling can be catastrophic - _frame.Navigated += OnFrameNavigated; - } - - /// - /// Sets currently used . - /// - /// Service to set. - public void SetService(IPageService pageService) - { - _pageService = pageService; - } - - /// - /// Gets currently used . - /// - public IPageService? GetService() - { - return _pageService ?? null; - } - - /// - /// Gets currently displayed item tag. - /// - public string GetCurrentTag() - { - if (_currentPageIndex < 0) - return "__external__"; - - if (_navigationServiceItems.Length == 0) - return string.Empty; - - if (_navigationServiceItems.Length - 1 < _currentPageIndex) - return string.Empty; - - return _navigationServiceItems[_currentPageIndex].Tag; - } - - /// - /// Currently displayed page Id. - /// - public int GetCurrentId() - { - return _currentPageIndex; - } - - /// - /// Previously displayed page Id. - /// - public int GetPreviousId() - { - return _previousPageIndex; - } - - #endregion Public methods - - #region Disposing - - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// If disposing equals , the method has been called directly or indirectly - /// by a user's code. Managed and unmanaged resources can be disposed. If disposing equals , - /// the method has been called by the runtime from inside the finalizer and you should not - /// reference other objects. - /// Only unmanaged resources can be disposed. - /// - /// If disposing equals , dispose all managed and unmanaged resources. - private void Dispose(bool disposing) - { - if (_disposed) - return; - - _disposed = true; - - if (!disposing) - return; - -#if DEBUG - System.Diagnostics.Debug.WriteLine($"INFO | {typeof(NavigationService)} disposed.", "Wpf.Ui.Navigation"); -#endif - - _navigationServiceItems = null; - } - - #endregion Disposing - - #region Internal navigation - - /// - /// Navigates internally depending on current state of the service. - /// - /// Id of the item to navigate. - /// Additional . - /// - private bool NavigateInternal(int serviceItemId, object dataContext) - { - if (!_navigationServiceItems.Any()) - return false; - - _currentActionIdentifier = _eventIdentifier.GetNext(); - - if (_navigationServiceItems.Length - 1 < serviceItemId) - return false; - - // The navigation item is the same, skip the navigation - if (_currentPageIndex == serviceItemId) - return false; - - // An empty navigation item may be just a button, but as navigation fails, so return false. - if (_navigationServiceItems[serviceItemId].Type == null && - _navigationServiceItems[serviceItemId].Source == null) - return false; - - _previousPageIndex = _currentPageIndex; - _currentPageIndex = serviceItemId; - - if (_pageService != null) - return NavigateInternalByService(serviceItemId); - - - if (!_navigationServiceItems[serviceItemId].Cache) - return NavigateInternalByItemWithoutCache(serviceItemId, dataContext); - - return NavigateInternalByItemWithCache(serviceItemId, dataContext); - } - - /// - /// Navigates internally without service and with enabled cache. - /// - private bool NavigateInternalByItemWithCache(int serviceItemId, object dataContext) - { - if (_frame == null) - return false; - - if (_navigationServiceItems.Length - 1 < serviceItemId) - return false; - - // Navigate internally, with cache enabled, instance does exist so reuse it - if (_navigationServiceItems[serviceItemId].Instance != null) - { - // Sometimes a user may want to update the context of a page that is already in the cache. - if (dataContext != null && _navigationServiceItems[serviceItemId].Instance is FrameworkElement) - ((FrameworkElement)_navigationServiceItems[serviceItemId].Instance).DataContext = dataContext; - - _frame.Navigate( - _navigationServiceItems[serviceItemId].Instance, - new NavigationServiceExtraData - { - PageId = serviceItemId, - Cache = true, - DataContext = dataContext - }); - -#if DEBUG - System.Diagnostics.Debug.WriteLine( - $"DEBUG | {_navigationServiceItems[serviceItemId].Tag} navigated internally, with cache by it's instance."); -#endif - AddToHistory(serviceItemId); - return true; - } - - // Navigate internally, with cache enabled, instance does not exist so create it using type - if (_navigationServiceItems[serviceItemId].Type != null) - { - _navigationServiceItems[serviceItemId].Instance = CreateFrameworkElementInstance(_navigationServiceItems[serviceItemId].Type, dataContext); - - _frame.Navigate( - _navigationServiceItems[serviceItemId].Instance, - new NavigationServiceExtraData - { - PageId = serviceItemId, - Cache = true, - DataContext = null // DataContext used - }); - -#if DEBUG - System.Diagnostics.Debug.WriteLine( - $"DEBUG | {_navigationServiceItems[serviceItemId].Tag} navigated internally, with cache by it's type."); -#endif - AddToHistory(serviceItemId); - return true; - } - - // Navigate internally, with cache enabled, instance does not exist so create it using source - if (_navigationServiceItems[serviceItemId].Source != null) - { - _frame.Navigate( - _navigationServiceItems[serviceItemId].Source, - new NavigationServiceExtraData - { - PageId = serviceItemId, - Cache = true, - DataContext = dataContext - }); - -#if DEBUG - System.Diagnostics.Debug.WriteLine( - $"DEBUG | {_navigationServiceItems[serviceItemId].Tag} navigated internally, with cache by it's source."); -#endif - - AddToHistory(serviceItemId); - return true; - } - - return false; - } - - /// - /// Navigates internally without service and with cache disabled. - /// - private bool NavigateInternalByItemWithoutCache(int serviceItemId, object dataContext) - { - if (_frame == null) - return false; - - if (_navigationServiceItems.Length - 1 < serviceItemId) - return false; - - // Navigate internally, without cache, based on type - if (_navigationServiceItems[serviceItemId].Type != null) - { - _frame.Navigate( - CreateFrameworkElementInstance - ( - _navigationServiceItems[serviceItemId].Type, - dataContext - ), - new NavigationServiceExtraData - { - PageId = serviceItemId, - Cache = false, - DataContext = null // DataContext set above by activator - }); -#if DEBUG - System.Diagnostics.Debug.WriteLine( - $"DEBUG | {_navigationServiceItems[serviceItemId].Tag} navigated internally, without cache by it's type."); -#endif - AddToHistory(serviceItemId); - return true; - } - - if (_navigationServiceItems[serviceItemId].Source != null) - { - _frame.Navigate( - _navigationServiceItems[serviceItemId].Source, - new NavigationServiceExtraData - { - PageId = serviceItemId, - Cache = false, - DataContext = dataContext - }); - -#if DEBUG - System.Diagnostics.Debug.WriteLine( - $"DEBUG | {_navigationServiceItems[serviceItemId].Tag} navigated internally, without cache by it's source."); -#endif - - AddToHistory(serviceItemId); - return true; - } - - // Wait... this should not happen... - return false; - } - - private bool NavigateInternalByService(int serviceItemId) - { - if (_frame == null) - return false; - - if (_navigationServiceItems.Length - 1 < serviceItemId) - return false; - - var servicePageInstance = _pageService.GetPage(_navigationServiceItems[serviceItemId].Type); - - if (servicePageInstance == null) - throw new InvalidOperationException($"The {_navigationServiceItems[serviceItemId].Type} has not been registered in the {typeof(IPageService)} service."); - - _frame.Navigate(servicePageInstance); - AddToHistory(serviceItemId); - - return true; - } - - private void AddToHistory(int serviceItemId) - { - if (_isBackNavigated) - { - _isBackNavigated = false; - _history.RemoveAt(_history.LastIndexOf(_history[_history.Count - 2])); - _history.RemoveAt(_history.LastIndexOf(_history[_history.Count - 1])); - } - - _history.Add(serviceItemId); - } - - #endregion Internal navigation - - #region Instance management - - /// - /// Tries to create an instance from the selected page type. - /// - private FrameworkElement CreateFrameworkElementInstance(Type pageType, object dataContext) - { - return NavigationServiceActivator.CreateInstance(pageType, dataContext); - } - - #endregion Instance management - - #region Frame events - - /// - /// Event triggered when the frame has already loaded the view, if the page uses the Cache, Content of the Frame should be saved. - /// - private void OnFrameNavigated(object sender, NavigationEventArgs e) - { - if (_frame == null) - return; - - if (_frame.CanGoBack) - _frame.RemoveBackEntry(); - - if (_frame.NavigationService.CanGoBack) - _frame.NavigationService?.RemoveBackEntry(); - - if (TransitionDuration > 0 && e.Content != null) - Transitions.ApplyTransition(e.Content, TransitionType, TransitionDuration); - - // If we are using the MVVM model, - // do not perform internal operations on DataContext and Instances. - if (_pageService != null) - { - // Instance and datacontext determined by the service, notify and leave - NotifyFrameContentAboutEnter(); - - return; - } - - if (e.ExtraData is not NavigationServiceExtraData extraData) - { - // Instance determined by the frame, context not provided, notify and leave - NotifyFrameContentAboutEnter(); - - return; - } - - if (!_currentActionIdentifier.Equals(_currentActionIdentifier)) - { - // Only god knows what's broken, but notify anyway and then leave. - NotifyFrameContentAboutEnter(); - - return; - } - - // DataContext provided by the frame extra data, set. - if (extraData.DataContext != null && _frame.Content is FrameworkElement) - { - ((FrameworkElement)_frame.Content).DataContext = extraData.DataContext; - - if (extraData.DataContext is IViewModel) - ((IViewModel)extraData.DataContext).OnMounted((FrameworkElement)_frame.Content); - } - - if (!extraData.Cache) - { - // Instance determined by the frame, context set from extra data, but without cache, notify and leave - NotifyFrameContentAboutEnter(); - - return; - } - - // We make sure that pageId exists, if it is wrong, the fault lies earlier. - if (_navigationServiceItems.Length - 1 < extraData.PageId || extraData.PageId < 0) - { - // Only god knows what's broken, but notify anyway and then leave. - NotifyFrameContentAboutEnter(); - - return; - } - - // If an instance already exists, do not overwrite it. - if (_navigationServiceItems[extraData.PageId].Instance != null) - { - // Instance determined by the frame, context set from extra data, with cache, but instance cached, notify and leave - NotifyFrameContentAboutEnter(); - - return; - } - - // Finally, the navigation took place internally, - // the context was set from extra data, the cache has to be saved, - // so we save it, notify it and this is the end of the method - _navigationServiceItems[extraData.PageId].Instance = _frame.Content; - - NotifyFrameContentAboutEnter(); - } - - /// - /// Event fired when Frame received a request to navigate. - /// - private void OnFrameNavigating(object sender, NavigatingCancelEventArgs e) - { - if (_frame == null) - return; - - NotifyFrameContentAboutLeave(); - - switch (e.NavigationMode) - { - case NavigationMode.Back: - e.Cancel = true; - - if (_currentPageIndex > 0) - Navigate(_currentPageIndex - 1, null); - break; - - case NavigationMode.Forward: - e.Cancel = true; - - if (_currentPageIndex < _navigationServiceItems.Length - 1) - Navigate(_currentPageIndex + 1, null); - break; - } - } - - /// - /// Notifies content about being navigated. - /// - private void NotifyFrameContentAboutEnter() - { - if (_frame == null) - return; - - if (_frame.Content is INavigationAware) - ((INavigationAware)_frame.Content).OnNavigatedTo(); - - if (_frame.Content is INavigableView navigableView && navigableView.ViewModel is INavigationAware) - ((INavigationAware)navigableView.ViewModel).OnNavigatedTo(); - - if (_frame.Content is FrameworkElement && ((FrameworkElement)_frame.Content).DataContext is INavigationAware) - ((INavigationAware)((FrameworkElement)_frame.Content).DataContext).OnNavigatedTo(); - } - - /// - /// Notifies content about leaving the navigation context. - /// - private void NotifyFrameContentAboutLeave() - { - if (_frame == null) - return; - - if (_frame.Content is INavigationAware) - ((INavigationAware)_frame.Content).OnNavigatedFrom(); - - if (_frame.Content is INavigableView navigableView && navigableView.ViewModel is INavigationAware) - ((INavigationAware)navigableView.ViewModel).OnNavigatedFrom(); - - if (_frame.Content is FrameworkElement && ((FrameworkElement)_frame.Content).DataContext is INavigationAware) - ((INavigationAware)((FrameworkElement)_frame.Content).DataContext).OnNavigatedFrom(); - } - - #endregion Frame events - - #region Preache - - /// - /// Precaches instances of the navigation items. - /// - private void PrecacheItems() - { - if (DesignerHelper.IsInDesignMode) - return; - - if (_pageService != null) - return; - } - - #endregion -} diff --git a/src/Wpf.Ui/Services/Internal/NavigationServiceExtraData.cs b/src/Wpf.Ui/Services/Internal/NavigationServiceExtraData.cs deleted file mode 100644 index 0f5892b75..000000000 --- a/src/Wpf.Ui/Services/Internal/NavigationServiceExtraData.cs +++ /dev/null @@ -1,27 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -namespace Wpf.Ui.Services.Internal; - -/// -/// Additional data passed through the method. -/// -internal struct NavigationServiceExtraData -{ - /// - /// Current page id. - /// - public int PageId { get; set; } - - /// - /// Whether we should use the cache. - /// - public bool Cache { get; set; } - - /// - /// Additional . - /// - public object DataContext { get; set; } -} diff --git a/src/Wpf.Ui/Services/Internal/NavigationServiceItem.cs b/src/Wpf.Ui/Services/Internal/NavigationServiceItem.cs deleted file mode 100644 index 7c1377cda..000000000 --- a/src/Wpf.Ui/Services/Internal/NavigationServiceItem.cs +++ /dev/null @@ -1,71 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System; -using System.Windows; -using Wpf.Ui.Controls.Interfaces; - -namespace Wpf.Ui.Services.Internal; - -/// -/// Represents in internal methods. -/// -internal class NavigationServiceItem -{ - /// - /// Tags of the presented page. - /// - public string Tag { get; set; } = string.Empty; - - /// - /// Whether the cache is active. - /// - public bool Cache { get; set; } = false; - - /// - /// Type of the . - /// - public Type Type { get; set; } = null; - - /// - /// Source of the . - /// - public Uri Source { get; set; } = null; - - /// - /// Instantiated page content. - /// - public object Instance { get; set; } = null; - - /// - /// Sets DataContext of the . - /// - public bool SetContext(object dataContext) - { - if (!Cache) - return false; - - if (Instance is not FrameworkElement) - return false; - - ((FrameworkElement)Instance).DataContext = dataContext; - - return true; - } - - /// - /// Creates from . - /// - public static NavigationServiceItem Create(INavigationItem navigationItem) - { - return new NavigationServiceItem - { - Tag = navigationItem.PageTag, - Type = navigationItem.PageType, - Source = navigationItem.AbsolutePageSource, - Cache = navigationItem.Cache - }; - } -} diff --git a/src/Wpf.Ui/Styles/Controls/Breadcrumb.xaml b/src/Wpf.Ui/Styles/Controls/Breadcrumb.xaml deleted file mode 100644 index df9936ef6..000000000 --- a/src/Wpf.Ui/Styles/Controls/Breadcrumb.xaml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - diff --git a/src/Wpf.Ui/Styles/Controls/Navigation.xaml b/src/Wpf.Ui/Styles/Controls/Navigation.xaml deleted file mode 100644 index 19161ffe3..000000000 --- a/src/Wpf.Ui/Styles/Controls/Navigation.xaml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/Wpf.Ui/Styles/Controls/NavigationBackButton.xaml b/src/Wpf.Ui/Styles/Controls/NavigationBackButton.xaml deleted file mode 100644 index 772909d50..000000000 --- a/src/Wpf.Ui/Styles/Controls/NavigationBackButton.xaml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Wpf.Ui/Styles/Controls/NavigationCompact.xaml b/src/Wpf.Ui/Styles/Controls/NavigationCompact.xaml deleted file mode 100644 index 9f25a1c0e..000000000 --- a/src/Wpf.Ui/Styles/Controls/NavigationCompact.xaml +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/Wpf.Ui/Styles/Controls/NavigationFluent.xaml b/src/Wpf.Ui/Styles/Controls/NavigationFluent.xaml deleted file mode 100644 index 2105adc07..000000000 --- a/src/Wpf.Ui/Styles/Controls/NavigationFluent.xaml +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - - - - diff --git a/src/Wpf.Ui/Styles/Controls/NavigationStore.xaml b/src/Wpf.Ui/Styles/Controls/NavigationStore.xaml deleted file mode 100644 index d3c3e85b5..000000000 --- a/src/Wpf.Ui/Styles/Controls/NavigationStore.xaml +++ /dev/null @@ -1,354 +0,0 @@ - - - - - - - - - - - diff --git a/src/Wpf.Ui/Styles/Controls/NavigationView.xaml b/src/Wpf.Ui/Styles/Controls/NavigationView.xaml index 7453eb9a9..e12d23caf 100644 --- a/src/Wpf.Ui/Styles/Controls/NavigationView.xaml +++ b/src/Wpf.Ui/Styles/Controls/NavigationView.xaml @@ -8,84 +8,1050 @@ + xmlns:controls="clr-namespace:Wpf.Ui.Controls" + xmlns:converters="clr-namespace:Wpf.Ui.Converters" + xmlns:navigation="clr-namespace:Wpf.Ui.Controls.Navigation" + xmlns:system="clr-namespace:System;assembly=mscorlib"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +