Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/Lemon.ModuleNavigation.Avaloniaui/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Avaloniaui")]
[assembly: XmlnsDefinition("https://github.com/NeverMorewd/Lemon.ModuleNavigation", "Lemon.ModuleNavigation.Avaloniaui.Regions")]
2 changes: 1 addition & 1 deletion src/Lemon.ModuleNavigation.Avaloniaui/DialogService.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@

<ItemGroup>
<AvaloniaXaml Remove="bin\**" />
<AvaloniaXaml Remove="RegionsOld\**" />
<Compile Remove="bin\**" />
<Compile Remove="RegionsOld\**" />
<EmbeddedResource Remove="bin\**" />
<EmbeddedResource Remove="RegionsOld\**" />
<None Remove="bin\**" />
<None Remove="RegionsOld\**" />
</ItemGroup>

<ItemGroup>
Expand Down
20 changes: 13 additions & 7 deletions src/Lemon.ModuleNavigation.Avaloniaui/NavigationExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void LoadedHandler(object? sender, RoutedEventArgs e)
{
var serviceProvider = navigationProvider!.ServiceProvider;
var handler = serviceProvider.GetRequiredService<INavigationHandler>();
handler.RegionManager.AddRegion(currentValue, control.ToContainer(currentValue));
handler.RegionManager.AddRegion(currentValue, control.ToRegion(currentValue));
}
control.Loaded -= LoadedHandler;
}
Expand Down Expand Up @@ -112,7 +112,7 @@ public static void SetModuleContainerName(Control control, string value)
#region CanUnloadProperty
public static readonly AttachedProperty<bool> CanUnloadProperty =
AvaloniaProperty.RegisterAttached<NavigationExtension, Button, bool>("CanUnload",
defaultValue: true,
defaultValue: false,
coerce: CoerceCanUnload);

private static bool CoerceCanUnload(AvaloniaObject targetObject, bool currentValue)
Expand Down Expand Up @@ -160,17 +160,23 @@ private static void UnloadModule(object? sender, RoutedEventArgs e)
{
if (sender is Button button)
{
var view = button.FindLogicalAncestorOfType<IView>(includeSelf: false);
if (view != null)
{
}
var tabItem = button.FindLogicalAncestorOfType<TabItem>(includeSelf: false) ?? throw new InvalidOperationException($"There is no 'TabItem' found in parents of {button}");
var tabContainer = tabItem.FindLogicalAncestorOfType<TabControl>(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<INavigationHandler>();
handler.ModuleManager.ActiveModules.Remove(item);
if (tabContainer.DataContext is IServiceAware serviceAware)
{
var handler = serviceAware.ServiceProvider.GetRequiredService<INavigationHandler>();
handler.ModuleManager.ActiveModules.Remove(item);
}
}
}
}
Expand Down
117 changes: 117 additions & 0 deletions src/Lemon.ModuleNavigation.Avaloniaui/Regions/ContentRegion.cs
Original file line number Diff line number Diff line change
@@ -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<IDataTemplate>
{
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;

/// <summary>
/// When Views with same ViewName were found, the latest one will be picked.
/// </summary>
/// <param name="target"></param>
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));
}
}
134 changes: 134 additions & 0 deletions src/Lemon.ModuleNavigation.Avaloniaui/Regions/ItemsRegion.cs
Original file line number Diff line number Diff line change
@@ -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<IDataTemplate>
{
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);
}

/// <summary>
/// When Views with same ViewName were found, the earliest one will be picked.
/// </summary>
/// <param name="target"></param>
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));
}
}
Loading
Loading