diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 52df96d..8b6dead 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -75,7 +75,7 @@ jobs:
- name: Publish WPF NuGet package
run: |
if (Test-Path "./nugets/*.nupkg") {
- dotnet nuget push "./nugets/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+ dotnet nuget push ".\nugets\*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
}
- name: Upload WPF NuGet Artifact
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/AssemblyInfo.cs b/src/Lemon.ModuleNavigation.Avaloniaui/AssemblyInfo.cs
index b41cdfb..b93bbf1 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/AssemblyInfo.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/AssemblyInfo.cs
@@ -2,4 +2,5 @@
[assembly: XmlnsPrefix("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "lm")]
[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation")]
-[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Avaloniaui")]
\ No newline at end of file
+[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Avaloniaui")]
+[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Avaloniaui.Regions")]
\ No newline at end of file
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/DialogService.cs b/src/Lemon.ModuleNavigation.Avaloniaui/DialogService.cs
index 8768985..6d527dd 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/DialogService.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/DialogService.cs
@@ -1,7 +1,7 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
using Microsoft.Extensions.DependencyInjection;
namespace Lemon.ModuleNavigation.Avaloniaui;
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/Lemon.ModuleNavigation.Avaloniaui.csproj b/src/Lemon.ModuleNavigation.Avaloniaui/Lemon.ModuleNavigation.Avaloniaui.csproj
index eced45c..ce7dc98 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/Lemon.ModuleNavigation.Avaloniaui.csproj
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/Lemon.ModuleNavigation.Avaloniaui.csproj
@@ -15,9 +15,13 @@
+
+
+
+
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/NavigationExtension.cs b/src/Lemon.ModuleNavigation.Avaloniaui/NavigationExtension.cs
index 19f3fcc..685acac 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/NavigationExtension.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/NavigationExtension.cs
@@ -36,7 +36,7 @@ void LoadedHandler(object? sender, RoutedEventArgs e)
{
var serviceProvider = navigationProvider!.ServiceProvider;
var handler = serviceProvider.GetRequiredService();
- handler.RegionManager.AddRegion(currentValue, control.ToContainer(currentValue));
+ handler.RegionManager.AddRegion(currentValue, control.ToRegion(currentValue));
}
control.Loaded -= LoadedHandler;
}
@@ -112,7 +112,7 @@ public static void SetModuleContainerName(Control control, string value)
#region CanUnloadProperty
public static readonly AttachedProperty CanUnloadProperty =
AvaloniaProperty.RegisterAttached("CanUnload",
- defaultValue: true,
+ defaultValue: false,
coerce: CoerceCanUnload);
private static bool CoerceCanUnload(AvaloniaObject targetObject, bool currentValue)
@@ -160,17 +160,23 @@ private static void UnloadModule(object? sender, RoutedEventArgs e)
{
if (sender is Button button)
{
+ var view = button.FindLogicalAncestorOfType(includeSelf: false);
+ if (view != null)
+ {
+ }
var tabItem = button.FindLogicalAncestorOfType(includeSelf: false) ?? throw new InvalidOperationException($"There is no 'TabItem' found in parents of {button}");
var tabContainer = tabItem.FindLogicalAncestorOfType(includeSelf: false);
if (tabContainer != null)
{
- IModule item = tabItem.DataContext as IModule ?? throw new InvalidOperationException($"The DataContext of tabItem is not derived from IModule");
- if (item.CanUnload)
+ if (tabItem.DataContext is IModule item)
{
- if (tabContainer.DataContext is IServiceAware serviceAware)
+ if (item.CanUnload)
{
- var handler = serviceAware.ServiceProvider.GetRequiredService();
- handler.ModuleManager.ActiveModules.Remove(item);
+ if (tabContainer.DataContext is IServiceAware serviceAware)
+ {
+ var handler = serviceAware.ServiceProvider.GetRequiredService();
+ handler.ModuleManager.ActiveModules.Remove(item);
+ }
}
}
}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ContentRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ContentRegion.cs
new file mode 100644
index 0000000..806709a
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ContentRegion.cs
@@ -0,0 +1,117 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Markup.Xaml.Templates;
+using Lemon.ModuleNavigation.Abstractions;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Lemon.ModuleNavigation.Avaloniaui.Regions;
+
+public class ContentRegion : Region, IContentRegionContext
+{
+ private readonly ContentControl _contentControl;
+ public ContentRegion(string name, ContentControl contentControl) : base(name)
+ {
+ _contentControl = contentControl;
+ SetBindingContentTemplate();
+ SetBindingContent();
+ }
+
+ private object? _content;
+ public object? Content
+ {
+ get => _content;
+ set
+ {
+ _content = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private IDataTemplate? _contentTemplate;
+ public IDataTemplate? ContentTemplate
+ {
+ get => _contentTemplate;
+ set
+ {
+ _contentTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ ///
+ /// When Views with same ViewName were found, the latest one will be picked.
+ ///
+ ///
+ public override void Activate(NavigationContext target)
+ {
+ if (ViewCache.TryGetValue(target, out IView? accurateView))
+ {
+ target.View = accurateView;
+ Content = target;
+ }
+ else if (ViewNameCache.TryGetValue(target.ViewName, out IView? view)
+ && view.DataContext is INavigationAware navigationAware
+ && navigationAware.IsNavigationTarget(target))
+ {
+ var context = Contexts.First(c => c.ViewName == target.ViewName);
+ context.View = view;
+ Content = context;
+ }
+ else
+ {
+ Contexts.Add(target);
+ Content = target;
+ }
+ }
+
+ public override void DeActivate(string regionName)
+ {
+ if (Content is NavigationContext current)
+ {
+ if (current.ViewName == regionName)
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ if (Content is NavigationContext current)
+ {
+ if (NavigationContext.ViewNameComparer.Equals(current, navigationContext))
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
+ }
+
+ protected virtual void SetBindingContentTemplate()
+ {
+ ContentTemplate = RegionContentTemplate;
+ _contentControl.Bind(ContentControl.ContentTemplateProperty,
+ new Binding(nameof(ContentTemplate))
+ {
+ Source = this,
+ Mode = BindingMode.TwoWay
+ });
+ }
+ protected virtual void SetBindingContent()
+ {
+ _contentControl.Bind(ContentControl.ContentProperty,
+ new Binding(nameof(Content))
+ {
+ Source = this,
+ Mode = BindingMode.TwoWay
+ });
+ }
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ItemsRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ItemsRegion.cs
new file mode 100644
index 0000000..3ac3d7d
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/ItemsRegion.cs
@@ -0,0 +1,134 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Markup.Xaml.Templates;
+using Lemon.ModuleNavigation.Abstractions;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Lemon.ModuleNavigation.Avaloniaui.Regions;
+
+public class ItemsRegion : Region, IItemsRegionDataContext
+{
+ private readonly ItemsControl _itemsControl;
+ public ItemsRegion(string name, ItemsControl itemsControl) : base(name)
+ {
+ _itemsControl = itemsControl;
+ SetBindingItemTemplate();
+ SetBindingSelectedItem();
+ SetBindingItemsSource();
+ }
+ private object? _selectItem;
+ public object? SelectedItem
+ {
+ get
+ {
+ return _selectItem;
+ }
+ set
+ {
+ _selectItem = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private IDataTemplate? _itemsTemplate;
+ public IDataTemplate? ItemTemplate
+ {
+ get => _itemsTemplate;
+ set
+ {
+ _itemsTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public override void ScrollIntoView(int index)
+ {
+ _itemsControl.ScrollIntoView(index);
+ }
+ public override void ScrollIntoView(NavigationContext item)
+ {
+ _itemsControl.ScrollIntoView(item);
+ }
+
+ ///
+ /// When Views with same ViewName were found, the earliest one will be picked.
+ ///
+ ///
+ public override void Activate(NavigationContext target)
+ {
+ try
+ {
+ if (ViewCache.TryGetValue(target, out IView? accurateView))
+ {
+ target.View = accurateView;
+ SelectedItem = target;
+ return;
+ }
+ var context = Contexts.FirstOrDefault(c => c.ViewName == target.ViewName);
+ if (context is not null
+ && context.View is not null
+ && context.View.DataContext is INavigationAware navigationAware
+ && navigationAware.IsNavigationTarget(target))
+ {
+ SelectedItem = context;
+ return;
+ }
+ Contexts.Add(target);
+ SelectedItem = target;
+ }
+ finally
+ {
+ ScrollIntoView((SelectedItem as NavigationContext)!);
+ }
+ }
+ public override void DeActivate(string viewName)
+ {
+ Contexts.Remove(Contexts.Last(c => c.ViewName == viewName));
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ Contexts.Remove(navigationContext);
+ }
+ public void Add(NavigationContext item)
+ {
+ Contexts.Add(item);
+ }
+
+ protected virtual void SetBindingItemsSource()
+ {
+ _itemsControl.Bind(ItemsControl.ItemsSourceProperty,
+ new Binding(nameof(Contexts))
+ {
+ Source = this
+ });
+ }
+ protected virtual void SetBindingItemTemplate()
+ {
+ ItemTemplate = RegionContentTemplate;
+ _itemsControl.Bind(ItemsControl.ItemTemplateProperty,
+ new Binding(nameof(ItemTemplate))
+ {
+ Source = this
+ });
+ }
+ protected virtual void SetBindingSelectedItem()
+ {
+ if (_itemsControl is SelectingItemsControl selector)
+ {
+ selector.Bind(SelectingItemsControl.SelectedItemProperty,
+ new Binding(nameof(SelectedItem))
+ {
+ Source = this,
+ Mode = BindingMode.TwoWay
+ });
+ }
+ }
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/Regions/Region.cs b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/Region.cs
new file mode 100644
index 0000000..58377d5
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/Region.cs
@@ -0,0 +1,151 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Lemon.ModuleNavigation.Abstractions;
+using Lemon.ModuleNavigation.Core;
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Concurrent;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace Lemon.ModuleNavigation.Avaloniaui.Regions;
+
+public abstract class Region : IRegion
+{
+ public Region(string name)
+ {
+ Name = name;
+ Current = new();
+ ViewCache = [];
+ Contexts = [];
+ RegionContentTemplate = CreateRegionDataTemplate();
+ Contexts.CollectionChanged += Contexts_CollectionChanged;
+ }
+ protected ConcurrentItem<(IView View, INavigationAware NavigationAware)> Current
+ {
+ get;
+ }
+ public string Name
+ {
+ get;
+ }
+ protected ConcurrentDictionary ViewCache
+ {
+ get;
+ }
+
+ protected ConcurrentDictionary ViewNameCache
+ {
+ get
+ {
+ return new(Contexts
+ .GroupBy(kv => kv.ViewName)
+ .Select(group => new KeyValuePair(
+ group.Key,
+ group.Last().View!)));
+ }
+ }
+ public ObservableCollection Contexts
+ {
+ get;
+ }
+
+ public IDataTemplate? RegionContentTemplate
+ {
+ get;
+ }
+ public virtual void ScrollIntoView(int index)
+ {
+ throw new NotImplementedException();
+ }
+ public virtual void ScrollIntoView(NavigationContext item)
+ {
+ throw new NotImplementedException();
+ }
+ public abstract void Activate(NavigationContext target);
+ public abstract void DeActivate(string viewName);
+ public abstract void DeActivate(NavigationContext target);
+
+ protected IView? ResolveView(NavigationContext context)
+ {
+ var view = context.View;
+ if (view is null)
+ {
+ view = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+ var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+
+ if (Current.TryTakeData(out var previousData))
+ {
+ previousData.NavigationAware.OnNavigatedFrom(context);
+ }
+
+ view.DataContext = navigationAware;
+ navigationAware.OnNavigatedTo(context);
+ navigationAware.RequestUnload += () =>
+ {
+ DeActivate(context);
+ };
+ Current.SetData((view, navigationAware));
+ context.View = view;
+ ViewCache.AddOrUpdate(context, view, (key, value) => view);
+ }
+ return view;
+ }
+
+ protected virtual void WhenContextsAdded(IEnumerable contexts)
+ {
+
+ }
+ protected virtual void WhenContextsRemoved(IEnumerable contexts)
+ {
+
+ }
+ private void Contexts_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ if (e.NewItems is not null)
+ {
+ WhenContextsAdded(e.NewItems.Cast());
+ }
+ }
+ if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ if (e.OldItems is not null)
+ {
+ WhenContextsRemoved(e.OldItems.Cast());
+ }
+ }
+ }
+ private IDataTemplate CreateRegionDataTemplate()
+ {
+ return new FuncDataTemplate((context, np) =>
+ {
+ if (context is null)
+ {
+ return null;
+ }
+ var view = context.View;
+ if (view is null)
+ {
+ view = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+ var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+
+ if (Current.TryTakeData(out var previousData))
+ {
+ previousData.NavigationAware.OnNavigatedFrom(context);
+ }
+
+ view.DataContext = navigationAware;
+ navigationAware.OnNavigatedTo(context);
+ navigationAware.RequestUnload += () =>
+ {
+ DeActivate(context);
+ };
+ Current.SetData((view, navigationAware));
+ context.View = view;
+ ViewCache.AddOrUpdate(context, view, (key, value) => view);
+ }
+ return view as Control;
+ });
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/Regions/TabRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/TabRegion.cs
new file mode 100644
index 0000000..015d89c
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/Regions/TabRegion.cs
@@ -0,0 +1,48 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+using Avalonia.Markup.Xaml.Templates;
+using Lemon.ModuleNavigation.Abstractions;
+
+namespace Lemon.ModuleNavigation.Avaloniaui.Regions;
+
+public class TabRegion : ItemsRegion, IContentRegionContext
+{
+ private readonly TabControl _tabControl;
+ public TabRegion(string name, TabControl tabControl) : base(name, tabControl)
+ {
+ _tabControl = tabControl;
+ SetBindingContentTemplate();
+ }
+
+ public object? Content
+ {
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
+ }
+
+ private IDataTemplate? _contentTemplate;
+ public IDataTemplate? ContentTemplate
+ {
+ get => _contentTemplate;
+ set
+ {
+ _contentTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+ protected override void SetBindingItemTemplate()
+ {
+ //base.SetBindingItemTemplate();
+ }
+ protected virtual void SetBindingContentTemplate()
+ {
+ ContentTemplate = RegionContentTemplate;
+ _tabControl.Bind(TabControl.ContentTemplateProperty,
+ new Binding(nameof(ContentTemplate))
+ {
+ Mode = BindingMode.TwoWay,
+ Source = this
+ });
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/RegionsExtension.cs b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsExtension.cs
index 19782bc..93cf0eb 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/RegionsExtension.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsExtension.cs
@@ -1,17 +1,18 @@
using Avalonia.Controls;
using Lemon.ModuleNavigation.Abstractions;
+using Lemon.ModuleNavigation.Avaloniaui.Regions;
namespace Lemon.ModuleNavigation.Avaloniaui;
public static class RegionsExtension
{
- public static IRegion ToContainer(this Control control, string name)
+ public static IRegion ToRegion(this Control control, string name)
{
return control switch
{
- TabControl tabControl => new TabRegion(tabControl, name),
- ItemsControl itemsControl => new ItemsRegion(itemsControl, name),
- ContentControl contentControl => new ContentRegion(contentControl, name),
+ TabControl tabControl => new TabRegion(name, tabControl),
+ ItemsControl itemsControl => new ItemsRegion(name, itemsControl),
+ ContentControl contentControl => new ContentRegion(name, contentControl),
_ => throw new NotSupportedException($"Unsupported control:{control.GetType()}"),
};
}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/ContentRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ContentRegion.cs
similarity index 56%
rename from src/Lemon.ModuleNavigation.Avaloniaui/ContentRegion.cs
rename to src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ContentRegion.cs
index c2973b3..9b045c1 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/ContentRegion.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ContentRegion.cs
@@ -1,11 +1,16 @@
using Avalonia.Controls;
+using Lemon.ModuleNavigation.Abstractions;
+using Lemon.ModuleNavigation.Core;
+using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
-namespace Lemon.ModuleNavigation.Avaloniaui;
+namespace Lemon.ModuleNavigation.Avaloniaui.RegionsOld;
-public class ContentRegion : AvaloniauiRegion
+public class ContentRegion : RegionBak
{
+ private readonly ConcurrentDictionary _viewCache = new();
+ private readonly ConcurrentItem<(IView View, INavigationAware NavigationAware)> _current = new();
private readonly ContentControl _contentControl;
public ContentRegion(ContentControl contentControl, string name) : base()
{
@@ -37,18 +42,37 @@ public override void Activate(NavigationContext target)
{
if(Content is NavigationContext current)
{
- if (target.TargetViewName == current.TargetViewName
+ if (target.ViewName == current.ViewName
&& !target.RequestNew)
{
return;
}
}
Content = target;
+ Contexts.Add(target);
}
- public override void DeActivate(NavigationContext target)
+ public override void DeActivate(string regionName)
{
- Content = null;
+ if (Content is NavigationContext current)
+ {
+ if (current.ViewName == regionName)
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ if (Content is NavigationContext current)
+ {
+ if (current == navigationContext)
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
}
private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/ItemsRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ItemsRegion.cs
similarity index 87%
rename from src/Lemon.ModuleNavigation.Avaloniaui/ItemsRegion.cs
rename to src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ItemsRegion.cs
index 30d4eea..65702b5 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/ItemsRegion.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/ItemsRegion.cs
@@ -3,9 +3,9 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
-namespace Lemon.ModuleNavigation.Avaloniaui;
+namespace Lemon.ModuleNavigation.Avaloniaui.RegionsOld;
-public class ItemsRegion : AvaloniauiRegion
+public class ItemsRegion : RegionBak
{
private readonly ItemsControl _itemsControl;
public ItemsRegion(ItemsControl itemsControl, string name)
@@ -84,9 +84,13 @@ public override void Activate(NavigationContext target)
SelectedItem = target;
}
}
- public override void DeActivate(NavigationContext target)
+ public override void DeActivate(string viewName)
{
- Contexts.Remove(target);
+ Contexts.Remove(Contexts.Last(c => c.ViewName == viewName));
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ Contexts.Remove(navigationContext);
}
public void Add(NavigationContext item)
{
@@ -110,7 +114,7 @@ private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChan
{
foreach (var item in e.OldItems)
{
- _itemsControl.Items.Remove(e.OldItems);
+ _itemsControl.Items.Remove(item);
}
}
}
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/AvaloniauiRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/RegionBak.cs
similarity index 79%
rename from src/Lemon.ModuleNavigation.Avaloniaui/AvaloniauiRegion.cs
rename to src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/RegionBak.cs
index a07d3c0..4eb43f9 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/AvaloniauiRegion.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/RegionBak.cs
@@ -6,14 +6,14 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
-namespace Lemon.ModuleNavigation.Avaloniaui;
+namespace Lemon.ModuleNavigation.Avaloniaui.RegionsOld;
-public abstract class AvaloniauiRegion : IRegion
+public abstract class RegionBak : IRegion
{
private readonly ConcurrentDictionary _viewCache = new();
private readonly ConcurrentItem<(IView View, INavigationAware NavigationAware)> _current = new();
- public AvaloniauiRegion()
+ public RegionBak()
{
RegionTemplate = CreateRegionDataTemplate();
}
@@ -24,6 +24,7 @@ public AvaloniauiRegion()
public IDataTemplate? RegionTemplate { get; set; }
public abstract void Activate(NavigationContext target);
+ public abstract void DeActivate(string viewName);
public abstract void DeActivate(NavigationContext target);
private IDataTemplate CreateRegionDataTemplate()
@@ -34,12 +35,12 @@ private IDataTemplate CreateRegionDataTemplate()
return null;
bool needNewView = context.RequestNew ||
- !_viewCache.TryGetValue(context.TargetViewName, out IView? view);
+ !_viewCache.TryGetValue(context.ViewName, out IView? view);
if (needNewView)
{
- view = context.ServiceProvider.GetRequiredKeyedService(context.TargetViewName);
- var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.TargetViewName);
+ view = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+ var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
if (_current.TryTakeData(out var previousData))
{
@@ -51,11 +52,11 @@ private IDataTemplate CreateRegionDataTemplate()
view.DataContext = navigationAware;
navigationAware.OnNavigatedTo(context);
_current.SetData((view, navigationAware));
- _viewCache.TryAdd(context.TargetViewName, view);
+ _viewCache.TryAdd(context.ViewName, view);
}
else
{
- view = _viewCache[context.TargetViewName];
+ view = _viewCache[context.ViewName];
}
return view as Control;
diff --git a/src/Lemon.ModuleNavigation.Avaloniaui/TabRegion.cs b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/TabRegion.cs
similarity index 54%
rename from src/Lemon.ModuleNavigation.Avaloniaui/TabRegion.cs
rename to src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/TabRegion.cs
index 5b5b460..c5b5070 100644
--- a/src/Lemon.ModuleNavigation.Avaloniaui/TabRegion.cs
+++ b/src/Lemon.ModuleNavigation.Avaloniaui/RegionsOld/TabRegion.cs
@@ -1,40 +1,64 @@
using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
-namespace Lemon.ModuleNavigation.Avaloniaui;
+namespace Lemon.ModuleNavigation.Avaloniaui.RegionsOld;
-public class TabRegion : AvaloniauiRegion
+public class TabRegion : RegionBak, INotifyPropertyChanged
{
private readonly TabControl _tabControl;
- public TabRegion(TabControl tabControl, string name)
+ public TabRegion(TabControl tabControl, string name)
{
_tabControl = tabControl;
- _tabControl.ContentTemplate = RegionTemplate;
Contexts = [];
- Contexts.CollectionChanged += ViewContents_CollectionChanged;
+ //Contexts.CollectionChanged += ViewContents_CollectionChanged;
+
+
+ _tabControl.Bind(SelectingItemsControl.SelectedItemProperty,
+ new Binding(nameof(SelectedItem))
+ {
+ Mode = BindingMode.TwoWay,
+ Source = this
+ });
+ _tabControl.Bind(ItemsControl.ItemsSourceProperty,
+ new Binding(nameof(Contexts))
+ {
+ Source = this
+ });
+ _tabControl.ContentTemplate = RegionTemplate;
+
Name = name;
}
public override string Name
{
get;
}
- public object? SelectedItem
+ private NavigationContext? _selectItem;
+ public NavigationContext? SelectedItem
{
get
{
- return _tabControl.SelectedItem;
+ return _selectItem;
}
set
{
- _tabControl.SelectedItem = value;
+ _selectItem = value;
+ OnPropertyChanged();
}
}
public override ObservableCollection Contexts
{
get;
}
-
+ public event PropertyChangedEventHandler? PropertyChanged;
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
public override void Activate(NavigationContext target)
{
if (!target.RequestNew)
@@ -63,9 +87,13 @@ public override void Activate(NavigationContext target)
SelectedItem = target;
}
}
- public override void DeActivate(NavigationContext target)
+ public override void DeActivate(string viewName)
+ {
+ Contexts.Remove(Contexts.Last(c => c.ViewName == viewName));
+ }
+ public override void DeActivate(NavigationContext navigationContext)
{
- Contexts.Remove(target);
+ Contexts.Remove(navigationContext);
}
public void Add(NavigationContext item)
{
@@ -89,7 +117,7 @@ private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChan
{
foreach (var item in e.OldItems)
{
- _tabControl.Items.Remove(e.OldItems);
+ _tabControl.Items.Remove(item);
}
}
}
diff --git a/src/Lemon.ModuleNavigation.Sample/ModuleAs/ViewModelA.cs b/src/Lemon.ModuleNavigation.Sample/ModuleAs/ViewModelA.cs
index 46305a9..08513b2 100644
--- a/src/Lemon.ModuleNavigation.Sample/ModuleAs/ViewModelA.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ModuleAs/ViewModelA.cs
@@ -5,7 +5,7 @@
namespace Lemon.ModuleNavigation.Sample.ModuleAs;
-public class ViewModelA : SampleViewModelBase, IModuleNavigationAware
+public class ViewModelA : BaseNavigationViewModel, IModuleNavigationAware
{
private readonly NavigationService _navigationService;
public ViewModelA(NavigationService navigationService)
diff --git a/src/Lemon.ModuleNavigation.Sample/ModuleBs/ViewModelB.cs b/src/Lemon.ModuleNavigation.Sample/ModuleBs/ViewModelB.cs
index 7d1a96c..ca58f6a 100644
--- a/src/Lemon.ModuleNavigation.Sample/ModuleBs/ViewModelB.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ModuleBs/ViewModelB.cs
@@ -5,7 +5,7 @@
namespace Lemon.ModuleNavigation.Sample.ModuleBs;
-public class ViewModelB : SampleViewModelBase, IModuleNavigationAware
+public class ViewModelB : BaseNavigationViewModel, IModuleNavigationAware
{
private readonly NavigationService _navigationService;
public ViewModelB(NavigationService navigationService)
diff --git a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModelC.cs b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModelC.cs
index accbfb9..a66c653 100644
--- a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModelC.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModelC.cs
@@ -6,7 +6,7 @@
namespace Lemon.ModuleNavigation.Sample.ModuleCs
{
- public class ViewModelC : SampleViewModelBase, IModuleNavigationAware, IServiceAware
+ public class ViewModelC : BaseNavigationViewModel, IModuleNavigationAware, IServiceAware
{
private readonly IModuleNavigationService _navigationService;
private readonly IServiceProvider _moduleServiceProvider;
diff --git a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel01.cs b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel01.cs
index 60d7fb1..07e14c0 100644
--- a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel01.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel01.cs
@@ -6,7 +6,7 @@
namespace Lemon.ModuleNavigation.Sample.ModuleCs.ViewModels;
-public class SubViewModel01 : SampleViewModelBase, IModuleNavigationAware
+public class SubViewModel01 : BaseNavigationViewModel, IModuleNavigationAware
{
private readonly ILogger _logger;
public SubViewModel01(IServiceProvider serviceProvider, IServiceProviderDecorator appServiceProvider)
diff --git a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel02.cs b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel02.cs
index ce32cef..309c335 100644
--- a/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel02.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ModuleCs/ViewModels/SubViewModel02.cs
@@ -4,7 +4,7 @@
namespace Lemon.ModuleNavigation.Sample.ModuleCs.ViewModels;
-public class SubViewModel02 : SampleViewModelBase, IModuleNavigationAware
+public class SubViewModel02 : BaseNavigationViewModel, IModuleNavigationAware
{
public SubViewModel02(IServiceProvider serviceProvider)
{
diff --git a/src/Lemon.ModuleNavigation.Sample/ViewModels/BaseNavigationViewModel.cs b/src/Lemon.ModuleNavigation.Sample/ViewModels/BaseNavigationViewModel.cs
new file mode 100644
index 0000000..4eb964d
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Sample/ViewModels/BaseNavigationViewModel.cs
@@ -0,0 +1,56 @@
+using Lemon.ModuleNavigation.Abstractions;
+using ReactiveUI;
+using System;
+using System.Diagnostics;
+using System.Reactive;
+
+namespace Lemon.ModuleNavigation.Sample.ViewModels;
+
+public class BaseNavigationViewModel : ReactiveObject, INavigationAware, IDisposable
+{
+ public virtual string Greeting => $"Welcome to {GetType().Name}[{Environment.ProcessId}][{Environment.CurrentManagedThreadId}]{Environment.NewLine}{DateTime.Now:yyyy-MM-dd HH-mm-ss.ffff}";
+
+
+ public BaseNavigationViewModel()
+ {
+ UnloadViewCommand = ReactiveCommand.Create(() =>
+ {
+ var code = this.GetHashCode();
+ Debug.WriteLine(code);
+ RequestUnload?.Invoke();
+ });
+ }
+ public ReactiveCommand UnloadViewCommand
+ {
+ get;
+ }
+
+ public event Action? RequestUnload;
+
+ public virtual void Dispose()
+ {
+
+ }
+
+ public virtual bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ if (navigationContext.Parameters is not null)
+ {
+ if (navigationContext.Parameters.TryGetValue("requestNew", out bool requestNew))
+ {
+ return !requestNew;
+ }
+ }
+ return true;
+ }
+
+ public virtual void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+
+ }
+
+ public virtual void OnNavigatedTo(NavigationContext navigationContext)
+ {
+
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Sample/ViewModels/MainViewModel.cs b/src/Lemon.ModuleNavigation.Sample/ViewModels/MainViewModel.cs
index 7f50b2a..f29c86b 100644
--- a/src/Lemon.ModuleNavigation.Sample/ViewModels/MainViewModel.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ViewModels/MainViewModel.cs
@@ -1,6 +1,5 @@
using Lemon.ModuleNavigation.Abstractions;
using Lemon.ModuleNavigation.Core;
-using Lemon.ModuleNavigation.Dialogs;
using Lemon.ModuleNavigation.Extensions;
using Microsoft.Extensions.Logging;
using ReactiveUI;
@@ -11,7 +10,7 @@
namespace Lemon.ModuleNavigation.Sample.ViewModels;
-public class MainViewModel : SampleViewModelBase, IServiceAware
+public class MainViewModel : ReactiveObject, IServiceAware
{
private readonly NavigationService _navigationService;
private readonly IServiceProvider _serviceProvider;
@@ -31,9 +30,9 @@ public MainViewModel(IEnumerable modules,
_dialogService = dialogService;
_regionManager = regionManager;
// default views for different regions
- _navigationService.RequestViewNavigation("ContentRegion", "ViewAlpha", false);
- _navigationService.RequestViewNavigation("TransitioningContentRegion", "ViewAlpha", false);
- Modules = new ObservableCollection(modules);
+ _navigationService.RequestViewNavigation("ContentRegion", "ViewAlpha");
+ _navigationService.RequestViewNavigation("TransitioningContentRegion", "ViewAlpha");
+ Modules = [.. modules];
ToViewCommand = ReactiveCommand.Create(content =>
{
var viewName = content;
@@ -44,10 +43,18 @@ public MainViewModel(IEnumerable modules,
requestNew = true;
}
- _navigationService.RequestViewNavigation("ContentRegion", viewName, requestNew);
- _navigationService.RequestViewNavigation("TabRegion", viewName, requestNew);
- _navigationService.RequestViewNavigation("ItemsRegion", viewName, requestNew);
- _navigationService.RequestViewNavigation("TransitioningContentRegion", viewName, requestNew);
+ _navigationService.RequestViewNavigation("ContentRegion",
+ viewName,
+ new NavigationParameters { { "requestNew", requestNew } });
+ _navigationService.RequestViewNavigation("TabRegion",
+ viewName,
+ new NavigationParameters { { "requestNew", requestNew } });
+ _navigationService.RequestViewNavigation("ItemsRegion",
+ viewName,
+ new NavigationParameters { { "requestNew", requestNew } });
+ _navigationService.RequestViewNavigation("TransitioningContentRegion",
+ viewName,
+ new NavigationParameters { { "requestNew", requestNew } });
});
ShowCommand = ReactiveCommand.Create(content =>
{
@@ -94,15 +101,25 @@ await _dialogService.ShowDialog(content,
_logger.LogDebug($"ShowDialog over:{result}");
});
+
+ UnloadViewCommand = ReactiveCommand.Create((context) =>
+ {
+ _regionManager.RequestViewUnload(context);
+ });
+
_regionManager.NavigationSubscribe(n =>
{
- _logger.LogDebug($"Request to : {n.RegionName}.{n.TargetViewName}");
+ _logger.LogDebug($"Request to : {n.RegionName}.{n.ViewName}");
});
_regionManager.NavigationSubscribe(r =>
{
_logger.LogDebug($"New region : {r.Name}");
});
}
+ public ReactiveCommand UnloadViewCommand
+ {
+ get;
+ }
public ReactiveCommand ToViewCommand
{
get;
diff --git a/src/Lemon.ModuleNavigation.Sample/ViewModels/SampleViewModelBase.cs b/src/Lemon.ModuleNavigation.Sample/ViewModels/SampleViewModelBase.cs
deleted file mode 100644
index 1fd699c..0000000
--- a/src/Lemon.ModuleNavigation.Sample/ViewModels/SampleViewModelBase.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using ReactiveUI;
-using System;
-
-namespace Lemon.ModuleNavigation.Sample.ViewModels;
-
-public class SampleViewModelBase : ReactiveObject, IDisposable
-{
- public virtual string Greeting => $"Welcome to {GetType().Name}[{Environment.ProcessId}][{Environment.CurrentManagedThreadId}]{Environment.NewLine}{DateTime.Now:yyyy-MM-dd HH-mm-ss.ffff}";
- public virtual void Dispose()
- {
-
- }
-}
diff --git a/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewAlphaViewModel.cs b/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewAlphaViewModel.cs
index a9e0370..7ea4585 100644
--- a/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewAlphaViewModel.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewAlphaViewModel.cs
@@ -1,67 +1,48 @@
using Lemon.ModuleNavigation.Abstractions;
using Lemon.ModuleNavigation.Core;
-using Lemon.ModuleNavigation.Dialogs;
using Microsoft.Extensions.Logging;
using ReactiveUI;
using System;
using System.Reactive;
-namespace Lemon.ModuleNavigation.Sample.ViewModels
+namespace Lemon.ModuleNavigation.Sample.ViewModels;
+
+public class ViewAlphaViewModel : BaseNavigationViewModel, IDialogAware
{
- public class ViewAlphaViewModel : SampleViewModelBase,
- IDialogAware,
- INavigationAware
+ private readonly ILogger _logger;
+ public ViewAlphaViewModel(ILogger logger)
{
- private readonly ILogger _logger;
- public ViewAlphaViewModel(ILogger logger)
- {
- _logger = logger;
- CloseCommand = ReactiveCommand.Create(() =>
- {
- var param = new DialogParameters
- {
- { "from", nameof(ViewAlphaViewModel) }
- };
- RequestClose?.Invoke(new DialogResult(ButtonResult.OK, param));
- });
- }
- private bool _isDialog = false;
- public bool IsDialog
+ _logger = logger;
+ CloseCommand = ReactiveCommand.Create(() =>
{
- get => _isDialog;
- set
+ var param = new DialogParameters
{
- this.RaiseAndSetIfChanged(ref _isDialog, value);
- }
- }
- public ReactiveCommand CloseCommand { get; }
- public string Title => nameof(ViewAlphaViewModel);
- public event Action? RequestClose;
-
- public void OnDialogClosed()
- {
- _logger.LogInformation("OnDialogClosed");
- }
-
- public void OnDialogOpened(IDialogParameters? parameters)
- {
- _logger.LogInformation($"OnDialogOpened:{parameters?.ToString()}");
- IsDialog = true;
- }
-
- public void OnNavigatedTo(NavigationContext navigationContext)
+ { "from", nameof(ViewAlphaViewModel) }
+ };
+ RequestClose?.Invoke(new DialogResult(ButtonResult.OK, param));
+ });
+ }
+ private bool _isDialog = false;
+ public bool IsDialog
+ {
+ get => _isDialog;
+ set
{
- //throw new NotImplementedException();
+ this.RaiseAndSetIfChanged(ref _isDialog, value);
}
+ }
+ public ReactiveCommand CloseCommand { get; }
+ public string Title => nameof(ViewAlphaViewModel);
+ public event Action? RequestClose;
- public bool IsNavigationTarget(NavigationContext navigationContext)
- {
- return true;
- }
+ public void OnDialogClosed()
+ {
+ _logger.LogInformation("OnDialogClosed");
+ }
- public void OnNavigatedFrom(NavigationContext navigationContext)
- {
- //throw new NotImplementedException();
- }
+ public void OnDialogOpened(IDialogParameters? parameters)
+ {
+ _logger.LogInformation($"OnDialogOpened:{parameters?.ToString()}");
+ IsDialog = true;
}
}
diff --git a/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewBetaViewModel.cs b/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewBetaViewModel.cs
index f03ffe3..b0c30e7 100644
--- a/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewBetaViewModel.cs
+++ b/src/Lemon.ModuleNavigation.Sample/ViewModels/ViewBetaViewModel.cs
@@ -1,67 +1,48 @@
using Lemon.ModuleNavigation.Abstractions;
using Lemon.ModuleNavigation.Core;
-using Lemon.ModuleNavigation.Dialogs;
using Microsoft.Extensions.Logging;
using ReactiveUI;
using System;
using System.Reactive;
-namespace Lemon.ModuleNavigation.Sample.ViewModels
+namespace Lemon.ModuleNavigation.Sample.ViewModels;
+
+public class ViewBetaViewModel : BaseNavigationViewModel, IDialogAware
{
- public class ViewBetaViewModel: SampleViewModelBase,
- IDialogAware,
- INavigationAware
+ private readonly ILogger _logger;
+ public ViewBetaViewModel(ILogger logger)
{
- private readonly ILogger _logger;
- public ViewBetaViewModel(ILogger logger)
- {
- _logger = logger;
- CloseCommand = ReactiveCommand.Create(() =>
- {
- var param = new DialogParameters
- {
- { "from", nameof(ViewAlphaViewModel) }
- };
- RequestClose?.Invoke(new DialogResult(ButtonResult.OK, param));
- });
- }
- private bool _isDialog = false;
- public bool IsDialog
+ _logger = logger;
+ CloseCommand = ReactiveCommand.Create(() =>
{
- get => _isDialog;
- set
+ var param = new DialogParameters
{
- this.RaiseAndSetIfChanged(ref _isDialog, value);
- }
- }
- public ReactiveCommand CloseCommand { get; }
- public string Title => nameof(ViewAlphaViewModel);
- public event Action? RequestClose;
-
- public void OnDialogClosed()
- {
- _logger.LogInformation("OnDialogClosed");
- }
-
- public void OnDialogOpened(IDialogParameters? parameters)
- {
- _logger.LogInformation($"OnDialogOpened:{parameters?.ToString()}");
- IsDialog = true;
- }
-
- public void OnNavigatedTo(NavigationContext navigationContext)
+ { "from", nameof(ViewAlphaViewModel) }
+ };
+ RequestClose?.Invoke(new DialogResult(ButtonResult.OK, param));
+ });
+ }
+ private bool _isDialog = false;
+ public bool IsDialog
+ {
+ get => _isDialog;
+ set
{
-
+ this.RaiseAndSetIfChanged(ref _isDialog, value);
}
+ }
+ public ReactiveCommand CloseCommand { get; }
+ public string Title => nameof(ViewAlphaViewModel);
+ public event Action? RequestClose;
- public bool IsNavigationTarget(NavigationContext navigationContext)
- {
- return true;
- }
+ public void OnDialogClosed()
+ {
+ _logger.LogInformation("OnDialogClosed");
+ }
- public void OnNavigatedFrom(NavigationContext navigationContext)
- {
-
- }
+ public void OnDialogOpened(IDialogParameters? parameters)
+ {
+ _logger.LogInformation($"OnDialogOpened:{parameters?.ToString()}");
+ IsDialog = true;
}
}
diff --git a/src/Lemon.ModuleNavigation.Sample/Views/MainView.axaml b/src/Lemon.ModuleNavigation.Sample/Views/MainView.axaml
index 20b365a..052df74 100644
--- a/src/Lemon.ModuleNavigation.Sample/Views/MainView.axaml
+++ b/src/Lemon.ModuleNavigation.Sample/Views/MainView.axaml
@@ -1,15 +1,15 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
+
@@ -59,9 +59,9 @@
Spacing="5">
@@ -70,12 +70,12 @@
-
+
-
+
@@ -84,29 +84,29 @@
+ Width="20"
+ lm:NavigationExtension.CanUnload="{Binding CanUnload}" />
-
+
+ BorderThickness="1"
+ Margin="2"
+ MaxHeight="600"
+ lm:NavigationExtension.ModuleContainerName="NListBox">
@@ -115,7 +115,7 @@
-
+
@@ -131,12 +131,12 @@
-
+
-
+
@@ -144,23 +144,33 @@
+
-
+
+ BorderBrush="LemonChiffon"
+ BorderThickness="1"
+ Margin="2"
+ MaxHeight="600"
+ lm:NavigationExtension.RegionName="ItemsRegion" />
-
+
@@ -169,9 +179,9 @@
+ Spacing="5"
+ Width="300">
-
-
-
+
+
+
diff --git a/src/Lemon.ModuleNavigation.Sample/Views/ViewAlpha.axaml b/src/Lemon.ModuleNavigation.Sample/Views/ViewAlpha.axaml
index f10423d..08565a8 100644
--- a/src/Lemon.ModuleNavigation.Sample/Views/ViewAlpha.axaml
+++ b/src/Lemon.ModuleNavigation.Sample/Views/ViewAlpha.axaml
@@ -1,21 +1,30 @@
-
-
-
-
+
diff --git a/src/Lemon.ModuleNavigation.Sample/Views/ViewBeta.axaml b/src/Lemon.ModuleNavigation.Sample/Views/ViewBeta.axaml
index 405e158..43e486b 100644
--- a/src/Lemon.ModuleNavigation.Sample/Views/ViewBeta.axaml
+++ b/src/Lemon.ModuleNavigation.Sample/Views/ViewBeta.axaml
@@ -7,15 +7,23 @@
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
-
-
-
-
+
+
+
+
-
-
+
+
diff --git a/src/Lemon.ModuleNavigation.Wpf/AssemblyInfo.cs b/src/Lemon.ModuleNavigation.Wpf/AssemblyInfo.cs
index b91e514..e0dc452 100644
--- a/src/Lemon.ModuleNavigation.Wpf/AssemblyInfo.cs
+++ b/src/Lemon.ModuleNavigation.Wpf/AssemblyInfo.cs
@@ -4,6 +4,7 @@
[assembly: XmlnsPrefix("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "lm")]
[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation")]
[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Wpf")]
+[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Wpf.Regions")]
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
diff --git a/src/Lemon.ModuleNavigation.Wpf/ContentRegion.cs b/src/Lemon.ModuleNavigation.Wpf/ContentRegion.cs
deleted file mode 100644
index c30f171..0000000
--- a/src/Lemon.ModuleNavigation.Wpf/ContentRegion.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Windows.Controls;
-
-namespace Lemon.ModuleNavigation.Wpf
-{
- public class ContentRegion : Region
- {
- private readonly ContentControl _contentControl;
- public ContentRegion(ContentControl contentControl, string name) : base()
- {
- _contentControl = contentControl;
- _contentControl.ContentTemplate = RegionTemplate;
- Contexts = [];
- Contexts.CollectionChanged += ViewContents_CollectionChanged;
- Name = name;
- }
-
- public override string Name
- {
- get;
- }
- public object? Content
- {
- get => _contentControl.Content;
- set
- {
- _contentControl.Content = value;
- }
- }
- public override ObservableCollection Contexts
- {
- get;
- }
-
- public override void Activate(NavigationContext target)
- {
- if(Content is NavigationContext current)
- {
- if (target.TargetViewName == current.TargetViewName
- && !target.RequestNew)
- {
- return;
- }
- }
- Content = target;
- }
-
- public override void DeActivate(NavigationContext target)
- {
- Content = null;
- }
- private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.Action == NotifyCollectionChangedAction.Add)
- {
- //
- }
- if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- //
- }
- }
- }
-}
diff --git a/src/Lemon.ModuleNavigation.Wpf/DialogService.cs b/src/Lemon.ModuleNavigation.Wpf/DialogService.cs
index a387aac..3e0f4e7 100644
--- a/src/Lemon.ModuleNavigation.Wpf/DialogService.cs
+++ b/src/Lemon.ModuleNavigation.Wpf/DialogService.cs
@@ -1,5 +1,5 @@
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
diff --git a/src/Lemon.ModuleNavigation.Wpf/Extensions/WpfExtensions.cs b/src/Lemon.ModuleNavigation.Wpf/Extensions/WpfExtensions.cs
index 1037162..8cd6063 100644
--- a/src/Lemon.ModuleNavigation.Wpf/Extensions/WpfExtensions.cs
+++ b/src/Lemon.ModuleNavigation.Wpf/Extensions/WpfExtensions.cs
@@ -1,52 +1,87 @@
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Wpf;
+using Lemon.ModuleNavigation.Wpf.Regions;
using System.Windows.Controls;
using System.Windows.Threading;
-namespace System.Windows
+namespace System.Windows;
+
+public static class WpfExtensions
{
- public static class WpfExtensions
+ public static T WaitOnDispatcherFrame(this Task task)
{
- public static T WaitOnDispatcherFrame(this Task task)
+ if (!task.IsCompleted)
{
- if (!task.IsCompleted)
- {
- var frame = new DispatcherFrame();
- task.ContinueWith(static (_, s) => ((DispatcherFrame)s!).Continue = false, frame);
- Dispatcher.PushFrame(frame);
- }
-
- return task.GetAwaiter().GetResult();
+ var frame = new DispatcherFrame();
+ task.ContinueWith(static (_, s) => ((DispatcherFrame)s!).Continue = false, frame);
+ Dispatcher.PushFrame(frame);
}
- public static T? FindLogicalAncestorOfType(this DependencyObject obj, bool includeSelf = false) where T : DependencyObject
- {
- if (obj == null) throw new ArgumentNullException(nameof(obj));
+ return task.GetAwaiter().GetResult();
+ }
- if (includeSelf && obj is T self)
- return self;
+ public static T? FindLogicalAncestorOfType(this DependencyObject obj, bool includeSelf = false) where T : DependencyObject
+ {
+ if (obj == null) throw new ArgumentNullException(nameof(obj));
- DependencyObject? parent = LogicalTreeHelper.GetParent(obj);
- while (parent != null)
- {
- if (parent is T ancestor)
- return ancestor;
+ if (includeSelf && obj is T self)
+ return self;
- parent = LogicalTreeHelper.GetParent(parent);
- }
+ DependencyObject? parent = LogicalTreeHelper.GetParent(obj);
+ while (parent != null)
+ {
+ if (parent is T ancestor)
+ return ancestor;
- return null;
+ parent = LogicalTreeHelper.GetParent(parent);
}
- public static IRegion ToContainer(this Control control, string name)
+ return null;
+ }
+
+ public static IRegion ToRegion(this Control control, string name)
+ {
+ return control switch
{
- return control switch
+ TabControl tabControl => new TabRegion(name, tabControl),
+ ItemsControl itemsControl => new ItemsRegion(name, itemsControl),
+ ContentControl contentControl => new ContentRegion(name, contentControl),
+ _ => throw new NotSupportedException($"Unsupported control:{control.GetType()}"),
+ };
+ }
+
+ public static void ScrollIntoView(this ItemsControl itemsControl, object item)
+ {
+ if (itemsControl == null || item == null)
+ return;
+ if (itemsControl is ListBox listBox)
+ {
+ listBox.Dispatcher.InvokeAsync(() => listBox.ScrollIntoView(item));
+ }
+ else
+ {
+ var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
+ if (container != null)
{
- TabControl tabControl => new TabRegion(tabControl, name),
- ItemsControl itemsControl => new ItemsRegion(itemsControl, name),
- ContentControl contentControl => new ContentRegion(contentControl, name),
- _ => throw new NotSupportedException($"Unsupported control:{control.GetType()}"),
+ container.BringIntoView();
+ return;
+ }
+ itemsControl.ItemContainerGenerator.StatusChanged += (s, e) =>
+ {
+ if (itemsControl.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
+ {
+ container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
+ container?.BringIntoView();
+ }
};
}
}
+
+ public static void ScrollIntoView(this ItemsControl itemsControl, int index)
+ {
+ if (itemsControl == null || index < 0 || index >= itemsControl.Items.Count)
+ return;
+
+ var item = itemsControl.Items[index];
+ itemsControl.ScrollIntoView(item);
+ }
}
diff --git a/src/Lemon.ModuleNavigation.Wpf/ItemsRegion.cs b/src/Lemon.ModuleNavigation.Wpf/ItemsRegion.cs
deleted file mode 100644
index a1e313f..0000000
--- a/src/Lemon.ModuleNavigation.Wpf/ItemsRegion.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-
-namespace Lemon.ModuleNavigation.Wpf;
-
-public class ItemsRegion : Region
-{
- private readonly ItemsControl _itemsControl;
- public ItemsRegion(ItemsControl itemsControl, string name)
- {
- _itemsControl = itemsControl;
- _itemsControl.ItemTemplate = RegionTemplate;
- Contexts = [];
- Contexts.CollectionChanged += ViewContents_CollectionChanged;
- Name = name;
- }
- public override string Name
- {
- get;
- }
- public object? SelectedItem
- {
- get
- {
- if (_itemsControl is Selector selecting)
- {
- return selecting.SelectedItem;
- }
- return null;
- }
- set
- {
- if (value != null)
- {
- if (_itemsControl is Selector selecting)
- {
- selecting.SelectedItem = value;
- }
- }
- }
- }
-
- public override ObservableCollection Contexts
- {
- get;
- }
-
- public void ScrollIntoView(int index)
- {
- throw new NotImplementedException();
- }
- public void ScrollIntoView(NavigationContext item)
- {
- throw new NotImplementedException();
- }
- public override void Activate(NavigationContext target)
- {
- if (!target.RequestNew)
- {
- var targetContext = Contexts.FirstOrDefault(context =>
- {
- if (NavigationContext.ViewNameComparer.Equals(target, context))
- {
- return true;
- }
- return false;
- });
- if (targetContext == null)
- {
- Contexts.Add(target);
- SelectedItem = target;
- }
- else
- {
- SelectedItem = targetContext;
- }
- }
- else
- {
- Contexts.Add(target);
- SelectedItem = target;
- }
- }
- public override void DeActivate(NavigationContext target)
- {
- Contexts.Remove(target);
- }
- public void Add(NavigationContext item)
- {
- Contexts.Add(item);
- }
- private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.Action == NotifyCollectionChangedAction.Add)
- {
- if (e.NewItems != null)
- {
- foreach (var item in e.NewItems)
- {
- _itemsControl.Items.Add(item);
- }
- }
- }
- if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- if (e.OldItems != null)
- {
- foreach (var item in e.OldItems)
- {
- _itemsControl.Items.Remove(e.OldItems);
- }
- }
- }
- }
-}
diff --git a/src/Lemon.ModuleNavigation.Wpf/Lemon.ModuleNavigation.Wpf.csproj b/src/Lemon.ModuleNavigation.Wpf/Lemon.ModuleNavigation.Wpf.csproj
index 7b630ea..d773758 100644
--- a/src/Lemon.ModuleNavigation.Wpf/Lemon.ModuleNavigation.Wpf.csproj
+++ b/src/Lemon.ModuleNavigation.Wpf/Lemon.ModuleNavigation.Wpf.csproj
@@ -13,6 +13,12 @@
README.md
../output
+
+
+
+
+
+
True
diff --git a/src/Lemon.ModuleNavigation.Wpf/NavigationExtension.cs b/src/Lemon.ModuleNavigation.Wpf/NavigationExtension.cs
index 268d013..1c86d80 100644
--- a/src/Lemon.ModuleNavigation.Wpf/NavigationExtension.cs
+++ b/src/Lemon.ModuleNavigation.Wpf/NavigationExtension.cs
@@ -30,7 +30,7 @@ void LoadedHandler(object? sender, RoutedEventArgs e)
var serviceProvider = navigationProvider!.ServiceProvider;
var handler = serviceProvider.GetRequiredService();
var value = GetRegionName(control);
- handler.RegionManager.AddRegion(value, control.ToContainer(value));
+ handler.RegionManager.AddRegion(value, control.ToRegion(value));
}
control.Loaded -= LoadedHandler;
}
@@ -54,84 +54,4 @@ public static string GetRegionName(Control element)
}
#endregion
- #region CanUnload -- ongoing
- public static readonly DependencyProperty CanUnloadProperty =
- DependencyProperty.RegisterAttached(
- "CanUnload", typeof(bool), typeof(NavigationExtension),
- new PropertyMetadata(true, OnCanUnloadChanged));
-
- public static bool GetCanUnload(DependencyObject obj) => (bool)obj.GetValue(CanUnloadProperty);
- public static void SetCanUnload(DependencyObject obj, bool value) => obj.SetValue(CanUnloadProperty, value);
-
- private static void OnCanUnloadChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is Button button)
- {
- bool newValue = (bool)e.NewValue;
- button.Visibility = newValue ? Visibility.Visible : Visibility.Collapsed;
-
- if (newValue)
- {
- if (!_targets.Contains(button))
- {
- _targets.Add(button);
- button.Unloaded += Button_Unloaded;
- button.Click += UnloadModule;
- }
- }
- else
- {
- if (_targets.Contains(button))
- {
- button.Unloaded -= Button_Unloaded;
- button.Click -= UnloadModule;
- _targets.Remove(button);
- }
- }
- }
- }
-
- private static void Button_Unloaded(object sender, RoutedEventArgs e)
- {
- if (sender is Button button && _targets.Contains(button))
- {
- button.Unloaded -= Button_Unloaded;
- button.Click -= UnloadModule;
- _targets.Remove(button);
- }
- }
-
- private static void UnloadModule(object sender, RoutedEventArgs e)
- {
- if (sender is Button button)
- {
- var tabItem = FindAncestor(button) ??
- throw new InvalidOperationException($"No 'TabItem' found in parents of {button}");
-
- var tabContainer = FindAncestor(tabItem);
- if (tabContainer != null)
- {
- if (tabItem.DataContext is INavigationAware item)
- {
- if (tabContainer.DataContext is IServiceAware serviceAware)
- {
- var handler = serviceAware.ServiceProvider.GetRequiredService();
- }
- }
- }
- }
- }
-
- private static T? FindAncestor(DependencyObject current) where T : DependencyObject
- {
- while (current != null)
- {
- if (current is T ancestor)
- return ancestor;
- current = LogicalTreeHelper.GetParent(current);
- }
- return null;
- }
- #endregion
-
}
diff --git a/src/Lemon.ModuleNavigation.Wpf/Region.cs b/src/Lemon.ModuleNavigation.Wpf/Region.cs
deleted file mode 100644
index 906c6d0..0000000
--- a/src/Lemon.ModuleNavigation.Wpf/Region.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Core;
-using Microsoft.Extensions.DependencyInjection;
-using System.Collections.Concurrent;
-using System.Collections.ObjectModel;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace Lemon.ModuleNavigation.Wpf;
-
-public abstract class Region : IRegion
-{
- private readonly ConcurrentDictionary _viewCache = new();
- private readonly ConcurrentItem<(IView View, INavigationAware NavigationAware)> _current = new();
-
- public Region()
- {
- RegionTemplate = CreateRegionDataTemplate();
- }
-
- public abstract string Name { get; }
- public abstract ObservableCollection Contexts { get; }
-
- public DataTemplate? RegionTemplate { get; set; }
-
- public abstract void Activate(NavigationContext target);
- public abstract void DeActivate(NavigationContext target);
-
- private DataTemplate CreateRegionDataTemplate()
- {
- var dataTemplate = new DataTemplate(typeof(NavigationContext));
- FrameworkElementFactory factory = new(typeof(ContentPresenter));
- factory.SetBinding(ContentPresenter.ContentProperty, new System.Windows.Data.Binding()
- {
- Converter = new NavigationContextToViewConverter(this)
- });
- dataTemplate.VisualTree = factory;
- return dataTemplate;
- }
-
- public class NavigationContextToViewConverter : System.Windows.Data.IValueConverter
- {
- private readonly Region _region;
- public NavigationContextToViewConverter(Region region)
- {
- _region = region;
- }
-
- public object? Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- if (value is not NavigationContext context)
- return null;
-
- bool needNewView = context.RequestNew ||
- !_region._viewCache.TryGetValue(context.TargetViewName, out IView? view);
-
- if (needNewView)
- {
- view = context.ServiceProvider.GetRequiredKeyedService(context.TargetViewName);
- var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.TargetViewName);
-
- if (_region._current.TryTakeData(out var previousData))
- {
- previousData.NavigationAware.OnNavigatedFrom(context);
- }
-
- if (!navigationAware.IsNavigationTarget(context))
- return null;
-
- view.DataContext = navigationAware;
- navigationAware.OnNavigatedTo(context);
- _region._current.SetData((view, navigationAware));
- _region._viewCache.TryAdd(context.TargetViewName, view);
- }
- else
- {
- view = _region._viewCache[context.TargetViewName];
- }
-
- return view as Control;
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- throw new NotImplementedException();
- }
- }
-}
-
-
diff --git a/src/Lemon.ModuleNavigation.Wpf/Regions/ContentRegion.cs b/src/Lemon.ModuleNavigation.Wpf/Regions/ContentRegion.cs
new file mode 100644
index 0000000..86040fc
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Wpf/Regions/ContentRegion.cs
@@ -0,0 +1,119 @@
+using Lemon.ModuleNavigation.Abstractions;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Lemon.ModuleNavigation.Wpf.Regions;
+
+public class ContentRegion : Region, IContentRegionContext
+{
+ private readonly ContentControl _contentControl;
+ public ContentRegion(string name, ContentControl contentControl) : base(name)
+ {
+ _contentControl = contentControl;
+ SetBindingContentTemplate();
+ SetBindingContent();
+ }
+
+ private object? _content;
+ public object? Content
+ {
+ get => _content;
+ set
+ {
+ _content = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private DataTemplate? _contentTemplate;
+ public DataTemplate? ContentTemplate
+ {
+ get => _contentTemplate;
+ set
+ {
+ _contentTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ ///
+ /// When Views with same ViewName were found, the latest one will be picked.
+ ///
+ ///
+ public override void Activate(NavigationContext target)
+ {
+ if (ViewCache.TryGetValue(target, out IView? accurateView))
+ {
+ target.View = accurateView;
+ Content = target;
+ }
+ else if (ViewNameCache.TryGetValue(target.ViewName, out IView? view)
+ && view.DataContext is INavigationAware navigationAware
+ && navigationAware.IsNavigationTarget(target))
+ {
+ var context = Contexts.First(c => c.ViewName == target.ViewName);
+ context.View = view;
+ Content = context;
+ }
+ else
+ {
+ Contexts.Add(target);
+ Content = target;
+ }
+ }
+
+ public override void DeActivate(string regionName)
+ {
+ if (Content is NavigationContext current)
+ {
+ if (current.ViewName == regionName)
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ if (Content is NavigationContext current)
+ {
+ if (NavigationContext.ViewNameComparer.Equals(current, navigationContext))
+ {
+ Contexts.Remove(current);
+ Content = null;
+ }
+ }
+ }
+
+ protected virtual void SetBindingContentTemplate()
+ {
+ ContentTemplate = RegionContentTemplate;
+ BindingOperations.SetBinding(_contentControl,
+ ContentControl.ContentTemplateProperty,
+ new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(ContentTemplate)),
+ });
+ }
+ protected virtual void SetBindingContent()
+ {
+ BindingOperations.SetBinding(_contentControl,
+ ContentControl.ContentProperty,
+ new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(Content)),
+ Mode = BindingMode.TwoWay
+ });
+ }
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Wpf/Regions/ItemsRegion.cs b/src/Lemon.ModuleNavigation.Wpf/Regions/ItemsRegion.cs
new file mode 100644
index 0000000..4ddaf57
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Wpf/Regions/ItemsRegion.cs
@@ -0,0 +1,133 @@
+using Lemon.ModuleNavigation.Abstractions;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+
+namespace Lemon.ModuleNavigation.Wpf.Regions;
+
+public class ItemsRegion : Region, IItemsRegionDataContext
+{
+ private readonly ItemsControl _itemsControl;
+ public ItemsRegion(string name, ItemsControl itemsControl) : base(name)
+ {
+ _itemsControl = itemsControl;
+ SetBindingItemTemplate();
+ SetBindingSelectedItem();
+ SetBindingItemsSource();
+ }
+ private object? _selectItem;
+ public object? SelectedItem
+ {
+ get
+ {
+ return _selectItem;
+ }
+ set
+ {
+ _selectItem = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private DataTemplate? _itemsTemplate;
+ public DataTemplate? ItemTemplate
+ {
+ get => _itemsTemplate;
+ set
+ {
+ _itemsTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public override void ScrollIntoView(int index)
+ {
+ _itemsControl.ScrollIntoView(index);
+ }
+ public override void ScrollIntoView(NavigationContext item)
+ {
+ _itemsControl.ScrollIntoView(item);
+ }
+
+ ///
+ /// When Views with same ViewName were found, the earliest one will be picked.
+ ///
+ ///
+ public override void Activate(NavigationContext target)
+ {
+ try
+ {
+ if (ViewCache.TryGetValue(target, out IView? accurateView))
+ {
+ target.View = accurateView;
+ SelectedItem = target;
+ return;
+ }
+ var context = Contexts.FirstOrDefault(c => c.ViewName == target.ViewName);
+ if (context is not null
+ && context.View is not null
+ && context.View.DataContext is INavigationAware navigationAware
+ && navigationAware.IsNavigationTarget(target))
+ {
+ SelectedItem = context;
+ return;
+ }
+ Contexts.Add(target);
+ SelectedItem = target;
+ }
+ finally
+ {
+ ScrollIntoView((SelectedItem as NavigationContext)!);
+ }
+ }
+ public override void DeActivate(string viewName)
+ {
+ Contexts.Remove(Contexts.Last(c => c.ViewName == viewName));
+ }
+ public override void DeActivate(NavigationContext navigationContext)
+ {
+ Contexts.Remove(navigationContext);
+ }
+ public void Add(NavigationContext item)
+ {
+ Contexts.Add(item);
+ }
+
+ protected virtual void SetBindingItemsSource()
+ {
+ BindingOperations.SetBinding(_itemsControl, ItemsControl.ItemsSourceProperty, new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(Contexts)),
+ });
+ }
+ protected virtual void SetBindingItemTemplate()
+ {
+ ItemTemplate = RegionContentTemplate;
+ BindingOperations.SetBinding(_itemsControl, ItemsControl.ItemTemplateProperty, new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(ItemTemplate)),
+ });
+ }
+ protected virtual void SetBindingSelectedItem()
+ {
+ if (_itemsControl is Selector selector)
+ {
+ BindingOperations.SetBinding(selector, Selector.SelectedItemProperty, new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(SelectedItem)),
+ Mode = BindingMode.TwoWay
+ });
+ }
+ }
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Wpf/Regions/Region.cs b/src/Lemon.ModuleNavigation.Wpf/Regions/Region.cs
new file mode 100644
index 0000000..5948264
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Wpf/Regions/Region.cs
@@ -0,0 +1,153 @@
+using Lemon.ModuleNavigation.Abstractions;
+using Lemon.ModuleNavigation.Core;
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Concurrent;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Lemon.ModuleNavigation.Wpf.Regions;
+
+public abstract class Region : IRegion
+{
+ public Region(string name)
+ {
+ Name = name;
+ Current = new();
+ ViewCache = [];
+ Contexts = [];
+ RegionContentTemplate = CreateRegionDataTemplate();
+ Contexts.CollectionChanged += Contexts_CollectionChanged;
+ }
+ protected ConcurrentItem<(IView View, INavigationAware NavigationAware)> Current
+ {
+ get;
+ }
+ public string Name
+ {
+ get;
+ }
+ protected ConcurrentDictionary ViewCache
+ {
+ get;
+ }
+
+ protected ConcurrentDictionary ViewNameCache
+ {
+ get
+ {
+ return new(Contexts
+ .GroupBy(kv => kv.ViewName)
+ .Select(group => new KeyValuePair(
+ group.Key,
+ group.Last().View!)));
+ }
+ }
+ public ObservableCollection Contexts
+ {
+ get;
+ }
+
+ public DataTemplate? RegionContentTemplate
+ {
+ get;
+ }
+ public virtual void ScrollIntoView(int index)
+ {
+ throw new NotImplementedException();
+ }
+ public virtual void ScrollIntoView(NavigationContext item)
+ {
+ throw new NotImplementedException();
+ }
+ public abstract void Activate(NavigationContext target);
+ public abstract void DeActivate(string viewName);
+ public abstract void DeActivate(NavigationContext target);
+
+ protected IView? ResolveView(NavigationContext context)
+ {
+ var view = context.View;
+ if (view is null)
+ {
+ view = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+ var navigationAware = context.ServiceProvider.GetRequiredKeyedService(context.ViewName);
+
+ if (Current.TryTakeData(out var previousData))
+ {
+ previousData.NavigationAware.OnNavigatedFrom(context);
+ }
+
+ view.DataContext = navigationAware;
+ navigationAware.OnNavigatedTo(context);
+ navigationAware.RequestUnload += () =>
+ {
+ DeActivate(context);
+ };
+ Current.SetData((view, navigationAware));
+ context.View = view;
+ ViewCache.AddOrUpdate(context, view, (key, value) => view);
+ }
+ return view;
+ }
+
+ protected virtual void WhenContextsAdded(IEnumerable contexts)
+ {
+
+ }
+ protected virtual void WhenContextsRemoved(IEnumerable contexts)
+ {
+
+ }
+ private void Contexts_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ if (e.NewItems is not null)
+ {
+ WhenContextsAdded(e.NewItems.Cast());
+ }
+ }
+ if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ if (e.OldItems is not null)
+ {
+ WhenContextsRemoved(e.OldItems.Cast());
+ }
+ }
+ }
+ private DataTemplate CreateRegionDataTemplate()
+ {
+ var dataTemplate = new DataTemplate(typeof(NavigationContext));
+ FrameworkElementFactory factory = new(typeof(ContentPresenter));
+ factory.SetBinding(ContentPresenter.ContentProperty, new System.Windows.Data.Binding()
+ {
+ Converter = new NavigationContextToViewConverter(this)
+ });
+ dataTemplate.VisualTree = factory;
+ return dataTemplate;
+ }
+
+ private class NavigationContextToViewConverter : System.Windows.Data.IValueConverter
+ {
+ private readonly Region _region;
+ public NavigationContextToViewConverter(Region region)
+ {
+ _region = region;
+ }
+
+ public object? Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (value is NavigationContext context)
+ {
+ return _region.ResolveView(context);
+ }
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Wpf/Regions/TabRegion.cs b/src/Lemon.ModuleNavigation.Wpf/Regions/TabRegion.cs
new file mode 100644
index 0000000..bd2e072
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.Wpf/Regions/TabRegion.cs
@@ -0,0 +1,48 @@
+using Lemon.ModuleNavigation.Abstractions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Lemon.ModuleNavigation.Wpf.Regions;
+
+public class TabRegion : ItemsRegion, IContentRegionContext
+{
+ private readonly TabControl _tabControl;
+ public TabRegion(string name, TabControl tabControl) : base(name, tabControl)
+ {
+ _tabControl = tabControl;
+ ContentTemplate = RegionContentTemplate;
+ SetBindingContentTemplate();
+ }
+
+ public object? Content
+ {
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
+ }
+
+ private DataTemplate? _contentTemplate;
+ public DataTemplate? ContentTemplate
+ {
+ get => _contentTemplate;
+ set
+ {
+ _contentTemplate = value;
+ OnPropertyChanged();
+ }
+ }
+ protected override void SetBindingItemTemplate()
+ {
+ //base.SetBindingItemTemplate();
+ }
+ protected virtual void SetBindingContentTemplate()
+ {
+ BindingOperations.SetBinding(_tabControl,
+ TabControl.ContentTemplateProperty,
+ new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(ContentTemplate)),
+ });
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.Wpf/TabRegion.cs b/src/Lemon.ModuleNavigation.Wpf/TabRegion.cs
deleted file mode 100644
index 3865ab1..0000000
--- a/src/Lemon.ModuleNavigation.Wpf/TabRegion.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Windows.Controls;
-
-namespace Lemon.ModuleNavigation.Wpf;
-
-public class TabRegion : Region
-{
- private readonly TabControl _tabControl;
- public TabRegion(TabControl tabControl, string name)
- {
- _tabControl = tabControl;
- _tabControl.ContentTemplate = RegionTemplate;
- Contexts = [];
- Contexts.CollectionChanged += ViewContents_CollectionChanged;
- Name = name;
- }
- public override string Name
- {
- get;
- }
- public object? SelectedItem
- {
- get
- {
- return _tabControl.SelectedItem;
- }
- set
- {
- _tabControl.SelectedItem = value;
- }
- }
- public override ObservableCollection Contexts
- {
- get;
- }
-
- public override void Activate(NavigationContext target)
- {
- if (!target.RequestNew)
- {
- var targetContext = Contexts.FirstOrDefault(context =>
- {
- if (NavigationContext.ViewNameComparer.Equals(target, context))
- {
- return true;
- }
- return false;
- });
- if (targetContext == null)
- {
- Contexts.Add(target);
- SelectedItem = target;
- }
- else
- {
- SelectedItem = targetContext;
- }
- }
- else
- {
- Contexts.Add(target);
- SelectedItem = target;
- }
- }
- public override void DeActivate(NavigationContext target)
- {
- Contexts.Remove(target);
- }
- public void Add(NavigationContext item)
- {
- Contexts.Add(item);
- }
- private void ViewContents_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.Action == NotifyCollectionChangedAction.Add)
- {
- if (e.NewItems != null)
- {
- foreach (var item in e.NewItems)
- {
- _tabControl.Items.Add(item);
- }
- }
- }
- if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- if (e.OldItems != null)
- {
- foreach (var item in e.OldItems)
- {
- _tabControl.Items.Remove(item);
- }
- }
- }
- }
-}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/App.xaml.cs b/src/Lemon.ModuleNavigation.WpfSample/App.xaml.cs
index 755dbbb..607be71 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/App.xaml.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/App.xaml.cs
@@ -15,12 +15,8 @@ protected override void OnStartup(StartupEventArgs e)
{
var services = new ServiceCollection();
services.AddWpfNavigationSupport()
- //.AddModule()
- //.AddModule()
- //.AddModule()
.AddSingleton()
.AddSingleton()
- //.AddDialogWindow(nameof(CustomDialogWindow))
.AddView(nameof(ViewAlpha))
.AddView(nameof(ViewBeta));
diff --git a/src/Lemon.ModuleNavigation.WpfSample/BaseNavigationViewModel.cs b/src/Lemon.ModuleNavigation.WpfSample/BaseNavigationViewModel.cs
new file mode 100644
index 0000000..0ddc219
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.WpfSample/BaseNavigationViewModel.cs
@@ -0,0 +1,42 @@
+using Lemon.ModuleNavigation.Abstractions;
+using ReactiveUI;
+using System.Diagnostics;
+using System.Reactive;
+
+namespace Lemon.ModuleNavigation.WpfSample;
+
+public class BaseNavigationViewModel : ReactiveObject, INavigationAware
+{
+ public virtual string Greeting => $"Welcome to {GetType().Name}[{Environment.ProcessId}][{Environment.CurrentManagedThreadId}]{Environment.NewLine}{DateTime.Now:yyyy-MM-dd HH-mm-ss.ffff}";
+
+ public BaseNavigationViewModel()
+ {
+ UnloadViewCommand = ReactiveCommand.Create(() =>
+ {
+ var code = this.GetHashCode();
+ Debug.WriteLine(code);
+ RequestUnload?.Invoke();
+ });
+ }
+ public ReactiveCommand UnloadViewCommand
+ {
+ get;
+ }
+
+ public event Action? RequestUnload;
+
+ public virtual bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ return true;
+ }
+
+ public virtual void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public virtual void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ //throw new NotImplementedException();
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/BaseViewModel.cs b/src/Lemon.ModuleNavigation.WpfSample/BaseViewModel.cs
deleted file mode 100644
index f1c7783..0000000
--- a/src/Lemon.ModuleNavigation.WpfSample/BaseViewModel.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Core;
-using ReactiveUI;
-
-namespace Lemon.ModuleNavigation.WpfSample;
-
-public class BaseViewModel : ReactiveObject, INavigationAware
-{
- public virtual string Greeting => $"Welcome to {GetType().Name}[{Environment.ProcessId}][{Environment.CurrentManagedThreadId}]{Environment.NewLine}{DateTime.Now:yyyy-MM-dd HH-mm-ss.ffff}";
- public bool IsNavigationTarget(NavigationContext navigationContext)
- {
- return true;
- }
-
- public virtual void OnNavigatedFrom(NavigationContext navigationContext)
- {
- //throw new NotImplementedException();
- }
-
- public virtual void OnNavigatedTo(NavigationContext navigationContext)
- {
- //throw new NotImplementedException();
- }
-}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/BoolToVisibilityConverter.cs b/src/Lemon.ModuleNavigation.WpfSample/BoolToVisibilityConverter.cs
new file mode 100644
index 0000000..5ec9e7a
--- /dev/null
+++ b/src/Lemon.ModuleNavigation.WpfSample/BoolToVisibilityConverter.cs
@@ -0,0 +1,34 @@
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace Lemon.ModuleNavigation.WpfSample;
+
+public class BoolToVisibilityConverter : IValueConverter
+{
+ public bool Inverse { get; set; } = false;
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is bool boolValue)
+ {
+ if (Inverse)
+ return boolValue ? Visibility.Collapsed : Visibility.Visible;
+ else
+ return boolValue ? Visibility.Visible : Visibility.Collapsed;
+ }
+ return Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Visibility visibility)
+ {
+ if (Inverse)
+ return visibility != Visibility.Visible;
+ else
+ return visibility == Visibility.Visible;
+ }
+ return false;
+ }
+}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml b/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml
index 332a5ce..4d77e85 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml
+++ b/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml
@@ -57,13 +57,14 @@
+ Command="{Binding DataContext.UnloadViewCommand, RelativeSource={RelativeSource AncestorType=TabControl}}"
+ CommandParameter="{Binding}" />
diff --git a/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml.cs b/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml.cs
index 78f08e8..7dc98c1 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/MainWindow.xaml.cs
@@ -1,13 +1,4 @@
-using System.Text;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
+using System.Windows;
namespace Lemon.ModuleNavigation.WpfSample;
diff --git a/src/Lemon.ModuleNavigation.WpfSample/MainWindowViewModel.cs b/src/Lemon.ModuleNavigation.WpfSample/MainWindowViewModel.cs
index 55d54da..fea7e11 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/MainWindowViewModel.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/MainWindowViewModel.cs
@@ -1,25 +1,27 @@
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
using ReactiveUI;
-using Splat;
using System.Diagnostics;
using System.Reactive;
namespace Lemon.ModuleNavigation.WpfSample;
-public class MainWindowViewModel : BaseViewModel, IServiceAware
+public class MainWindowViewModel : ReactiveObject, IServiceAware
{
private readonly INavigationService _navigationService;
private readonly IDialogService _dialogService;
- public MainWindowViewModel(INavigationService navigationService,
- IDialogService dialogService,
+ private readonly IRegionManager _regionManager;
+ public MainWindowViewModel(INavigationService navigationService,
+ IDialogService dialogService,
+ IRegionManager regionManager,
IServiceProvider serviceProvider)
{
_dialogService = dialogService;
+ _regionManager = regionManager;
_navigationService = navigationService;
- _navigationService.RequestViewNavigation("ContentRegion", "ViewAlpha", false);
+ _navigationService.RequestViewNavigation("ContentRegion", "ViewAlpha");
ServiceProvider = serviceProvider;
- NavigateToViewCommand = ReactiveCommand.Create(content =>
+ NavigateToViewCommand = ReactiveCommand.Create(content =>
{
var viewName = content;
var requestNew = false;
@@ -29,9 +31,9 @@ public MainWindowViewModel(INavigationService navigationService,
requestNew = true;
}
- _navigationService.RequestViewNavigation("ContentRegion", viewName, requestNew);
- _navigationService.RequestViewNavigation("TabRegion", viewName, requestNew);
- _navigationService.RequestViewNavigation("ItemsRegion", viewName, requestNew);
+ _navigationService.RequestViewNavigation("ContentRegion", viewName, new NavigationParameters { { "requestNew", requestNew } });
+ _navigationService.RequestViewNavigation("TabRegion", viewName, new NavigationParameters { { "requestNew", requestNew } });
+ _navigationService.RequestViewNavigation("ItemsRegion", viewName, new NavigationParameters { { "requestNew", requestNew } });
});
ShowCommand = ReactiveCommand.Create(content =>
@@ -78,14 +80,20 @@ await _dialogService.ShowDialog(content,
});
Debug.WriteLine($"ShowDialog over:{result}");
});
-
+ UnloadViewCommand = ReactiveCommand.Create((context) =>
+ {
+ _regionManager.RequestViewUnload(context);
+ });
}
public IServiceProvider ServiceProvider
{
get;
}
-
+ public ReactiveCommand UnloadViewCommand
+ {
+ get;
+ }
public ReactiveCommand NavigateToViewCommand
{
get;
diff --git a/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewAlphaViewModel.cs b/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewAlphaViewModel.cs
index 08adb9b..b34a2ed 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewAlphaViewModel.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewAlphaViewModel.cs
@@ -1,11 +1,11 @@
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
using ReactiveUI;
using System.Reactive;
namespace Lemon.ModuleNavigation.WpfSample.ViewModels;
-public class ViewAlphaViewModel : BaseViewModel, IDialogAware
+public class ViewAlphaViewModel : BaseNavigationViewModel, IDialogAware
{
public ViewAlphaViewModel()
{
@@ -42,4 +42,16 @@ public void OnDialogOpened(IDialogParameters? parameters)
{
IsDialog = true;
}
+
+ public override bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ if (navigationContext.Parameters is not null)
+ {
+ if (navigationContext.Parameters.TryGetValue("requestNew", out bool requestNew))
+ {
+ return !requestNew;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewBetaViewModel.cs b/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewBetaViewModel.cs
index bb2eb35..dd126a3 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewBetaViewModel.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/ViewModels/ViewBetaViewModel.cs
@@ -1,11 +1,11 @@
using Lemon.ModuleNavigation.Abstractions;
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
using ReactiveUI;
using System.Reactive;
namespace Lemon.ModuleNavigation.WpfSample.ViewModels;
-public class ViewBetaViewModel : BaseViewModel, IDialogAware
+public class ViewBetaViewModel : BaseNavigationViewModel, IDialogAware
{
public string Title => nameof(ViewBetaViewModel);
@@ -38,4 +38,15 @@ public void OnDialogOpened(IDialogParameters? parameters)
{
IsDialog = true;
}
+ public override bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ if (navigationContext.Parameters is not null)
+ {
+ if (navigationContext.Parameters.TryGetValue("requestNew", out bool requestNew))
+ {
+ return !requestNew;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml
index 355d9c9..5034d9f 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml
+++ b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml
@@ -1,20 +1,35 @@
-
-
+
+
+
+
+
+
+ HorizontalContentAlignment="Center"
+ VerticalContentAlignment="Center"
+ Command="{Binding UnloadViewCommand}"
+ Content="UnloadView"
+ Visibility="{Binding IsDialog, Converter={StaticResource BoolToVisibilityConverterInverse}}"/>
+
diff --git a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml.cs b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml.cs
index 64307f3..bc57de3 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewAlpha.xaml.cs
@@ -1,29 +1,15 @@
using Lemon.ModuleNavigation.Abstractions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
-namespace Lemon.ModuleNavigation.WpfSample.Views
+namespace Lemon.ModuleNavigation.WpfSample.Views;
+
+///
+/// Interaction logic for ViewAlpha.xaml
+///
+public partial class ViewAlpha : UserControl, IView
{
- ///
- /// Interaction logic for ViewAlpha.xaml
- ///
- public partial class ViewAlpha : UserControl,IView
+ public ViewAlpha()
{
- public ViewAlpha()
- {
- InitializeComponent();
- }
+ InitializeComponent();
}
}
diff --git a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml
index 7f72f9b..45da567 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml
+++ b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml
@@ -1,20 +1,35 @@
-
-
+
+
+
+
+
+
+ HorizontalContentAlignment="Center"
+ VerticalContentAlignment="Center"
+ Command="{Binding UnloadViewCommand}"
+ Content="UnloadView"
+ Visibility="{Binding IsDialog, Converter={StaticResource BoolToVisibilityConverterInverse}}" />
+
diff --git a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml.cs b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml.cs
index 50287e5..dff684f 100644
--- a/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml.cs
+++ b/src/Lemon.ModuleNavigation.WpfSample/Views/ViewBeta.xaml.cs
@@ -1,16 +1,15 @@
using Lemon.ModuleNavigation.Abstractions;
using System.Windows.Controls;
-namespace Lemon.ModuleNavigation.WpfSample.Views
+namespace Lemon.ModuleNavigation.WpfSample.Views;
+
+///
+/// Interaction logic for ViewBeta.xaml
+///
+public partial class ViewBeta : UserControl, IView
{
- ///
- /// Interaction logic for ViewBeta.xaml
- ///
- public partial class ViewBeta : UserControl, IView
+ public ViewBeta()
{
- public ViewBeta()
- {
- InitializeComponent();
- }
+ InitializeComponent();
}
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IContentRegionContext{TDataTemplate}.cs b/src/Lemon.ModuleNavigation/Abstractions/IContentRegionContext{TDataTemplate}.cs
new file mode 100644
index 0000000..95c4262
--- /dev/null
+++ b/src/Lemon.ModuleNavigation/Abstractions/IContentRegionContext{TDataTemplate}.cs
@@ -0,0 +1,10 @@
+using System.ComponentModel;
+
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IContentRegionContext : INotifyPropertyChanged
+{
+ object? Content { get; set; }
+ TDataTemplate? ContentTemplate { get; set; }
+}
+
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IContentRegion{TDataTemplate}.cs b/src/Lemon.ModuleNavigation/Abstractions/IContentRegion{TDataTemplate}.cs
deleted file mode 100644
index 71b895a..0000000
--- a/src/Lemon.ModuleNavigation/Abstractions/IContentRegion{TDataTemplate}.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Lemon.ModuleNavigation.Abstractions;
-
-namespace Lemon.ModuleNavigation.Regions;
-
-public interface IContentRegion : IRegion
-{
- public object? Content
- {
- get;
- set;
- }
- TDataTemplate? RegionTemplate
- {
- get;
- set;
- }
-}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IDialogResult.cs b/src/Lemon.ModuleNavigation/Abstractions/IDialogResult.cs
index 3c47ebe..f008b28 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IDialogResult.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IDialogResult.cs
@@ -1,17 +1,16 @@
-using Lemon.ModuleNavigation.Dialogs;
+using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IDialogResult
{
- public interface IDialogResult
- {
- ///
- /// The parameters from the dialog.
- ///
- IDialogParameters Parameters { get; }
+ ///
+ /// The parameters from the dialog.
+ ///
+ IDialogParameters Parameters { get; }
- ///
- /// The result of the dialog.
- ///
- ButtonResult Result { get; }
- }
+ ///
+ /// The result of the dialog.
+ ///
+ ButtonResult Result { get; }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IItemsRegionDataContext{TDataTemplate}.cs b/src/Lemon.ModuleNavigation/Abstractions/IItemsRegionDataContext{TDataTemplate}.cs
new file mode 100644
index 0000000..10deafd
--- /dev/null
+++ b/src/Lemon.ModuleNavigation/Abstractions/IItemsRegionDataContext{TDataTemplate}.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel;
+
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IItemsRegionDataContext : INotifyPropertyChanged
+{
+ object? SelectedItem
+ {
+ get;
+ set;
+ }
+ TDataTemplate? ItemTemplate
+ {
+ get;
+ set;
+ }
+}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IItemsRegion{TDataTemplate}.cs b/src/Lemon.ModuleNavigation/Abstractions/IItemsRegion{TDataTemplate}.cs
deleted file mode 100644
index 45cef62..0000000
--- a/src/Lemon.ModuleNavigation/Abstractions/IItemsRegion{TDataTemplate}.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Lemon.ModuleNavigation.Abstractions;
-
-namespace Lemon.ModuleNavigation.Regions;
-
-public interface IItemsRegion : IRegion
-{
- object? SelectedItem
- {
- get;
- set;
- }
- TDataTemplate? RegionTemplate
- {
- get;
- set;
- }
- void ScrollIntoView(int index);
- void ScrollIntoView(object item);
- void Add(object item);
-}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleManager.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleManager.cs
index 1156043..93d8d7a 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleManager.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleManager.cs
@@ -16,6 +16,6 @@ ObservableCollection ActiveModules
}
IView CreateView(IModule module);
IView GetOrCreateView(IModule module, string regionName);
- void RequestNavigate(string moduleName, NavigationParameters parameters);
- void RequestNavigate(IModule module, NavigationParameters parameters);
+ void RequestNavigate(string moduleName, NavigationParameters? parameters);
+ void RequestNavigate(IModule module, NavigationParameters? parameters);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler.cs
index 647e786..cb77973 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler.cs
@@ -1,9 +1,8 @@
using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleNavigationHandler
{
- public interface IModuleNavigationHandler
- {
- void OnNavigateTo(string moduleKey, NavigationParameters parameters);
- }
+ void OnNavigateTo(string moduleKey, NavigationParameters? parameters);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler{T}.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler{T}.cs
index 5d19fff..1ecfc9c 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler{T}.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationHandler{T}.cs
@@ -1,9 +1,8 @@
using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleNavigationHandler :IModuleNavigationHandler where T : IModule
{
- public interface IModuleNavigationHandler :IModuleNavigationHandler where T : IModule
- {
- void OnNavigateTo(T module, NavigationParameters parameter);
- }
+ void OnNavigateTo(T module, NavigationParameters? parameter);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService.cs
index 74b394f..18121be 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService.cs
@@ -1,10 +1,9 @@
using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleNavigationService
{
- public interface IModuleNavigationService
- {
- IDisposable BindingNavigationHandler(IModuleNavigationHandler handler);
- void RequestModuleNavigate(string moduleKey, NavigationParameters parameters);
- }
+ IDisposable BindingNavigationHandler(IModuleNavigationHandler handler);
+ void RequestModuleNavigate(string moduleKey, NavigationParameters? parameters);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService{T}.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService{T}.cs
index 74541d7..135fb06 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService{T}.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleNavigationService{T}.cs
@@ -1,10 +1,9 @@
using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleNavigationService : IModuleNavigationService where T : IModule
{
- public interface IModuleNavigationService : IModuleNavigationService where T : IModule
- {
- IDisposable BindingNavigationHandler(IModuleNavigationHandler handler);
- void RequestModuleNavigate(T module, NavigationParameters parameters);
- }
+ IDisposable BindingNavigationHandler(IModuleNavigationHandler handler);
+ void RequestModuleNavigate(T module, NavigationParameters? parameters);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleScope.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleScope.cs
index 5e91e9d..3d70ce5 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleScope.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleScope.cs
@@ -1,10 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleScope
{
- public interface IModuleScope
- {
- IServiceCollection ScopeServiceCollection { get; }
- IServiceProvider ScopeServiceProvider { get; }
- }
+ IServiceCollection ScopeServiceCollection { get; }
+ IServiceProvider ScopeServiceProvider { get; }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IModuleServiceProvider.cs b/src/Lemon.ModuleNavigation/Abstractions/IModuleServiceProvider.cs
index 78612e4..26b4f88 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IModuleServiceProvider.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IModuleServiceProvider.cs
@@ -1,7 +1,6 @@
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IModuleServiceProvider : IServiceProvider
{
- public interface IModuleServiceProvider : IServiceProvider
- {
- }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/INavigationAware.cs b/src/Lemon.ModuleNavigation/Abstractions/INavigationAware.cs
index 6e64d75..23d0913 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/INavigationAware.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/INavigationAware.cs
@@ -1,11 +1,9 @@
-using Lemon.ModuleNavigation.Core;
+namespace Lemon.ModuleNavigation.Abstractions;
-namespace Lemon.ModuleNavigation.Abstractions
+public interface INavigationAware
{
- public interface INavigationAware
- {
- void OnNavigatedTo(NavigationContext navigationContext);
- bool IsNavigationTarget(NavigationContext navigationContext);
- void OnNavigatedFrom(NavigationContext navigationContext);
- }
+ event Action? RequestUnload;
+ void OnNavigatedTo(NavigationContext navigationContext);
+ bool IsNavigationTarget(NavigationContext navigationContext);
+ void OnNavigatedFrom(NavigationContext navigationContext);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/INavigationHandler.cs b/src/Lemon.ModuleNavigation/Abstractions/INavigationHandler.cs
index 3cf7f6b..c360091 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/INavigationHandler.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/INavigationHandler.cs
@@ -1,10 +1,7 @@
-using System.Collections.ObjectModel;
+namespace Lemon.ModuleNavigation.Abstractions;
-namespace Lemon.ModuleNavigation.Abstractions
+public interface INavigationHandler : IModuleNavigationHandler, IViewNavigationHandler
{
- public interface INavigationHandler : IModuleNavigationHandler, IViewNavigationHandler
- {
- IRegionManager RegionManager { get; }
- IModuleManager ModuleManager { get; }
- }
+ IRegionManager RegionManager { get; }
+ IModuleManager ModuleManager { get; }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/INavigationService.cs b/src/Lemon.ModuleNavigation/Abstractions/INavigationService.cs
index 77b3526..36182a8 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/INavigationService.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/INavigationService.cs
@@ -1,7 +1,6 @@
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface INavigationService: IModuleNavigationService, IViewNavigationService
{
- public interface INavigationService: IModuleNavigationService, IViewNavigationService
- {
- }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IRegion.cs b/src/Lemon.ModuleNavigation/Abstractions/IRegion.cs
index ed724b3..058557b 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IRegion.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IRegion.cs
@@ -1,16 +1,17 @@
-using Lemon.ModuleNavigation.Core;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IRegion
{
- public interface IRegion
- {
- string Name { get; }
- ObservableCollection Contexts
- {
- get;
- }
- void Activate(NavigationContext target);
- void DeActivate(NavigationContext target);
+ string Name { get; }
+ ObservableCollection Contexts
+ {
+ get;
}
+ void Activate(NavigationContext target);
+ void DeActivate(NavigationContext target);
+ void DeActivate(string viewName);
+
+ ///ongoing: last view
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IRegionManager.cs b/src/Lemon.ModuleNavigation/Abstractions/IRegionManager.cs
index 52c6558..bc855da 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IRegionManager.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IRegionManager.cs
@@ -1,12 +1,21 @@
-
-using Lemon.ModuleNavigation.Core;
+using Lemon.ModuleNavigation.Core;
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IRegionManager : IObservable, IObservable
{
- public interface IRegionManager: IObservable, IObservable
- {
- void AddRegion(string regionName, IRegion region);
- IRegion? GetRegion(string regionName);
- void RequestNavigate(string regionName, string viewName, bool requestNae, NavigationParameters? parameters = null);
- }
+ void AddRegion(string regionName, IRegion region);
+ IRegion? GetRegion(string regionName);
+
+ void RequestViewNavigate(string regionName, string viewName, NavigationParameters? parameters = null);
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
+ void RequestNavigate(string regionName, string viewName, bool requestNew, NavigationParameters? parameters = null);
+ void RequestViewUnload(string regionName, string viewName);
+ void RequestViewUnload(NavigationContext context);
+
+
+ void RequestModuleNavigate(string regionName, string moduleName, NavigationParameters? parameters);
+ void RequestModuleNavigate(string regionName, IModule module, NavigationParameters? parameters);
+ void RequestModuleUnload(string moduleName, string viewName);
+ void RequestModuleUnload(IModule module);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IServiceAware.cs b/src/Lemon.ModuleNavigation/Abstractions/IServiceAware.cs
index 74df7ae..a3f6884 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IServiceAware.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IServiceAware.cs
@@ -1,7 +1,6 @@
-namespace Lemon.ModuleNavigation.Abstractions
+namespace Lemon.ModuleNavigation.Abstractions;
+
+public interface IServiceAware
{
- public interface IServiceAware
- {
- IServiceProvider ServiceProvider { get; }
- }
+ IServiceProvider ServiceProvider { get; }
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/ITabRegion{TDataTemplate}.cs b/src/Lemon.ModuleNavigation/Abstractions/ITabRegion{TDataTemplate}.cs
deleted file mode 100644
index 460ef90..0000000
--- a/src/Lemon.ModuleNavigation/Abstractions/ITabRegion{TDataTemplate}.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Lemon.ModuleNavigation.Abstractions;
-
-namespace Lemon.ModuleNavigation.Regions;
-
-public interface ITabRegion : IRegion
-{
- object? SelectedItem
- {
- get;
- set;
- }
- TDataTemplate? RegionTemplate
- {
- get;
- set;
- }
- void Add(object item);
-}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationHandler.cs b/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationHandler.cs
index ecf3dc3..d74c595 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationHandler.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationHandler.cs
@@ -5,10 +5,10 @@ namespace Lemon.ModuleNavigation.Abstractions;
public interface IViewNavigationHandler
{
void OnNavigateTo(string regionName,
- string viewName,
- bool requestNew);
+ string viewName);
void OnNavigateTo(string regionName,
string viewName,
- NavigationParameters parameters,
- bool requestNew);
+ NavigationParameters parameters);
+ void OnViewUnload(string regionName,
+ string viewName);
}
diff --git a/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationService.cs b/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationService.cs
index 18af02b..c05cafd 100644
--- a/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationService.cs
+++ b/src/Lemon.ModuleNavigation/Abstractions/IViewNavigationService.cs
@@ -5,11 +5,48 @@ namespace Lemon.ModuleNavigation.Abstractions;
public interface IViewNavigationService
{
IDisposable BindingViewNavigationHandler(IViewNavigationHandler handler);
+ ///
+ /// RequestViewNavigation
+ ///
+ ///
+ ///
+ ///
+ /// requestNew can not decide wether to get a new instance of View,
+ /// It is controlled by IsNavigationTarget() in INavigationAware.
+ ///
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
void RequestViewNavigation(string regionName,
- string viewKey,
- bool requestNew = false);
+ string viewName,
+ bool requestNew);
+
+ void RequestViewNavigation(string regionName,
+ string viewName);
+
+ ///
+ /// RequestViewNavigation
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// requestNew can not decide wether to get a new instance of View,
+ /// It is controlled by IsNavigationTarget() in INavigationAware.
+ ///
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
void RequestViewNavigation(string regionName,
- string viewKey,
+ string viewName,
NavigationParameters parameters,
- bool requestNew = false);
+ bool requestNew);
+
+ void RequestViewNavigation(string regionName,
+ string viewName,
+ NavigationParameters parameters);
+
+ ///
+ /// RequestViewUnload
+ ///
+ ///
+ ///
+ void RequestViewUnload(string regionName,
+ string viewName);
}
diff --git a/src/Lemon.ModuleNavigation/Core/ButtonResult.cs b/src/Lemon.ModuleNavigation/Core/ButtonResult.cs
index b9e67b6..427835e 100644
--- a/src/Lemon.ModuleNavigation/Core/ButtonResult.cs
+++ b/src/Lemon.ModuleNavigation/Core/ButtonResult.cs
@@ -1,4 +1,4 @@
-namespace Lemon.ModuleNavigation.Dialogs;
+namespace Lemon.ModuleNavigation.Core;
public enum ButtonResult
{
diff --git a/src/Lemon.ModuleNavigation/Core/DialogParameters.cs b/src/Lemon.ModuleNavigation/Core/DialogParameters.cs
index c2828dc..421421c 100644
--- a/src/Lemon.ModuleNavigation/Core/DialogParameters.cs
+++ b/src/Lemon.ModuleNavigation/Core/DialogParameters.cs
@@ -1,9 +1,8 @@
using Lemon.ModuleNavigation.Abstractions;
-namespace Lemon.ModuleNavigation.Dialogs
+namespace Lemon.ModuleNavigation.Core;
+
+public class DialogParameters : BaseParameters, IDialogParameters
{
- public class DialogParameters : BaseParameters, IDialogParameters
- {
-
- }
+
}
diff --git a/src/Lemon.ModuleNavigation/Core/DialogResult.cs b/src/Lemon.ModuleNavigation/Core/DialogResult.cs
index fb28abd..dee5648 100644
--- a/src/Lemon.ModuleNavigation/Core/DialogResult.cs
+++ b/src/Lemon.ModuleNavigation/Core/DialogResult.cs
@@ -1,6 +1,6 @@
using Lemon.ModuleNavigation.Abstractions;
-namespace Lemon.ModuleNavigation.Dialogs;
+namespace Lemon.ModuleNavigation.Core;
///
/// An that contains from the dialog
diff --git a/src/Lemon.ModuleNavigation/Core/RegionNameNotFoundException.cs b/src/Lemon.ModuleNavigation/Core/RegionNameNotFoundException.cs
new file mode 100644
index 0000000..50ef332
--- /dev/null
+++ b/src/Lemon.ModuleNavigation/Core/RegionNameNotFoundException.cs
@@ -0,0 +1,9 @@
+namespace Lemon.ModuleNavigation.Core;
+
+public class RegionNameNotFoundException : Exception
+{
+ public RegionNameNotFoundException(string regionName) : base($"{regionName} was not found!")
+ {
+
+ }
+}
diff --git a/src/Lemon.ModuleNavigation/ModuleManager.cs b/src/Lemon.ModuleNavigation/ModuleManager.cs
index 50a6ebe..d156cb9 100644
--- a/src/Lemon.ModuleNavigation/ModuleManager.cs
+++ b/src/Lemon.ModuleNavigation/ModuleManager.cs
@@ -23,7 +23,7 @@ public ModuleManager(IEnumerable modules,
_regionCache = [];
_modulesCache = new ConcurrentDictionary(modules.ToDictionary(m => m.Key, m => m));
Modules = _modulesCache.Values;
- ActiveModules = new ObservableCollection(_modulesCache
+ ActiveModules = [.. _modulesCache
.Where(m =>
{
return !m.Value.LoadOnDemand;
@@ -32,7 +32,7 @@ public ModuleManager(IEnumerable modules,
{
m.Value.Initialize();
return m.Value;
- }));
+ })];
}
public ObservableCollection ActiveModules
{
@@ -63,7 +63,7 @@ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- public void RequestNavigate(string moduleName, NavigationParameters parameters)
+ public void RequestNavigate(string moduleName, NavigationParameters? parameters)
{
if (_modulesCache.TryGetValue(moduleName, out var module))
{
@@ -75,7 +75,7 @@ public void RequestNavigate(string moduleName, NavigationParameters parameters)
}
}
- public void RequestNavigate(IModule module, NavigationParameters parameters)
+ public void RequestNavigate(IModule module, NavigationParameters? parameters)
{
if (module.ForceNew)
{
diff --git a/src/Lemon.ModuleNavigation/NavigationContext.cs b/src/Lemon.ModuleNavigation/NavigationContext.cs
index 6bdd567..8f1eced 100644
--- a/src/Lemon.ModuleNavigation/NavigationContext.cs
+++ b/src/Lemon.ModuleNavigation/NavigationContext.cs
@@ -1,26 +1,36 @@
-using Lemon.ModuleNavigation.Core;
+using Lemon.ModuleNavigation.Abstractions;
+using Lemon.ModuleNavigation.Core;
namespace Lemon.ModuleNavigation;
public class NavigationContext
{
- private readonly Guid _guid;
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
internal NavigationContext(string viewName,
string regionName,
IServiceProvider serviceProvider,
bool requestNew,
NavigationParameters? navigationParameters)
{
- TargetViewName = viewName;
+ ViewName = viewName;
Parameters = navigationParameters;
RequestNew = requestNew;
RegionName = regionName;
- _guid = Guid.NewGuid();
+ ServiceProvider = serviceProvider;
+ }
+ internal NavigationContext(string viewName,
+ string regionName,
+ IServiceProvider serviceProvider,
+ NavigationParameters? navigationParameters)
+ {
+ ViewName = viewName;
+ Parameters = navigationParameters;
+ RegionName = regionName;
ServiceProvider = serviceProvider;
}
public static ViewNameComparer ViewNameComparer => new();
public static StrictComparer StrictComparer => new();
- public string TargetViewName
+ public string ViewName
{
get;
private set;
@@ -30,6 +40,7 @@ public NavigationParameters? Parameters
get;
private set;
}
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
public bool RequestNew
{
get;
@@ -40,19 +51,19 @@ public string RegionName
get;
private set;
}
- public Uri? Uri
+ public int Key => GetHashCode();
+ public IServiceProvider ServiceProvider
{
get;
- set;
}
- public Guid Guid => _guid;
- public IServiceProvider ServiceProvider
+ public IView? View
{
get;
+ set;
}
public override string ToString()
{
- return TargetViewName;
+ return $"{RegionName}.{ViewName}";
}
}
public class ViewNameComparer : IEqualityComparer
@@ -61,12 +72,12 @@ public bool Equals(NavigationContext? x, NavigationContext? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
- return x.TargetViewName == y.TargetViewName;
+ return x.ViewName == y.ViewName;
}
public int GetHashCode(NavigationContext obj)
{
- return obj.TargetViewName.GetHashCode();
+ return obj.ViewName.GetHashCode();
}
}
public class StrictComparer : IEqualityComparer
@@ -75,11 +86,11 @@ public bool Equals(NavigationContext? x, NavigationContext? y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
- return x.Guid == y.Guid;
+ return x.Key == y.Key;
}
public int GetHashCode(NavigationContext obj)
{
- return obj.Guid.GetHashCode();
+ return obj.Key.GetHashCode();
}
}
diff --git a/src/Lemon.ModuleNavigation/NavigationHandler.cs b/src/Lemon.ModuleNavigation/NavigationHandler.cs
index d1cee33..da2d2bb 100644
--- a/src/Lemon.ModuleNavigation/NavigationHandler.cs
+++ b/src/Lemon.ModuleNavigation/NavigationHandler.cs
@@ -24,26 +24,29 @@ public NavigationHandler(INavigationService navigationService,
public IModuleManager ModuleManager => _moduleManager;
- public void OnNavigateTo(IModule module, NavigationParameters parameter)
+ public void OnNavigateTo(IModule module, NavigationParameters? parameter)
{
_moduleManager.RequestNavigate(module, parameter);
}
- public void OnNavigateTo(string moduleKey, NavigationParameters parameters)
+ public void OnNavigateTo(string moduleKey, NavigationParameters? parameters)
{
_moduleManager.RequestNavigate(moduleKey, parameters);
}
public void OnNavigateTo(string regionName,
- string viewName,
- bool requestNew = false)
+ string viewName)
{
- RegionManager.RequestNavigate(regionName, viewName, requestNew, null);
+ RegionManager.RequestViewNavigate(regionName, viewName, null);
}
public void OnNavigateTo(string regionName,
string viewName,
- NavigationParameters navigationParameters,
- bool requestNew = false)
+ NavigationParameters navigationParameters)
{
- RegionManager.RequestNavigate(regionName, viewName, requestNew, navigationParameters);
+ RegionManager.RequestViewNavigate(regionName, viewName, navigationParameters);
+ }
+
+ public void OnViewUnload(string regionName, string viewName)
+ {
+ RegionManager.RequestViewUnload(regionName, viewName);
}
void IDisposable.Dispose()
diff --git a/src/Lemon.ModuleNavigation/NavigationService.cs b/src/Lemon.ModuleNavigation/NavigationService.cs
index 10efd8f..502402f 100644
--- a/src/Lemon.ModuleNavigation/NavigationService.cs
+++ b/src/Lemon.ModuleNavigation/NavigationService.cs
@@ -11,16 +11,16 @@ public class NavigationService : INavigationService
private readonly List _viewHandlers = [];
// reserve only one for now.
- private readonly ConcurrentStack<(IModule module, NavigationParameters parameter)> _bufferModule = [];
- private readonly ConcurrentStack<(string moduleName, NavigationParameters parameter)> _bufferModuleName = [];
- private readonly ConcurrentStack<(string regionName, string viewName, NavigationParameters? parameter, bool requestNew)> _bufferViewName = [];
+ private readonly ConcurrentStack<(IModule module, NavigationParameters? parameter)> _bufferModule = [];
+ private readonly ConcurrentStack<(string moduleName, NavigationParameters? parameter)> _bufferModuleName = [];
+ private readonly ConcurrentStack<(string regionName, string viewName, NavigationParameters? parameter)> _bufferViewName = [];
public NavigationService()
{
}
- public void RequestModuleNavigate(IModule module, NavigationParameters parameters)
+ public void RequestModuleNavigate(IModule module, NavigationParameters? parameters)
{
foreach (var handler in _handlers)
{
@@ -31,7 +31,7 @@ public void RequestModuleNavigate(IModule module, NavigationParameters parameter
}
_bufferModule.Push((module, parameters));
}
- public void RequestModuleNavigate(string moduleName, NavigationParameters parameters)
+ public void RequestModuleNavigate(string moduleName, NavigationParameters? parameters)
{
foreach (var handler in _handlers)
{
@@ -40,26 +40,56 @@ public void RequestModuleNavigate(string moduleName, NavigationParameters parame
_bufferModuleName.Push((moduleName, parameters));
}
public void RequestViewNavigation(string regionName,
- string viewKey,
- bool requestNew = false)
+ string viewName)
{
foreach (var handler in _viewHandlers)
{
- handler.OnNavigateTo(regionName, viewKey, requestNew);
+ handler.OnNavigateTo(regionName, viewName);
}
- _bufferViewName.Push((regionName, viewKey, null, requestNew));
+ _bufferViewName.Push((regionName, viewName, null));
}
public void RequestViewNavigation(string regionName,
- string viewKey,
+ string viewName,
+ NavigationParameters parameters)
+ {
+ foreach (var handler in _viewHandlers)
+ {
+ handler.OnNavigateTo(regionName, viewName, parameters);
+ }
+ _bufferViewName.Push((regionName, viewName, parameters));
+ }
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
+ public void RequestViewNavigation(string regionName,
+ string viewName,
+ bool requestNew)
+ {
+ foreach (var handler in _viewHandlers)
+ {
+ handler.OnNavigateTo(regionName, viewName);
+ }
+ _bufferViewName.Push((regionName, viewName, null));
+ }
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
+ public void RequestViewNavigation(string regionName,
+ string viewName,
NavigationParameters parameters,
- bool requestNew = false)
+ bool requestNew)
{
foreach (var handler in _viewHandlers)
{
- handler.OnNavigateTo(regionName, viewKey, parameters, requestNew);
+ handler.OnNavigateTo(regionName, viewName, parameters);
}
- _bufferViewName.Push((regionName, viewKey, parameters, requestNew));
+ _bufferViewName.Push((regionName, viewName, parameters));
}
+
+ public void RequestViewUnload(string regionName, string viewName)
+ {
+ foreach (var handler in _viewHandlers)
+ {
+ handler.OnViewUnload(regionName, viewName);
+ }
+ }
+
IDisposable IModuleNavigationService.BindingNavigationHandler(IModuleNavigationHandler moduleHandler)
{
_handlers.Add(moduleHandler);
@@ -90,15 +120,15 @@ IDisposable IModuleNavigationService.BindingNavigationHandler(IModuleNavigationH
IDisposable IViewNavigationService.BindingViewNavigationHandler(IViewNavigationHandler handler)
{
_viewHandlers.Add(handler);
- foreach (var (regionName, viewName, parameters, requestNew) in _bufferViewName)
+ foreach (var (regionName, viewName, parameters) in _bufferViewName)
{
if (parameters == null)
{
- handler.OnNavigateTo(regionName, viewName, requestNew);
+ handler.OnNavigateTo(regionName, viewName);
}
else
{
- handler.OnNavigateTo(regionName, viewName, parameters, requestNew);
+ handler.OnNavigateTo(regionName, viewName, parameters);
}
}
return new DisposableAction(() =>
diff --git a/src/Lemon.ModuleNavigation/RegionManager.cs b/src/Lemon.ModuleNavigation/RegionManager.cs
index 965b84a..a26575d 100644
--- a/src/Lemon.ModuleNavigation/RegionManager.cs
+++ b/src/Lemon.ModuleNavigation/RegionManager.cs
@@ -16,6 +16,7 @@ public RegionManager(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider;
}
+ [Obsolete("requestNew was obsolete.Consider IsNavigationTarget() in INavigationAware instead.")]
public void RequestNavigate(string regionName, string viewName, bool requestNew, NavigationParameters? parameters = null)
{
var context = new NavigationContext(viewName, regionName, _serviceProvider, requestNew, parameters);
@@ -40,6 +41,30 @@ public void RequestNavigate(string regionName, string viewName, bool requestNew,
});
}
}
+ public void RequestViewNavigate(string regionName, string viewName, NavigationParameters? parameters = null)
+ {
+ var context = new NavigationContext(viewName, regionName, _serviceProvider, parameters);
+ if (_regions.TryGetValue(regionName, out var region))
+ {
+ region.Activate(context);
+ ToNavigationObservers(context);
+ }
+ else
+ {
+ _buffer.AddOrUpdate(regionName,
+ key =>
+ {
+ var stack = new ConcurrentStack();
+ stack.Push(context);
+ return stack;
+ },
+ (key, value) =>
+ {
+ value.Push(context);
+ return value;
+ });
+ }
+ }
public void AddRegion(string regionName, IRegion region)
{
@@ -68,6 +93,29 @@ public void AddRegion(string regionName, IRegion region)
return region;
}
+ public void RequestViewUnload(string regionName, string viewName)
+ {
+ if (_regions.TryGetValue(regionName, out var region))
+ {
+ region.DeActivate(viewName);
+ }
+ else
+ {
+ throw new RegionNameNotFoundException(nameof(regionName));
+ }
+ }
+ public void RequestViewUnload(NavigationContext navigationContext)
+ {
+ if (_regions.TryGetValue(navigationContext.RegionName, out var region))
+ {
+ region.DeActivate(navigationContext);
+ }
+ else
+ {
+ throw new RegionNameNotFoundException(nameof(navigationContext.RegionName));
+ }
+ }
+
public IDisposable Subscribe(IObserver observer)
{
if (!_navigationObservers.Add(observer))
@@ -116,4 +164,24 @@ private void ToRegionsObservers(IRegion region)
});
}
}
+
+ public void RequestModuleNavigate(string regionName, string moduleName, NavigationParameters? parameters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RequestModuleNavigate(string regionName, IModule module, NavigationParameters? parameters)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RequestModuleUnload(string moduleName, string viewName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RequestModuleUnload(IModule module)
+ {
+ throw new NotImplementedException();
+ }
}
diff --git a/src/Lemon.ModuleNavigation/ViewInfo.cs b/src/Lemon.ModuleNavigation/ViewDiscription.cs
similarity index 100%
rename from src/Lemon.ModuleNavigation/ViewInfo.cs
rename to src/Lemon.ModuleNavigation/ViewDiscription.cs
diff --git a/src/Lemon.ModuleNavigation/ViewId.cs b/src/Lemon.ModuleNavigation/ViewId.cs
new file mode 100644
index 0000000..8c1a73c
--- /dev/null
+++ b/src/Lemon.ModuleNavigation/ViewId.cs
@@ -0,0 +1,24 @@
+using Lemon.ModuleNavigation.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lemon.ModuleNavigation;
+
+public struct ViewId
+{
+ //public ViewId(Guid navigationGuid, IView view)
+ //{
+ // Guid = view.gu;
+ //}
+ //public Guid Guid
+ //{
+ // get;
+ //}
+ //public static ViewId GetId(IView view)
+ //{
+
+ //}
+}
diff --git a/src/Package.props b/src/Package.props
index 723e559..f0fa9df 100644
--- a/src/Package.props
+++ b/src/Package.props
@@ -1,6 +1,6 @@
- 2.1.0-rc
+ 2.1.1-rc
Easley
https://github.com/NeverMorewd/Lemon.ModuleNavigation
MIT
diff --git a/src/breakdownchanges.md b/src/breakdownchanges.md
index cae9604..3cf7648 100644
--- a/src/breakdownchanges.md
+++ b/src/breakdownchanges.md
@@ -1,5 +1,7 @@
-remove namespaces:
-Lemon.ModuleNavigation.Avaloniaui.Extensions;
+### remove namespaces:
+- Lemon.ModuleNavigation.Avaloniaui.Extensions;
+- Lemon.ModuleNavigation.Avaloniaui.Dialogs;
+ original members were pulled to Lemon.ModuleNavigation.Avaloniaui.Core;
-rename namespaces:
-Lemon.ModuleNavigation.Abstracts to Lemon.ModuleNavigation.Abstractions;
\ No newline at end of file
+### rename namespaces:
+- Lemon.ModuleNavigation.Abstracts to Lemon.ModuleNavigation.Abstractions;
\ No newline at end of file