Improvement to navigation regions

This commit is contained in:
TheXamlGuy
2024-05-09 22:37:36 +01:00
parent 711353c8e9
commit 54d2b5374d
31 changed files with 173 additions and 184 deletions
@@ -5,7 +5,7 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class ClassicDesktopStyleApplicationHandler(INavigationContext navigationContext) : public class ClassicDesktopStyleApplicationHandler :
INavigateHandler<IClassicDesktopStyleApplicationLifetime> INavigateHandler<IClassicDesktopStyleApplicationLifetime>
{ {
public Task Handle(Navigate<IClassicDesktopStyleApplicationLifetime> args, public Task Handle(Navigate<IClassicDesktopStyleApplicationLifetime> args,
@@ -18,8 +18,6 @@ public class ClassicDesktopStyleApplicationHandler(INavigationContext navigation
{ {
lifeTime.MainWindow = window; lifeTime.MainWindow = window;
window.DataContext = args.Content; window.DataContext = args.Content;
navigationContext.Set(window);
} }
} }
+1 -3
View File
@@ -4,7 +4,7 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class ContentControlHandler(INavigationContext navigationContext) : public class ContentControlHandler :
INavigateHandler<ContentControl> INavigateHandler<ContentControl>
{ {
public async Task Handle(Navigate<ContentControl> args, public async Task Handle(Navigate<ContentControl> args,
@@ -51,8 +51,6 @@ public class ContentControlHandler(INavigationContext navigationContext) :
control.DataContext = args.Content; control.DataContext = args.Content;
contentControl.Content = control; contentControl.Content = control;
navigationContext.Set(control);
await taskCompletionSource.Task; await taskCompletionSource.Task;
} }
} }
+2 -2
View File
@@ -17,7 +17,7 @@ public class ContentTemplate :
if (observableViewModel.Provider is IServiceProvider provider) if (observableViewModel.Provider is IServiceProvider provider)
{ {
IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService<IContentTemplateDescriptorProvider>(); IContentTemplateDescriptorProvider? contentTemplateProvider = provider.GetService<IContentTemplateDescriptorProvider>();
INavigationContext? viewModelContentBinder = provider.GetService<INavigationContext>(); INavigationRegion? viewModelContentBinder = provider.GetService<INavigationRegion>();
if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor) if (contentTemplateProvider?.Get(item.GetType().Name) is IContentTemplateDescriptor descriptor)
{ {
@@ -55,7 +55,7 @@ public class ContentTemplate :
control.Loaded += HandleLoaded; control.Loaded += HandleLoaded;
control.Unloaded += HandleUnloaded; control.Unloaded += HandleUnloaded;
viewModelContentBinder?.Set(control); //viewModelContentBinder?.Register(control);
return control; return control;
} }
+1 -56
View File
@@ -8,7 +8,7 @@ using Toolkit.UI.Controls.Avalonia;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class FrameHandler(INavigationContext navigationContext) : public class FrameHandler :
INavigateHandler<Frame>, INavigateHandler<Frame>,
INavigateBackHandler<Frame> INavigateBackHandler<Frame>
{ {
@@ -45,32 +45,6 @@ public class FrameHandler(INavigationContext navigationContext) :
{ {
await deactivating.Deactivating(); await deactivating.Deactivating();
} }
Type contentType = content.GetType();
if (contentType.GetInterfaces() is Type[] contracts)
{
foreach (Type contract in contracts)
{
if (contract.Name == typeof(IDeactivating<>).Name &&
contract.GetGenericArguments() is { Length: 1 } arguments)
{
if (contentType.GetMethods().FirstOrDefault(x =>
x.Name == "Deactivating" && x.ReturnType == typeof(Task<>)
.MakeGenericType(arguments[0]))
is MethodInfo methodInfo)
{
if (methodInfo.GetCustomAttribute<NavigationContextAttribute>()
is NavigationContextAttribute attribute)
{
if (await methodInfo.InvokeAsync<object?>(content) is object result)
{
results.Add(attribute.Name, result);
}
}
}
}
}
}
} }
} }
} }
@@ -100,33 +74,6 @@ public class FrameHandler(INavigationContext navigationContext) :
{ {
await deactivated.Deactivated(); await deactivated.Deactivated();
} }
Type contentType = content.GetType();
if (contentType.GetInterfaces() is Type[] contracts)
{
foreach (Type contract in contracts)
{
if (contract.Name == typeof(IActivated<>).Name &&
contract.GetGenericArguments() is { Length: 1 } arguments)
{
if (contentType.GetMethods().FirstOrDefault(x =>
x.Name == "NavigatedToAsync" &&
x.GetCustomAttribute<NavigationContextAttribute>()
is NavigationContextAttribute attribute && results.ContainsKey(attribute.Name))
is MethodInfo methodInfo)
{
if (methodInfo.GetCustomAttribute<NavigationContextAttribute>()
is NavigationContextAttribute attribute)
{
if (results.TryGetValue(attribute.Name, out object? value))
{
await methodInfo.InvokeAsync(content, value);
}
}
}
}
}
}
} }
} }
@@ -181,8 +128,6 @@ public class FrameHandler(INavigationContext navigationContext) :
} }
control.DataContext = args.Content; control.DataContext = args.Content;
navigationContext.Set(control);
NavigatedTo(args.Sender, control); NavigatedTo(args.Sender, control);
frame.NavigateFromObject(control, new FrameNavigationOptions { TransitionInfoOverride = new SuppressNavigationTransitionInfo() }); frame.NavigateFromObject(control, new FrameNavigationOptions { TransitionInfoOverride = new SuppressNavigationTransitionInfo() });
} }
-8
View File
@@ -1,8 +0,0 @@
using Avalonia.Controls;
namespace Toolkit.Avalonia;
public interface INavigationContext
{
void Set(Control control);
}
@@ -126,7 +126,7 @@ public static class IServiceCollectionExtensions
services.AddTransient<IDispatcher, AvaloniaDispatcher>(); services.AddTransient<IDispatcher, AvaloniaDispatcher>();
services.AddTransient<IContentTemplate, ContentTemplate>(); services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient<INavigationContext, NavigationContext>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddNavigateHandler<ClassicDesktopStyleApplicationHandler>(); services.AddNavigateHandler<ClassicDesktopStyleApplicationHandler>();
services.AddNavigateHandler<SingleViewApplicationHandler>(); services.AddNavigateHandler<SingleViewApplicationHandler>();
@@ -134,7 +134,7 @@ public static class IServiceCollectionExtensions
services.AddNavigateHandler<FrameHandler>(); services.AddNavigateHandler<FrameHandler>();
services.AddNavigateHandler<ContentDialogHandler>(); services.AddNavigateHandler<ContentDialogHandler>();
services.AddScoped<INavigationContextCollection, NavigationContextCollection>(provider => new NavigationContextCollection services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>(provider => new NavigationRegionCollection
{ {
{ typeof(IClassicDesktopStyleApplicationLifetime), typeof(IClassicDesktopStyleApplicationLifetime) }, { typeof(IClassicDesktopStyleApplicationLifetime), typeof(IClassicDesktopStyleApplicationLifetime) },
{ typeof(ISingleViewApplicationLifetime), typeof(ISingleViewApplicationLifetime) } { typeof(ISingleViewApplicationLifetime), typeof(ISingleViewApplicationLifetime) }
@@ -148,7 +148,7 @@ public static class IServiceCollectionExtensions
services.AddTransient<IContentTemplateDescriptorProvider, ContentTemplateDescriptorProvider>(); services.AddTransient<IContentTemplateDescriptorProvider, ContentTemplateDescriptorProvider>();
services.AddTransient<IContentTemplate, ContentTemplate>(); services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient<INavigationContext, NavigationContext>(); services.AddTransient<INavigationRegion, NavigationRegion>();
services.AddNavigateHandler<ContentControlHandler>(); services.AddNavigateHandler<ContentControlHandler>();
services.AddNavigateHandler<FrameHandler>(); services.AddNavigateHandler<FrameHandler>();
-36
View File
@@ -1,36 +0,0 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using System.Reflection;
using Toolkit.Foundation;
namespace Toolkit.Avalonia;
public class NavigationContext(INavigationContextCollection contexts) :
INavigationContext
{
public void Set(Control control)
{
if (control.GetType().GetCustomAttributes<NavigationTargetAttribute>()
is IEnumerable<NavigationTargetAttribute> attributes)
{
foreach (NavigationTargetAttribute attribute in attributes)
{
if (!contexts.ContainsKey(attribute.Name))
{
if (control.Find<TemplatedControl>(attribute.Name) is TemplatedControl content)
{
contexts.Add(attribute.Name, content);
void HandleUnloaded(object? sender, RoutedEventArgs args)
{
control.Unloaded -= HandleUnloaded;
contexts.Remove(attribute.Name);
}
control.Unloaded += HandleUnloaded;
}
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Toolkit.Foundation;
namespace Toolkit.Avalonia;
public class NavigationRegion(INavigationRegionCollection collection) :
INavigationRegion
{
public void Register(string name,
object target)
{
if (target is Control control)
{
if (!collection.ContainsKey(name))
{
collection.Add(name, control);
void HandleUnloaded(object? sender, RoutedEventArgs args)
{
control.Unloaded -= HandleUnloaded;
collection.Remove(name);
}
control.Unloaded += HandleUnloaded;
}
}
}
}
@@ -5,7 +5,7 @@ using Toolkit.Foundation;
namespace Toolkit.Avalonia; namespace Toolkit.Avalonia;
public class SingleViewApplicationHandler(INavigationContext navigationContext) : public class SingleViewApplicationHandler :
INavigateHandler<ISingleViewApplicationLifetime> INavigateHandler<ISingleViewApplicationLifetime>
{ {
public Task Handle(Navigate<ISingleViewApplicationLifetime> args, public Task Handle(Navigate<ISingleViewApplicationLifetime> args,
@@ -18,8 +18,6 @@ public class SingleViewApplicationHandler(INavigationContext navigationContext)
{ {
lifeTime.MainView = control; lifeTime.MainView = control;
control.DataContext = args.Content; control.DataContext = args.Content;
navigationContext.Set(control);
} }
} }
+2 -2
View File
@@ -39,8 +39,8 @@ public class ComponentBuilder :
services.AddTransient<INavigationScope, NavigationScope>(); services.AddTransient<INavigationScope, NavigationScope>();
services.AddTransient<INavigationProvider, NavigationProvider>(); services.AddTransient<INavigationProvider, NavigationProvider>();
services.AddScoped<INavigationContextCollection, NavigationContextCollection>(); services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>();
services.AddTransient<INavigationContextProvider, NavigationContextProvider>(); services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>();
services.AddHandler<NavigateHandler>(); services.AddHandler<NavigateHandler>();
services.AddHandler<NavigateBackHandler>(); services.AddHandler<NavigateBackHandler>();
+4 -4
View File
@@ -25,10 +25,10 @@ public class ComponentFactory(IServiceProvider provider,
provider.GetRequiredService<IProxyService<IComponentHostCollection>>()); provider.GetRequiredService<IProxyService<IComponentHostCollection>>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<INavigationContextCollection>()); provider.GetRequiredService<INavigationRegionCollection>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<INavigationContextProvider>()); provider.GetRequiredService<INavigationRegionProvider>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<IComponentScopeCollection>()); provider.GetRequiredService<IComponentScopeCollection>());
@@ -37,7 +37,7 @@ public class ComponentFactory(IServiceProvider provider,
provider.GetRequiredService<IComponentScopeProvider>()); provider.GetRequiredService<IComponentScopeProvider>());
services.AddRange(proxy.Services); services.AddRange(proxy.Services);
services.AddSingleton(new ComponentScope(name)); services.AddSingleton(new NamedComponent(name));
if (servicesDelegate is not null) if (servicesDelegate is not null)
{ {
@@ -45,7 +45,7 @@ public class ComponentFactory(IServiceProvider provider,
} }
}); });
builder.AddConfiguration<TConfiguration>(name, configuration); builder.AddConfiguration(name, configuration);
IComponentHost host = builder.Build(); IComponentHost host = builder.Build();
scopes.Add(new ComponentScopeDescriptor(name, scopes.Add(new ComponentScopeDescriptor(name,
+3 -3
View File
@@ -23,10 +23,10 @@ public class ComponentInitializer(IEnumerable<IComponent> components,
provider.GetRequiredService<IProxyService<IComponentHostCollection>>()); provider.GetRequiredService<IProxyService<IComponentHostCollection>>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<INavigationContextCollection>()); provider.GetRequiredService<INavigationRegionCollection>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<INavigationContextProvider>()); provider.GetRequiredService<INavigationRegionProvider>());
services.AddScoped(_ => services.AddScoped(_ =>
provider.GetRequiredService<IComponentScopeCollection>()); provider.GetRequiredService<IComponentScopeCollection>());
@@ -36,7 +36,7 @@ public class ComponentInitializer(IEnumerable<IComponent> components,
services.AddRange(typedServices.Services); services.AddRange(typedServices.Services);
services.AddSingleton(new ComponentScope(component.GetType().Name)); services.AddSingleton(new NamedComponent(component.GetType().Name));
}); });
IComponentHost host = builder.Build(); IComponentHost host = builder.Build();
-3
View File
@@ -1,3 +0,0 @@
namespace Toolkit.Foundation;
public record ComponentScope(string Name);
+5 -5
View File
@@ -33,8 +33,8 @@ public class DefaultHostBuilder :
services.AddScoped<IProxyService<IPublisher>>(provider => services.AddScoped<IProxyService<IPublisher>>(provider =>
new ProxyService<IPublisher>(provider.GetRequiredService<IPublisher>())); new ProxyService<IPublisher>(provider.GetRequiredService<IPublisher>()));
services.AddScoped<IProxyService<INavigationContextProvider>>(provider => services.AddScoped<IProxyService<INavigationRegionProvider>>(provider =>
new ProxyService<INavigationContextProvider>(provider.GetRequiredService<INavigationContextProvider>())); new ProxyService<INavigationRegionProvider>(provider.GetRequiredService<INavigationRegionProvider>()));
services.AddScoped<IProxyService<IComponentHostCollection>>(provider => services.AddScoped<IProxyService<IComponentHostCollection>>(provider =>
new ProxyService<IComponentHostCollection>(provider.GetRequiredService<IComponentHostCollection>())); new ProxyService<IComponentHostCollection>(provider.GetRequiredService<IComponentHostCollection>()));
@@ -45,12 +45,12 @@ public class DefaultHostBuilder :
services.AddTransient<INavigationProvider, NavigationProvider>(); services.AddTransient<INavigationProvider, NavigationProvider>();
services.AddScoped<INavigationContextCollection, NavigationContextCollection>(); services.AddScoped<INavigationRegionCollection, NavigationRegionCollection>();
services.AddTransient<INavigationContextProvider, NavigationContextProvider>(); services.AddTransient<INavigationRegionProvider, NavigationRegionProvider>();
services.AddTransient<INavigationScope, NavigationScope>(); services.AddTransient<INavigationScope, NavigationScope>();
services.AddSingleton(new ComponentScope("Root")); services.AddSingleton(new NamedComponent("Root"));
services.AddScoped<IComponentScopeCollection, ComponentScopeCollection>(provider => new ComponentScopeCollection services.AddScoped<IComponentScopeCollection, ComponentScopeCollection>(provider => new ComponentScopeCollection
{ {
new ComponentScopeDescriptor("Root", provider.GetRequiredService<IServiceProvider>()) new ComponentScopeDescriptor("Root", provider.GetRequiredService<IServiceProvider>())
+7
View File
@@ -0,0 +1,7 @@
namespace Toolkit.Foundation;
public interface INavigationRegion
{
void Register(string name,
object target);
}
@@ -1,4 +1,4 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public interface INavigationContextCollection : public interface INavigationRegionCollection :
IDictionary<object, object?>; IDictionary<object, object?>;
@@ -1,6 +1,6 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public interface INavigationContextProvider public interface INavigationRegionProvider
{ {
object? Get(object key); object? Get(object key);
+6
View File
@@ -0,0 +1,6 @@
namespace Toolkit.Foundation;
public record NamedComponent(string Name)
{
public override string ToString() => Name;
}
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class NavigateHandler(ComponentScope scope, public class NavigateHandler(NamedComponent scope,
IComponentScopeProvider provider) : IComponentScopeProvider provider) :
INotificationHandler<Navigate> INotificationHandler<Navigate>
{ {
@@ -1,12 +0,0 @@
namespace Toolkit.Foundation;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class NavigationContextAttribute : Attribute
{
public NavigationContextAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
@@ -1,4 +0,0 @@
namespace Toolkit.Foundation;
public class NavigationContextCollection : Dictionary<object, object?>,
INavigationContextCollection;
@@ -0,0 +1,4 @@
namespace Toolkit.Foundation;
public class NavigationRegionCollection : Dictionary<object, object?>,
INavigationRegionCollection;
@@ -1,7 +1,7 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public class NavigationContextProvider(INavigationContextCollection contexts) : public class NavigationRegionProvider(INavigationRegionCollection contexts) :
INavigationContextProvider INavigationRegionProvider
{ {
public object? Get(object key) => public object? Get(object key) =>
contexts.TryGetValue(key, out object? target) ? target : default; contexts.TryGetValue(key, out object? target) ? target : default;
+1 -1
View File
@@ -6,7 +6,7 @@ public class NavigationScope(IPublisher publisher,
IServiceProvider provider, IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
INavigationProvider navigationProvider, INavigationProvider navigationProvider,
INavigationContextProvider navigationContextProvider, INavigationRegionProvider navigationContextProvider,
IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) : IContentTemplateDescriptorProvider contentTemplateDescriptorProvider) :
INavigationScope INavigationScope
{ {
+1 -2
View File
@@ -9,8 +9,7 @@ public class ConditionAction :
IAction IAction
{ {
public static readonly DirectProperty<ConditionAction, ActionCollection> ActionsProperty = public static readonly DirectProperty<ConditionAction, ActionCollection> ActionsProperty =
AvaloniaProperty.RegisterDirect<ConditionAction, ActionCollection>(nameof(Actions), AvaloniaProperty.RegisterDirect<ConditionAction, ActionCollection>(nameof(Actions), x => x.Actions);
x => x.Actions);
public static readonly StyledProperty<ICondition> ConditionProperty = public static readonly StyledProperty<ICondition> ConditionProperty =
AvaloniaProperty.Register<ConditionAction, ICondition>(nameof(Condition)); AvaloniaProperty.Register<ConditionAction, ICondition>(nameof(Condition));
+2 -1
View File
@@ -74,7 +74,8 @@ public class NavigateAction :
ParameterBindings.Select(binding => new KeyValuePair<string, object>(binding.Key, binding.Value)).ToArray() : ParameterBindings.Select(binding => new KeyValuePair<string, object>(binding.Key, binding.Value)).ToArray() :
Enumerable.Empty<KeyValuePair<string, object>>()]; Enumerable.Empty<KeyValuePair<string, object>>()];
observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null, control.DataContext, Navigated, parameters)); observableViewModel.Publisher.Publish(new Navigate(Route, Context == this ? control : Context, Scope ?? null,
control.DataContext, Navigated, parameters)).ConfigureAwait(false);
} }
} }
+1 -1
View File
@@ -35,7 +35,7 @@ public class NavigateBackAction :
if (control.DataContext is IObservableViewModel observableViewModel) if (control.DataContext is IObservableViewModel observableViewModel)
{ {
observableViewModel.Publisher.Publish(new NavigateBack(Context observableViewModel.Publisher.Publish(new NavigateBack(Context
?? null, Scope ?? null)).GetAwaiter().GetResult(); ?? null, Scope ?? null)).ConfigureAwait(false);
} }
} }
@@ -0,0 +1,47 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Metadata;
using Avalonia.Xaml.Interactivity;
using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation;
namespace Toolkit.UI.Avalonia;
public class NavigateRegionAction :
AvaloniaObject,
IAction
{
public static readonly DirectProperty<NavigateRegionAction, ActionCollection> ActionsProperty =
AvaloniaProperty.RegisterDirect<NavigateRegionAction, ActionCollection>(nameof(Actions), x => x.Actions);
public static readonly StyledProperty<string> NameProperty =
AvaloniaProperty.Register<NavigateRegionAction, string>(nameof(Name));
private ActionCollection? actions;
[Content]
public ActionCollection Actions => actions ??= [];
public string Name
{
get => GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
public object? Execute(object? sender,
object? parameter)
{
if (sender is Control control)
{
if (control.DataContext is IObservableViewModel observableViewModel)
{
if (observableViewModel.Provider.GetRequiredService<INavigationRegion>() is INavigationRegion navigationRegion)
{
navigationRegion.Register(Name, sender);
Interaction.ExecuteActions(sender, Actions, parameter);
}
}
}
return true;
}
}
@@ -2,6 +2,24 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Toolkit.UI.Controls.Avalonia"> xmlns:controls="using:Toolkit.UI.Controls.Avalonia">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="PersonPictureForegroundThemeBrush" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeForegroundThemeBrush" ResourceKey="TextOnAccentFillColorPrimaryBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeFillThemeBrush" ResourceKey="AccentFillColorDefaultBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeStrokeThemeBrush" ResourceKey="ControlFillColorTransparentBrush" />
<StaticResource x:Key="PersonPictureEllipseFillThemeBrush" ResourceKey="ControlAltFillColorQuarternaryBrush" />
<StaticResource x:Key="PersonPictureEllipseFillStrokeBrush" ResourceKey="CardStrokeColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<StaticResource x:Key="PersonPictureForegroundThemeBrush" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeForegroundThemeBrush" ResourceKey="TextOnAccentFillColorPrimaryBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeFillThemeBrush" ResourceKey="AccentFillColorDefaultBrush" />
<StaticResource x:Key="PersonPictureEllipseBadgeStrokeThemeBrush" ResourceKey="ControlFillColorTransparentBrush" />
<StaticResource x:Key="PersonPictureEllipseFillThemeBrush" ResourceKey="ControlAltFillColorQuarternaryBrush" />
<StaticResource x:Key="PersonPictureEllipseFillStrokeBrush" ResourceKey="CardStrokeColorDefaultBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<x:Double x:Key="PersonPictureEllipseBadgeStrokeOpacity">1</x:Double> <x:Double x:Key="PersonPictureEllipseBadgeStrokeOpacity">1</x:Double>
<x:Double x:Key="PersonPictureEllipseBadgeImageSourceStrokeOpacity">1</x:Double> <x:Double x:Key="PersonPictureEllipseBadgeImageSourceStrokeOpacity">1</x:Double>
<x:Double x:Key="PersonPictureEllipseStrokeThickness">1</x:Double> <x:Double x:Key="PersonPictureEllipseStrokeThickness">1</x:Double>
@@ -2,39 +2,41 @@
internal class PersonPictureInitialsGenerator internal class PersonPictureInitialsGenerator
{ {
public static PersonPictureCharacterType GetCharacterType(string str) public static PersonPictureCharacterType GetCharacterType(string content)
{ {
PersonPictureCharacterType result = PersonPictureCharacterType.Other; PersonPictureCharacterType result = PersonPictureCharacterType.Other;
for (int i = 0; i < 3; i++) if (content is { Length: > 0 })
{ {
if ((i >= str.Length) || (str[i] == '\0') || (str[i] == 0xFEFF)) for (int i = 0; i < 3; i++)
{ {
break; if ((i >= content.Length) || (content[i] == '\0') || (content[i] == 0xFEFF))
} {
break;
}
char character = str[i]; char character = content[i];
PersonPictureCharacterType evaluationResult = GetCharacterType(character); PersonPictureCharacterType evaluationResult = GetCharacterType(character);
switch (evaluationResult) switch (evaluationResult)
{ {
case PersonPictureCharacterType.Glyph: case PersonPictureCharacterType.Glyph:
result = PersonPictureCharacterType.Glyph; result = PersonPictureCharacterType.Glyph;
break; break;
case PersonPictureCharacterType.Symbolic: case PersonPictureCharacterType.Symbolic:
if (result != PersonPictureCharacterType.Glyph) if (result != PersonPictureCharacterType.Glyph)
{ {
result = PersonPictureCharacterType.Symbolic; result = PersonPictureCharacterType.Symbolic;
} }
break; break;
case PersonPictureCharacterType.Standard: case PersonPictureCharacterType.Standard:
if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic)) if ((result != PersonPictureCharacterType.Glyph) && (result != PersonPictureCharacterType.Symbolic))
{ {
result = PersonPictureCharacterType.Standard; result = PersonPictureCharacterType.Standard;
} }
break; break;
default: default:
break; break;
}
} }
} }
return result; return result;
@@ -6,6 +6,7 @@
<MergeResourceInclude Source="../ContentDialog/ContentDialog.axaml" /> <MergeResourceInclude Source="../ContentDialog/ContentDialog.axaml" />
<MergeResourceInclude Source="../SettingsExpander/SettingsExpander.axaml" /> <MergeResourceInclude Source="../SettingsExpander/SettingsExpander.axaml" />
<MergeResourceInclude Source="../SettingsExpander/SettingsExpanderItem.axaml" /> <MergeResourceInclude Source="../SettingsExpander/SettingsExpanderItem.axaml" />
<MergeResourceInclude Source="../PersonPicture/PersonPicture.axaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Styles.Resources> </Styles.Resources>