diff --git a/src/Packages.props b/src/Packages.props
index f2ca092b9..0b215faf7 100644
--- a/src/Packages.props
+++ b/src/Packages.props
@@ -7,5 +7,6 @@
+
\ No newline at end of file
diff --git a/src/Wpf.Ui.Demo/App.xaml.cs b/src/Wpf.Ui.Demo/App.xaml.cs
index 0fba0c658..2af198870 100644
--- a/src/Wpf.Ui.Demo/App.xaml.cs
+++ b/src/Wpf.Ui.Demo/App.xaml.cs
@@ -93,6 +93,8 @@ public partial class App
services.AddScoped();
services.AddScoped();
+
+ services.AddScoped();
// Test windows
services.AddTransient();
diff --git a/src/Wpf.Ui.Demo/ViewModels/BreadcrumbPagesViewModel.cs b/src/Wpf.Ui.Demo/ViewModels/BreadcrumbPagesViewModel.cs
new file mode 100644
index 000000000..80037cd82
--- /dev/null
+++ b/src/Wpf.Ui.Demo/ViewModels/BreadcrumbPagesViewModel.cs
@@ -0,0 +1,31 @@
+using System.Windows.Input;
+using Microsoft.Toolkit.Mvvm.Input;
+using Wpf.Ui.Mvvm.Contracts;
+
+namespace Wpf.Ui.Demo.ViewModels;
+
+public class BreadcrumbPagesViewModel
+{
+ public RelayCommand OnClickCommand { get; }
+ public ICommand OnNavigateBackCommand { get; }
+
+ private readonly INavigationService _navigationService;
+
+ public BreadcrumbPagesViewModel(INavigationService navigationService)
+ {
+ _navigationService = navigationService;
+
+ OnClickCommand = new RelayCommand(OnClick);
+ OnNavigateBackCommand = new RelayCommand(OnNavigateBack);
+ }
+
+ private void OnClick(string pageTag)
+ {
+ _navigationService.NavigateTo($"/{pageTag}", this);
+ }
+
+ private void OnNavigateBack()
+ {
+ _navigationService.NavigateTo("..");
+ }
+}
diff --git a/src/Wpf.Ui.Demo/ViewModels/ButtonsViewModel.cs b/src/Wpf.Ui.Demo/ViewModels/ButtonsViewModel.cs
index 877d7fb81..f12bf72c1 100644
--- a/src/Wpf.Ui.Demo/ViewModels/ButtonsViewModel.cs
+++ b/src/Wpf.Ui.Demo/ViewModels/ButtonsViewModel.cs
@@ -27,9 +27,8 @@ public ButtonsViewModel(INavigationService navigationService)
var currentTheme = testGetThemeService.GetSystemTheme();
}
-
private void OnShowMore(string parameter)
{
- _navigationService.Navigate(typeof(Views.Pages.Input));
+ _navigationService.NavigateTo("/input");
}
}
diff --git a/src/Wpf.Ui.Demo/ViewModels/DashboardViewModel.cs b/src/Wpf.Ui.Demo/ViewModels/DashboardViewModel.cs
index 038786190..4c7c89793 100644
--- a/src/Wpf.Ui.Demo/ViewModels/DashboardViewModel.cs
+++ b/src/Wpf.Ui.Demo/ViewModels/DashboardViewModel.cs
@@ -47,19 +47,19 @@ private void OnNavigate(string parameter)
switch (parameter)
{
case "navigate_to_input":
- _navigationService.Navigate(typeof(Views.Pages.Input));
+ _navigationService.NavigateTo(typeof(Views.Pages.Input));
return;
case "navigate_to_controls":
- _navigationService.Navigate(typeof(Views.Pages.Controls));
+ _navigationService.NavigateTo(typeof(Views.Pages.Controls));
return;
case "navigate_to_colors":
- _navigationService.Navigate(typeof(Views.Pages.Colors));
+ _navigationService.NavigateTo(typeof(Views.Pages.Colors));
return;
case "navigate_to_icons":
- _navigationService.Navigate(typeof(Views.Pages.Icons));
+ _navigationService.NavigateTo(typeof(Views.Pages.Icons));
return;
}
}
diff --git a/src/Wpf.Ui.Demo/Views/Container.xaml b/src/Wpf.Ui.Demo/Views/Container.xaml
index aaf32c924..c6a971ab5 100644
--- a/src/Wpf.Ui.Demo/Views/Container.xaml
+++ b/src/Wpf.Ui.Demo/Views/Container.xaml
@@ -2,6 +2,7 @@
x:Class="Wpf.Ui.Demo.Views.Container"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:breadcrumbDemo="clr-namespace:Wpf.Ui.Demo.Views.Pages.BreadcrumbDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:demo="clr-namespace:Wpf.Ui.Demo"
xmlns:diagnostics="clr-namespace:Wpf.Ui.Demo.Views.Diagnostics"
@@ -98,6 +99,12 @@
Content="Theme"
Icon="DarkTheme24" />
+
+
+
+
+
+
@@ -198,8 +205,8 @@
ForceShutdown="False"
Icon="pack://application:,,,/Resources/wpfui.png"
MinimizeToTray="False"
- ShowHelp="False"
ShowClose="True"
+ ShowHelp="False"
ShowMaximize="True"
ShowMinimize="True"
UseSnapLayout="True">
@@ -237,5 +244,10 @@
+
+
diff --git a/src/Wpf.Ui.Demo/Views/Container.xaml.cs b/src/Wpf.Ui.Demo/Views/Container.xaml.cs
index e9e158b98..aa02a4864 100644
--- a/src/Wpf.Ui.Demo/Views/Container.xaml.cs
+++ b/src/Wpf.Ui.Demo/Views/Container.xaml.cs
@@ -4,6 +4,7 @@
// All Rights Reserved.
using System;
+using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -96,11 +97,14 @@ public Frame GetFrame()
public INavigation GetNavigation()
=> RootNavigation;
- public bool Navigate(Type pageType)
- => RootNavigation.Navigate(pageType);
+ public void NavigateTo(Type type, object dataContext = null)
+ => RootNavigation.NavigateTo(type, dataContext);
+
+ public void NavigateTo(string pageTag, object dataContext = null)
+ => RootNavigation.NavigateTo(pageTag, dataContext);
public void SetPageService(IPageService pageService)
- => RootNavigation.PageService = pageService;
+ => RootNavigation.SetIPageService(pageService);
public void ShowWindow()
=> Show();
@@ -108,9 +112,10 @@ public void ShowWindow()
public void CloseWindow()
=> Close();
+
#endregion INavigationWindow methods
- private void InvokeSplashScreen()
+ private async void InvokeSplashScreen()
{
if (_initialized)
return;
@@ -122,24 +127,16 @@ private void InvokeSplashScreen()
_taskBarService.SetState(this, TaskBarProgressState.Indeterminate);
- Task.Run(async () =>
- {
- // Remember to always include Delays and Sleeps in
- // your applications to be able to charge the client for optimizations later.
- await Task.Delay(4000);
+ // Remember to always include Delays and Sleeps in
+ // your applications to be able to charge the client for optimizations later.
+ await Task.Delay(4000);
- await Dispatcher.InvokeAsync(() =>
- {
- RootWelcomeGrid.Visibility = Visibility.Hidden;
- RootMainGrid.Visibility = Visibility.Visible;
+ RootWelcomeGrid.Visibility = Visibility.Hidden;
+ RootMainGrid.Visibility = Visibility.Visible;
- Navigate(typeof(Pages.Dashboard));
+ NavigateTo(typeof(Pages.Dashboard));
- _taskBarService.SetState(this, TaskBarProgressState.None);
- });
-
- return true;
- });
+ _taskBarService.SetState(this, TaskBarProgressState.None);
}
private void NavigationButtonTheme_OnClick(object sender, RoutedEventArgs e)
@@ -157,16 +154,15 @@ private void TrayMenuItem_OnClick(object sender, RoutedEventArgs e)
private void RootNavigation_OnNavigated(INavigation sender, RoutedNavigationEventArgs e)
{
- System.Diagnostics.Debug.WriteLine($"DEBUG | WPF UI Navigated to: {sender?.Current ?? null}", "Wpf.Ui.Demo");
+ System.Diagnostics.Debug.WriteLine($"DEBUG | WPF UI Navigated to: {e.NavigatedTo}", "Wpf.Ui.Demo");
// This funky solution allows us to impose a negative
// margin for Frame only for the Dashboard page, thanks
// to which the banner will cover the entire page nicely.
RootFrame.Margin = new Thickness(
left: 0,
- top: sender?.Current?.PageTag == "dashboard" ? -69 : 0,
+ top: e.NavigatedTo.PageTag == "dashboard" ? -69 : 0,
right: 0,
bottom: 0);
}
}
-
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml
new file mode 100644
index 000000000..d3e8394ee
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml.cs b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml.cs
new file mode 100644
index 000000000..2f18ed5a9
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page1.xaml.cs
@@ -0,0 +1,11 @@
+using System.Windows.Controls;
+
+namespace Wpf.Ui.Demo.Views.Pages.BreadcrumbDemo;
+
+public partial class Page1 : Page
+{
+ public Page1()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml
new file mode 100644
index 000000000..583c4cc4a
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml.cs b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml.cs
new file mode 100644
index 000000000..452815201
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page2.xaml.cs
@@ -0,0 +1,11 @@
+using System.Windows.Controls;
+
+namespace Wpf.Ui.Demo.Views.Pages.BreadcrumbDemo;
+
+public partial class Page2 : Page
+{
+ public Page2()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml
new file mode 100644
index 000000000..7b4089dd7
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml.cs b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml.cs
new file mode 100644
index 000000000..5c2059e40
--- /dev/null
+++ b/src/Wpf.Ui.Demo/Views/Pages/BreadcrumbDemo/Page3.xaml.cs
@@ -0,0 +1,11 @@
+using System.Windows.Controls;
+
+namespace Wpf.Ui.Demo.Views.Pages.BreadcrumbDemo;
+
+public partial class Page3 : Page
+{
+ public Page3()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/Wpf.Ui.Demo/Views/Pages/Controls.xaml b/src/Wpf.Ui.Demo/Views/Pages/Controls.xaml
index c00c43d1e..f52cba0da 100644
--- a/src/Wpf.Ui.Demo/Views/Pages/Controls.xaml
+++ b/src/Wpf.Ui.Demo/Views/Pages/Controls.xaml
@@ -29,6 +29,7 @@
+
@@ -84,6 +85,22 @@
Text="Opens the MessageBox." />
+
+
+
+
+
+
+
();
+ _navigation.NavigateTo("/page1", viewModel);
+ }
}
diff --git a/src/Wpf.Ui.Demo/Views/Pages/ExperimentalDashboard.xaml.cs b/src/Wpf.Ui.Demo/Views/Pages/ExperimentalDashboard.xaml.cs
index e1fd83569..a8ece4934 100644
--- a/src/Wpf.Ui.Demo/Views/Pages/ExperimentalDashboard.xaml.cs
+++ b/src/Wpf.Ui.Demo/Views/Pages/ExperimentalDashboard.xaml.cs
@@ -58,7 +58,7 @@ private void ButtonExternal_OnClick(object sender, RoutedEventArgs e)
if (DataContext is not ExperimentalViewModel viewData)
return;
- viewData.ParentWindow.Navigate(typeof(ExperimentalDashboard));
+ viewData.ParentWindow.NavigateTo(typeof(ExperimentalDashboard));
}
private void TaskbarStateComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
diff --git a/src/Wpf.Ui.Demo/Views/Windows/ExperimentalWindow.xaml.cs b/src/Wpf.Ui.Demo/Views/Windows/ExperimentalWindow.xaml.cs
index 9e6b6d692..a90b5fb71 100644
--- a/src/Wpf.Ui.Demo/Views/Windows/ExperimentalWindow.xaml.cs
+++ b/src/Wpf.Ui.Demo/Views/Windows/ExperimentalWindow.xaml.cs
@@ -101,7 +101,7 @@ public ExperimentalWindow(ExperimentalViewModel viewModel, IThemeService themeSe
private void RootNavigationOnLoaded(object sender, RoutedEventArgs e)
{
- RootNavigation.Navigate(0, DataContext);
+ //RootNavigation.NavigateTo(0, DataContext);
}
private void NavigationButtonTheme_OnClick(object sender, RoutedEventArgs e)
@@ -130,15 +130,18 @@ public Frame GetFrame()
public INavigation GetNavigation()
=> RootNavigation;
- public bool Navigate(Type pageType)
- => RootNavigation.Navigate(pageType);
-
public void SetPageService(IPageService pageService)
- => RootNavigation.PageService = pageService;
+ => RootNavigation.SetIPageService(pageService);
public void ShowWindow()
=> Show();
public void CloseWindow()
=> Close();
+
+ public void NavigateTo(Type type, object dataContext = null)
+ => RootNavigation.NavigateTo(type, dataContext);
+
+ public void NavigateTo(string pageTag, object dataContext = null)
+ => RootNavigation.NavigateTo(pageTag, dataContext);
}
diff --git a/src/Wpf.Ui.SimpleDemo/MainWindow.xaml b/src/Wpf.Ui.SimpleDemo/MainWindow.xaml
index 9e4f5fe78..2a82b0442 100644
--- a/src/Wpf.Ui.SimpleDemo/MainWindow.xaml
+++ b/src/Wpf.Ui.SimpleDemo/MainWindow.xaml
@@ -29,12 +29,24 @@
Icon="Home24"
PageTag="home"
PageType="{x:Type local:DashboardPage}" />
-
-
+
+
-
-
+
+
+/// TODO
+///
+public interface INavigationCancelable
+{
+ ///
+ /// TODO
+ ///
+ ///
+ bool CouldNavigate(INavigationItem? navigationFrom);
+}
diff --git a/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs b/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs
index ae124110c..d93551187 100644
--- a/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs
+++ b/src/Wpf.Ui/Common/RoutedNavigationEventArgs.cs
@@ -3,30 +3,30 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
+#nullable enable
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; }
+ public readonly INavigationItem? NavigatedFrom;
+ public readonly INavigationItem NavigatedTo;
///
- /// Constructor for .
+ /// TODO
///
- /// 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)
+ ///
+ ///
+ ///
+ ///
+ public RoutedNavigationEventArgs(RoutedEvent routedEvent, INavigation source, INavigationItem? navigatedFrom, INavigationItem navigatedTo) : base(routedEvent, source)
{
- CurrentPage = currentPage;
+ NavigatedFrom = navigatedFrom;
+ NavigatedTo = navigatedTo;
}
}
diff --git a/src/Wpf.Ui/Controls/Breadcrumb.cs b/src/Wpf.Ui/Controls/Breadcrumb.cs
index cb0b089ef..c4704ac5c 100644
--- a/src/Wpf.Ui/Controls/Breadcrumb.cs
+++ b/src/Wpf.Ui/Controls/Breadcrumb.cs
@@ -3,8 +3,12 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
-using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
using System.Windows;
+using System.Windows.Input;
+using CommunityToolkit.Diagnostics;
using Wpf.Ui.Common;
using Wpf.Ui.Controls.Interfaces;
@@ -15,27 +19,16 @@ namespace Wpf.Ui.Controls;
///
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));
+ new PropertyMetadata(null));
+
+ public static readonly DependencyProperty BreadcrumbItemsProperty = DependencyProperty.Register(nameof(BreadcrumbItems),
+ typeof(ObservableCollection), typeof(Breadcrumb), new PropertyMetadata(null));
- ///
- /// based on which displays the titles.
- ///
- public string Current
- {
- get => (string)GetValue(CurrentProperty);
- set => SetValue(CurrentProperty, value);
- }
///
/// based on which displays the titles.
@@ -46,35 +39,76 @@ public INavigation Navigation
set => SetValue(NavigationProperty, value);
}
- protected virtual void OnNavigated(INavigation sender, RoutedNavigationEventArgs e)
+ public ObservableCollection BreadcrumbItems
{
-#if DEBUG
- System.Diagnostics.Debug.WriteLine($"INFO | {typeof(Breadcrumb)} builded, current nav: {Navigation.GetType()}", "Wpf.Ui.Breadcrumb");
-#endif
+ get => (ObservableCollection)GetValue(BreadcrumbItemsProperty);
+ private set => SetValue(BreadcrumbItemsProperty, value);
+ }
- //TODO: Navigate with previous levels
+ private readonly ICommand _onClickCommand;
- if (Navigation?.Current is not INavigationItem item)
+ public Breadcrumb()
+ {
+ BreadcrumbItems = new ObservableCollection();
+ _onClickCommand = new RelayCommand(OnClick);
+
+ if (DesignerProperties.GetIsInDesignMode(this))
return;
- var pageName = item.Content as string;
+ Loaded += (_, _) =>
+ {
+ Guard.IsNotNull(Navigation, nameof(Navigation));
+ Navigation.NavigationStack.CollectionChanged += NavigationStackOnCollectionChanged;
- if (String.IsNullOrEmpty(pageName))
- return;
+ if (Navigation.NavigationStack.Count <= 0)
+ return;
+
+ foreach (var item in Navigation.NavigationStack)
+ BreadcrumbItems.Add( BreadcrumbItem.Create(item, _onClickCommand));
+
+ BreadcrumbItems[BreadcrumbItems.Count - 1].IsActive = true;
+ };
- Current = pageName;
+ Unloaded += (_, _) =>
+ {
+ Navigation.NavigationStack.CollectionChanged -= NavigationStackOnCollectionChanged;
+ };
}
- protected virtual void OnNavigationChanged()
+ private void NavigationStackOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
- Navigation.Navigated += OnNavigated;
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ var newItem = (INavigationItem) e.NewItems![0];
+ BreadcrumbItems.Add(BreadcrumbItem.Create(newItem, _onClickCommand));
+ break;
+ }
+ case NotifyCollectionChangedAction.Remove:
+ BreadcrumbItems.RemoveAt(e.OldStartingIndex);
+ break;
+ case NotifyCollectionChangedAction.Replace:
+ var replaceItem = (INavigationItem) e.NewItems![0];
+ var breadcrumbItem = BreadcrumbItem.Create(replaceItem, _onClickCommand);
+
+ BreadcrumbItems[0] = breadcrumbItem;
+ break;
+ default:
+ return;
+ }
+
+ if (BreadcrumbItems.Count > 1)
+ BreadcrumbItems[BreadcrumbItems.Count - 2].IsActive = false;
+
+ BreadcrumbItems[BreadcrumbItems.Count - 1].IsActive = true;
+
}
- private static void OnNavigationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ private void OnClick(object obj)
{
- if (d is not Breadcrumb breadcrumb)
- return;
+ var pageTag = (string)obj;
- breadcrumb.OnNavigationChanged();
+ Navigation.NavigateTo(pageTag);
}
}
diff --git a/src/Wpf.Ui/Controls/BreadcrumbItem.cs b/src/Wpf.Ui/Controls/BreadcrumbItem.cs
new file mode 100644
index 000000000..48a5cf507
--- /dev/null
+++ b/src/Wpf.Ui/Controls/BreadcrumbItem.cs
@@ -0,0 +1,51 @@
+using System.Windows;
+using System.Windows.Input;
+using Wpf.Ui.Controls.Interfaces;
+
+namespace Wpf.Ui.Controls;
+
+public class BreadcrumbItem : System.Windows.Controls.Control
+{
+ public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text),
+ typeof(string), typeof(BreadcrumbItem), new PropertyMetadata(string.Empty));
+
+ public static readonly DependencyProperty PageTagProperty = DependencyProperty.Register(nameof(PageTag),
+ typeof(string), typeof(BreadcrumbItem), new PropertyMetadata(string.Empty));
+
+ public static readonly DependencyProperty OnClickCommandProperty = DependencyProperty.Register(nameof(OnClickCommand),
+ typeof(ICommand), typeof(BreadcrumbItem), new PropertyMetadata(null));
+
+ public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(nameof(IsActive),
+ typeof(bool), typeof(BreadcrumbItem), new PropertyMetadata(false));
+
+ public string Text
+ {
+ get => (string)GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public string PageTag
+ {
+ get => (string)GetValue(PageTagProperty);
+ set => SetValue(PageTagProperty, value);
+ }
+
+ public ICommand OnClickCommand
+ {
+ get => (ICommand)GetValue(OnClickCommandProperty);
+ set => SetValue(OnClickCommandProperty, value);
+ }
+
+ public bool IsActive
+ {
+ get => (bool)GetValue(IsActiveProperty);
+ set => SetValue(IsActiveProperty, value);
+ }
+
+ public static BreadcrumbItem Create(INavigationItem item, ICommand onClickCommand) => new BreadcrumbItem()
+ {
+ Text = item.Content as string ?? string.Empty,
+ PageTag = item.PageTag,
+ OnClickCommand = onClickCommand
+ };
+}
diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigation.cs b/src/Wpf.Ui/Controls/Interfaces/INavigation.cs
index 3e2592e12..1eeb093aa 100644
--- a/src/Wpf.Ui/Controls/Interfaces/INavigation.cs
+++ b/src/Wpf.Ui/Controls/Interfaces/INavigation.cs
@@ -3,7 +3,9 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
+#nullable enable
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls;
@@ -18,22 +20,12 @@ namespace Wpf.Ui.Controls.Interfaces;
///
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.
///
@@ -45,9 +37,9 @@ public interface INavigation
bool CanGoBack { get; }
///
- /// Currently used item like .
+ /// TODO
///
- INavigationItem Current { get; }
+ ObservableCollection NavigationStack { get; }
///
/// Gets or sets the in which the will be loaded after navigation.
@@ -58,15 +50,18 @@ public interface INavigation
///
/// 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; }
+ ///
+ /// TODO
+ ///
+ List HiddenItems { get; set; }
+
///
/// Specifies dimension of children stacking.
///
@@ -78,18 +73,6 @@ public interface INavigation
[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.
///
@@ -102,117 +85,33 @@ public interface INavigation
[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 .
+ /// TODO
///
- ///
- bool NavigateBack();
+ void SetIPageService(IPageService pageService);
///
- /// Navigates to the page using the .
+ ///
///
- /// Type of the page to navigate.
- /// if the operation was successful.
- bool Navigate(Type pageType);
+ void Preload();
///
- /// 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.
+ /// Clears all initialized instances of the pages.
///
- /// Id of the page from or .
- /// Context of the data for data binding.
- /// if the operation was successful.
- bool SetContext(int pageId, object dataContext);
+ void ClearCache();
///
- /// 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.
+ /// TODO
///
- /// Tag of the page from or .
- /// Context of the data for data binding.
- /// if the operation was successful.
- bool SetContext(string pageTag, object dataContext);
+ ///
+ ///
+ void NavigateTo(string pageTag, object? dataContext = null);
///
- /// Tires to set the DataContext for the currently displayed page.
+ /// TODO
///
- /// Data context to be set.
- void SetCurrentContext(object dataContext);
+ ///
+ ///
+ void NavigateTo(Type type, object? dataContext = null);
}
diff --git a/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs b/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs
index 216171173..9ff4c1fa7 100644
--- a/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs
+++ b/src/Wpf.Ui/Controls/Interfaces/INavigationItem.cs
@@ -3,6 +3,7 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
+#nullable enable
using System;
using System.ComponentModel;
using System.Windows;
@@ -43,16 +44,26 @@ public interface INavigationItem
///
/// A inherited from that defines page of the item.
///
- Type PageType { get; set; }
+ Type? PageType { get; set; }
///
/// Absolute path to the XAML template based on and .
///
- Uri AbsolutePageSource { get; }
+ Uri? AbsolutePageSource { get; }
///
/// Add / Remove ClickEvent handler
///
[Category("Behavior")]
event RoutedEventHandler Click;
+
+ ///
+ /// TODO
+ ///
+ internal bool IsHidden { get; set; }
+
+ ///
+ ///
+ ///
+ internal bool WasInBreadcrumb { get; set; }
}
diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs b/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs
index d1ffefb16..40f0b4d88 100644
--- a/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs
+++ b/src/Wpf.Ui/Controls/Navigation/NavigationBackButton.cs
@@ -34,6 +34,6 @@ public INavigation? Navigation
public NavigationBackButton()
{
- SetValue(CommandProperty, new Common.RelayCommand(_ => Navigation?.NavigateBack(), () => Navigation is not null && Navigation.CanGoBack));
+ SetValue(CommandProperty, new Common.RelayCommand(_ => Navigation?.NavigateTo(".."), () => Navigation is not null && Navigation.CanGoBack));
}
}
diff --git a/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs b/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs
index bc8ea838c..4a29a17e9 100644
--- a/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs
+++ b/src/Wpf.Ui/Controls/Navigation/NavigationBase.cs
@@ -4,19 +4,19 @@
// All Rights Reserved.
#nullable enable
-#pragma warning disable CS8600
-#pragma warning disable CS8603
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Collections.Specialized;
+using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
+using CommunityToolkit.Diagnostics;
using Wpf.Ui.Common;
using Wpf.Ui.Controls.Interfaces;
using Wpf.Ui.Mvvm.Contracts;
-using Wpf.Ui.Mvvm.Interfaces;
+using Wpf.Ui.Services.Internal;
namespace Wpf.Ui.Controls.Navigation;
@@ -25,24 +25,33 @@ namespace Wpf.Ui.Controls.Navigation;
///
public abstract class NavigationBase : System.Windows.Controls.Control, INavigation
{
- ///
- /// Service used for navigation purposes.
- ///
- private readonly Services.Internal.NavigationService? _navigationService;
+ private FrameManager _frameManager = null!;
+ private NavigationManager _navigationManager = null!;
+ private IPageService? _pageService;
+ private bool _loaded;
+
+ #region DependencyProperties
///
/// Property for .
///
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(nameof(Items),
typeof(ObservableCollection), typeof(NavigationBase),
- new PropertyMetadata((ObservableCollection)null, OnItemsChanged));
+ new PropertyMetadata(null));
///
/// Property for .
///
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(nameof(Footer),
typeof(ObservableCollection), typeof(NavigationBase),
- new PropertyMetadata((ObservableCollection)null, OnFooterChanged));
+ new PropertyMetadata(null));
+
+ ///
+ /// Property for .
+ ///
+ public static readonly DependencyProperty HiddenItemsProperty = DependencyProperty.Register(nameof(HiddenItems),
+ typeof(List), typeof(NavigationBase),
+ new PropertyMetadata(null));
///
/// Property for .
@@ -57,7 +66,7 @@ public abstract class NavigationBase : System.Windows.Controls.Control, INavigat
///
public static readonly DependencyProperty FrameProperty = DependencyProperty.Register(nameof(Frame),
typeof(Frame), typeof(NavigationBase),
- new PropertyMetadata((Frame)null, OnFrameChanged));
+ new PropertyMetadata());
///
/// Property for .
@@ -98,20 +107,31 @@ public abstract class NavigationBase : System.Windows.Controls.Control, INavigat
nameof(NavigationParent), typeof(INavigation), typeof(NavigationBase),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
+ #endregion
+
+ #region Properties
+
///
public ObservableCollection Items
{
- get => GetValue(ItemsProperty) as ObservableCollection;
+ get => (ObservableCollection) GetValue(ItemsProperty);
set => SetValue(ItemsProperty, value);
}
///
public ObservableCollection Footer
{
- get => GetValue(FooterProperty) as ObservableCollection;
+ get => (ObservableCollection) GetValue(FooterProperty);
set => SetValue(FooterProperty, value);
}
+ ///
+ public List HiddenItems
+ {
+ get => (List) GetValue(HiddenItemsProperty);
+ set => SetValue(HiddenItemsProperty, value);
+ }
+
///
[Obsolete("Work in progress.")]
public Orientation Orientation
@@ -135,9 +155,9 @@ public Animations.TransitionType TransitionType
}
///
- public Frame? Frame
+ public Frame Frame
{
- get => GetValue(FrameProperty) as Frame;
+ get => (GetValue(FrameProperty) as Frame)!;
set => SetValue(FrameProperty, value);
}
@@ -161,6 +181,8 @@ internal INavigation NavigationParent
private set => SetValue(NavigationParentProperty, value);
}
+ #endregion
+
#region Events
///
@@ -176,51 +198,12 @@ public event RoutedNavigationEvent Navigated
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 bool CanGoBack => !DesignerProperties.GetIsInDesignMode(this) && _navigationManager.CanGoBack;
- ///
- public int PreviousPageIndex => _navigationService?.GetPreviousId() ?? 0;
-
- ///
- public bool CanGoBack => _navigationService is not null && _navigationService.CanGoBack;
-
- ///
- public INavigationItem? Current { get; internal set; }
+ public ObservableCollection NavigationStack => _navigationManager.NavigationStack;
///
/// Static constructor overriding default properties.
@@ -241,234 +224,75 @@ static NavigationBase()
///
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);
+ Items = new ObservableCollection();
+ Footer = new ObservableCollection();
+ HiddenItems = new List();
// 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)
+ if (DesignerProperties.GetIsInDesignMode(this))
return;
- ((FrameworkElement)Frame.Content).DataContext = dataContext;
-
- if (dataContext is IViewModel)
- ((IViewModel)dataContext).OnMounted(((FrameworkElement)Frame.Content));
+ Loaded += OnLoaded;
+ Unloaded += OnUnloaded;
}
///
- public bool SetContext(string pageTag, object dataContext)
+ public void SetIPageService(IPageService pageService)
{
- if (_navigationService == null)
- return false;
-
- return _navigationService.SetContext(pageTag, dataContext);
+ _pageService = pageService;
}
///
- public bool SetContext(int pageId, object dataContext)
- {
- if (_navigationService == null)
- return false;
+ public void Preload() => _navigationManager.Preload();
- return _navigationService.SetContext(pageId, dataContext);
- }
+ ///
+ public void ClearCache() => _navigationManager.ClearCache();
///
- public void Flush()
+ public void NavigateTo(string pageTag, object? dataContext = null)
{
- Items.Clear();
- Footer.Clear();
-
- Current = (INavigationItem)null;
+ if (_navigationManager.NavigateTo(pageTag, dataContext))
+ OnNavigated();
}
///
- public void ClearCache()
+ public void NavigateTo(Type type, object? dataContext = null)
{
- if (_navigationService == null)
- return;
-
- _navigationService.ClearCache();
+ if (_navigationManager.NavigateTo(type, dataContext))
+ OnNavigated();
}
///
- /// Updates property and modifies Active attribute of navigation items.
+ /// This virtual method is called when is loaded.
///
- private void UpdateItems()
+ protected virtual void OnLoaded(object sender, RoutedEventArgs e)
{
- var currentTag = _navigationService?.GetCurrentTag() ?? String.Empty;
-
- foreach (var singleNavigationControl in Items)
- {
- if (singleNavigationControl is not INavigationItem)
- continue;
+ Guard.IsNotNull(Frame, nameof(Frame));
- if (((INavigationItem)singleNavigationControl).PageTag == currentTag)
- {
- ((INavigationItem)singleNavigationControl).IsActive = true;
- Current = (INavigationItem)singleNavigationControl;
- }
- else
- {
- ((INavigationItem)singleNavigationControl).IsActive = false;
- }
- }
+ _frameManager = new FrameManager(Frame, TransitionDuration, TransitionType);
+ _navigationManager = new NavigationManager(Frame, _pageService, MergeItems());
- foreach (var singleNavigationControl in Footer)
+ if (SelectedPageIndex > -1)
{
- if (singleNavigationControl is not INavigationItem)
- continue;
-
- if (((INavigationItem)singleNavigationControl).PageTag == currentTag)
- {
- ((INavigationItem)singleNavigationControl).IsActive = true;
- Current = (INavigationItem)singleNavigationControl;
- }
- else
- {
- ((INavigationItem)singleNavigationControl).IsActive = false;
- }
+ _navigationManager.NavigateTo(SelectedPageIndex);
+ OnNavigated();
}
+
+ _loaded = true;
}
- ///
- /// This virtual method is called when is loaded.
- ///
- protected virtual void OnLoaded(object sender, RoutedEventArgs e)
+ protected virtual void OnUnloaded(object sender, RoutedEventArgs e)
{
- UpdateServiceItems();
-
- if (PageService == null && Frame != null && SelectedPageIndex > -1)
- Navigate(SelectedPageIndex);
+ Loaded -= OnLoaded;
+ Unloaded -= OnUnloaded;
- // 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.");
+ foreach (var item in _navigationManager.NavigationItems)
+ item.Click -= OnNavigationItemClicked;
- // TODO: Precache
- //await PrecacheInstances();
- }
+ _frameManager.Dispose();
+ _navigationManager.Dispose();
}
///
@@ -521,25 +345,9 @@ static void MoveFocus(FrameworkElement element, FocusNavigationDirection directi
///
protected virtual void OnNavigated()
{
- var newEvent = new RoutedNavigationEventArgs(NavigatedEvent, this, Current);
- RaiseEvent(newEvent);
- }
+ var navigatedFrom = _navigationManager.History.Count > 1 ? _navigationManager.NavigationItems[_navigationManager.History[_navigationManager.History.Count - 2]] : null;
- ///
- /// 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);
+ var newEvent = new RoutedNavigationEventArgs(NavigatedEvent, this, navigatedFrom, NavigationStack[NavigationStack.Count - 1]);
RaiseEvent(newEvent);
}
@@ -551,94 +359,7 @@ 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);
+ NavigateTo(navigationItem.PageTag);
}
private static void OnTransitionDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -646,10 +367,8 @@ private static void OnTransitionDurationChanged(DependencyObject d, DependencyPr
if (d is not NavigationBase navigation)
return;
- if (navigation._navigationService == null)
- return;
-
- navigation._navigationService.TransitionDuration = (int)e.NewValue;
+ if (navigation._loaded)
+ navigation._frameManager.TransitionDuration = (int)e.NewValue;
}
private static void OnTransitionTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -657,10 +376,8 @@ private static void OnTransitionTypeChanged(DependencyObject d, DependencyProper
if (d is not NavigationBase navigation)
return;
- if (navigation._navigationService == null)
- return;
-
- navigation._navigationService.TransitionType = (Animations.TransitionType)e.NewValue;
+ if (navigation._loaded)
+ navigation._frameManager.TransitionType = (Animations.TransitionType)e.NewValue;
}
///
@@ -674,49 +391,28 @@ private static void OnTransitionTypeChanged(DependencyObject d, DependencyProper
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()
+ private INavigationItem[] MergeItems()
{
- var navigationItems = GetValue(ItemsProperty) as ObservableCollection ?? new ObservableCollection { };
- var navigationFooter = GetValue(FooterProperty) as ObservableCollection ?? new ObservableCollection { };
+ List buffer = new List(Items.Count);
- if (_navigationService != null)
- _navigationService.UpdateItems(navigationItems, navigationFooter);
- }
+ AddToBuffer(Items);
+ AddToBuffer(Footer);
+ AddToBuffer(HiddenItems, item => item.IsHidden = true);
- private void NavigateInternal(int arg, bool updateItems)
- {
- SelectedPageIndex = _navigationService?.GetCurrentId() ?? +arg;
+ return buffer.ToArray();
- if (updateItems)
- UpdateItems();
+ void AddToBuffer(IEnumerable
public interface INavigationService
{
+
+ ///
+ /// TODO
+ ///
+ ///
+ ///
+ void Initialize(INavigation navigation, IPageService pageService);
+
///
/// Provides direct access to the used in navigation.
///
@@ -38,7 +47,6 @@ public interface INavigationService
///
/// Instance of the .
void SetNavigationControl(INavigation navigation);
-
///
/// Lets you attach the service that delivers page instances to .
///
@@ -46,20 +54,16 @@ public interface INavigationService
void SetPageService(IPageService pageService);
///
- /// Lets you navigate to the selected page based on it's type. Should be used with .
- ///
- /// of the page.
- bool Navigate(Type pageType);
-
- ///
- /// Lets you navigate to the selected page based on it's id. Should be used with .
+ /// Lets you navigate to the selected page based on it's tag. Should be used with .
///
- /// Id of the page.
- bool Navigate(int pageId);
+ /// Tag of the page.
+ ///
+ void NavigateTo(string pageTag, object? dataContext = null);
///
- /// Lets you navigate to the selected page based on it's tag. Should be used with .
+ /// TODO
///
- /// Tag of the page.
- bool Navigate(string pageTag);
+ ///
+ ///
+ void NavigateTo(Type type, object? dataContext = null);
}
diff --git a/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs b/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs
index 6d46035a2..62515ebe4 100644
--- a/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs
+++ b/src/Wpf.Ui/Mvvm/Contracts/INavigationWindow.cs
@@ -3,6 +3,7 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
+#nullable enable
using System;
using System.Windows.Controls;
using Wpf.Ui.Controls.Interfaces;
@@ -27,12 +28,6 @@ public interface INavigationWindow
/// Instance of the control.
INavigation GetNavigation();
- ///
- /// Lets you navigate to the selected page based on it's type. Should be used with .
- ///
- /// of the page.
- bool Navigate(Type pageType);
-
///
/// Lets you attach the service that delivers page instances to .
///
@@ -48,4 +43,18 @@ public interface INavigationWindow
/// Triggers the command to close a window.
///
void CloseWindow();
+
+ ///
+ /// TODO
+ ///
+ ///
+ ///
+ void NavigateTo(string pageTag, object? dataContext = null);
+
+ ///
+ /// TODO
+ ///
+ ///
+ ///
+ void NavigateTo(Type type, object? dataContext = null);
}
diff --git a/src/Wpf.Ui/Mvvm/Services/NavigationService.cs b/src/Wpf.Ui/Mvvm/Services/NavigationService.cs
index 4818c102d..0d973fddc 100644
--- a/src/Wpf.Ui/Mvvm/Services/NavigationService.cs
+++ b/src/Wpf.Ui/Mvvm/Services/NavigationService.cs
@@ -3,8 +3,10 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.
+#nullable enable
using System;
using System.Windows.Controls;
+using CommunityToolkit.Diagnostics;
using Wpf.Ui.Controls.Interfaces;
using Wpf.Ui.Mvvm.Contracts;
@@ -15,28 +17,41 @@ namespace Wpf.Ui.Mvvm.Services;
///
public partial class NavigationService : INavigationService
{
+ private INavigation? _navigationControl;
+
///
/// Locally attached page service.
///
- private IPageService _pageService;
+ private IPageService? _pageService;
///
/// Control representing navigation.
///
- protected INavigation NavigationControl;
+ private INavigation NavigationControl
+ {
+ get
+ {
+ Guard.IsNotNull(_navigationControl, nameof(NavigationControl));
+ return _navigationControl;
+ }
+ set => _navigationControl = value;
+ }
+
+ public void Initialize(INavigation navigation, IPageService pageService)
+ {
+ NavigationControl = navigation;
+ NavigationControl.SetIPageService(pageService);
+ }
///
public Frame GetFrame()
{
- return NavigationControl?.Frame;
+ return NavigationControl.Frame;
}
///
public void SetFrame(Frame frame)
{
- if (NavigationControl == null)
- return;
-
NavigationControl.Frame = frame;
}
@@ -46,52 +61,34 @@ public INavigation GetNavigationControl()
return NavigationControl;
}
- ///
public void SetNavigationControl(INavigation navigation)
{
NavigationControl = navigation;
- if (_pageService != null)
- NavigationControl.PageService = _pageService;
+ if (_pageService is not null)
+ NavigationControl.SetIPageService(_pageService);
}
- ///
public void SetPageService(IPageService pageService)
{
- if (NavigationControl == null)
+ if (_navigationControl is null)
{
_pageService = pageService;
-
return;
}
- NavigationControl.PageService = pageService;
- }
-
- ///
- public bool Navigate(Type pageType)
- {
- if (NavigationControl == null)
- return false;
-
- return NavigationControl.Navigate(pageType);
+ NavigationControl.SetIPageService(pageService);
}
///
- public bool Navigate(int pageId)
+ public void NavigateTo(string pageTag, object? dataContext = null)
{
- if (NavigationControl == null)
- return false;
-
- return NavigationControl.Navigate(pageId);
+ NavigationControl.NavigateTo(pageTag, dataContext);
}
///
- public bool Navigate(string pageTag)
+ public void NavigateTo(Type type, object? dataContext = null)
{
- if (NavigationControl == null)
- return false;
-
- return NavigationControl.Navigate(pageTag);
+ NavigationControl.NavigateTo(type, dataContext);
}
}
diff --git a/src/Wpf.Ui/Services/Internal/FrameManager.cs b/src/Wpf.Ui/Services/Internal/FrameManager.cs
new file mode 100644
index 000000000..d2fce91e0
--- /dev/null
+++ b/src/Wpf.Ui/Services/Internal/FrameManager.cs
@@ -0,0 +1,81 @@
+#nullable enable
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Navigation;
+using Wpf.Ui.Animations;
+using Wpf.Ui.Common.Interfaces;
+
+namespace Wpf.Ui.Services.Internal;
+
+internal sealed class FrameManager : IDisposable
+{
+ private readonly Frame _frame;
+
+ public int TransitionDuration { get; set; }
+ public TransitionType TransitionType { get; set; }
+
+ public FrameManager(Frame frame, int transitionDuration, TransitionType transitionType)
+ {
+ _frame = frame;
+ TransitionDuration = transitionDuration;
+ TransitionType = transitionType;
+
+ _frame.NavigationUIVisibility = NavigationUIVisibility.Hidden;
+
+ _frame.Navigating += OnFrameNavigating;
+ _frame.Navigated += OnFrameNavigated;
+ }
+
+ public void Dispose()
+ {
+ _frame.Navigating -= OnFrameNavigating;
+ _frame.Navigated -= OnFrameNavigated;
+ }
+
+ private void OnFrameNavigating(object sender, NavigatingCancelEventArgs e)
+ {
+ NotifyFrameContentAboutLeave();
+ }
+
+ private void OnFrameNavigated(object sender, NavigationEventArgs e)
+ {
+ _frame.NavigationService.RemoveBackEntry();
+
+ if (TransitionDuration > 0 && e.Content != null)
+ Transitions.ApplyTransition(e.Content, TransitionType, TransitionDuration);
+
+ //TODO
+ /*// 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();
+ }
+
+ private void NotifyFrameContentAboutEnter()
+ {
+ var navigationAware = GetINavigationAwareOrDefault();
+ navigationAware?.OnNavigatedTo();
+ }
+
+ private void NotifyFrameContentAboutLeave()
+ {
+ var navigationAware = GetINavigationAwareOrDefault();
+ navigationAware?.OnNavigatedFrom();
+ }
+
+ private INavigationAware? GetINavigationAwareOrDefault()
+ {
+ INavigationAware? navigationAware = _frame.Content switch
+ {
+ INavigationAware aware => aware,
+ INavigableView