Get ContentControl navigation working

This commit is contained in:
TheXamlGuy
2024-02-11 18:09:22 +00:00
parent 565c6866d8
commit 812556f8b0
133 changed files with 335 additions and 195 deletions
@@ -0,0 +1,9 @@
using Microsoft.UI.Xaml;
namespace Hyperbar.UI.Windows;
public interface IViewModelContentBinder
{
void Bind(FrameworkElement view,
object context);
}
@@ -1,6 +0,0 @@
namespace Hyperbar.UI.Windows;
public interface IViewModelTemplate
{
}
@@ -0,0 +1,6 @@
namespace Hyperbar.UI.Windows;
public interface IViewModelTemplateSelector
{
}
+16 -6
View File
@@ -7,15 +7,25 @@ public sealed class NavigateAction :
DependencyObject, DependencyObject,
IAction IAction
{ {
public static readonly DependencyProperty PathProperty = public static readonly DependencyProperty NameProperty =
DependencyProperty.Register(nameof(Path), DependencyProperty.Register(nameof(Name),
typeof(string), typeof(NavigateAction), typeof(string), typeof(NavigateAction),
new PropertyMetadata(null)); new PropertyMetadata(null));
public string Path public static readonly DependencyProperty TargetNameProperty =
DependencyProperty.Register(nameof(Name),
typeof(string), typeof(NavigateAction),
new PropertyMetadata(null));
public string Name
{ {
get => (string)GetValue(PathProperty); get => (string)GetValue(NameProperty);
set => SetValue(PathProperty, value); set => SetValue(NameProperty, value);
}
public string TargetName
{
get => (string)GetValue(TargetNameProperty);
set => SetValue(TargetNameProperty, value);
} }
public object Execute(object sender, object parameter) public object Execute(object sender, object parameter)
@@ -24,7 +34,7 @@ public sealed class NavigateAction :
{ {
if (frameworkElement.DataContext is IObservableViewModel observableViewModel) if (frameworkElement.DataContext is IObservableViewModel observableViewModel)
{ {
observableViewModel.Publisher.PublishAsync(new Navigate(Path)) observableViewModel.Publisher.PublishAsync(new Navigate(Name, TargetName ?? null))
.GetAwaiter().GetResult(); .GetAwaiter().GetResult();
} }
} }
@@ -0,0 +1,31 @@
using Microsoft.UI.Xaml;
using System.Reflection;
namespace Hyperbar.UI.Windows;
public class ViewModelContentBinder(NavigationTargetCollection contents) :
IViewModelContentBinder
{
public void Bind(FrameworkElement view,
object context)
{
if (context.GetType().GetCustomAttributes<NavigationTargetAttribute>()
is IEnumerable<NavigationTargetAttribute> attributes)
{
foreach (NavigationTargetAttribute attribute in attributes)
{
if (view.FindName(attribute.Name) is FrameworkElement content)
{
contents.Add(attribute.Name, content);
void HandleUnloaded(object sender, RoutedEventArgs args)
{
view.Unloaded -= HandleUnloaded;
contents.Remove(attribute.Name);
}
view.Unloaded += HandleUnloaded;
}
}
}
}
}
@@ -4,22 +4,13 @@ using Microsoft.UI.Xaml.Markup;
namespace Hyperbar.UI.Windows; namespace Hyperbar.UI.Windows;
public class ViewModelBinder public class ViewModelTemplateSelector(IViewModelTemplateProvider descriptors) :
{
public void Bind(object viewModel,
FrameworkElement view)
{
view.DataContext ??= viewModel;
}
}
public class ViewModelTemplate(IViewModelTemplateDescriptorProvider descriptors) :
DataTemplateSelector, DataTemplateSelector,
IViewModelTemplate IViewModelTemplateSelector
{ {
protected override DataTemplate SelectTemplateCore(object item) protected override DataTemplate SelectTemplateCore(object item)
{ {
return descriptors.Get(item.GetType().Name) is IViewModelTemplateDescriptor descriptor return descriptors.Get(item.GetType().Name) is Hyperbar.IViewModelTemplate descriptor
? CreateDataTemplate(descriptor) ? CreateDataTemplate(descriptor)
: new DataTemplate(); : new DataTemplate();
} }
@@ -28,7 +19,7 @@ public class ViewModelTemplate(IViewModelTemplateDescriptorProvider descriptors)
DependencyObject container) => DependencyObject container) =>
SelectTemplateCore(item); SelectTemplateCore(item);
private static DataTemplate CreateDataTemplate(IViewModelTemplateDescriptor descriptor) private static DataTemplate CreateDataTemplate(Hyperbar.IViewModelTemplate descriptor)
{ {
string xamlString = @$" string xamlString = @$"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" <DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
+12 -7
View File
@@ -3,24 +3,28 @@ using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.UI.Windows; namespace Hyperbar.UI.Windows;
public class FrameHandler : public class ContentControlHandler(IViewModelContentBinder viewModelContentBinder) :
INavigationHandler<Frame> INavigationHandler<ContentControl>
{ {
public Task Handle(Navigate<Frame> args, public Task Handle(Navigate<ContentControl> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken)
{ {
if (args.Target is ContentControl contentControl)
{
contentControl.Content = args.View;
}
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
public class WindowHandler : public class WindowHandler(IViewModelContentBinder viewModelContentBinder) :
INavigationHandler<Window> INavigationHandler<Window>
{ {
public Task Handle(Navigate<Window> args, public Task Handle(Navigate<Window> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (args.View is Window window) if (args.Target is Window window)
{ {
if (window.Content is FrameworkElement content) if (window.Content is FrameworkElement content)
{ {
@@ -33,8 +37,9 @@ public class WindowHandler :
} }
} }
//ViewModelBinder.Bind(args.ViewModel, content); viewModelContentBinder.Bind(content, window);
window.Closed += HandleClosed; window.Closed += HandleClosed;
content.DataContext = args.ViewModel;
} }
window.Activate(); window.Activate();
@@ -2,12 +2,11 @@
<UserControl <UserControl
x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerView" x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:ui="using:Hyperbar.UI.Windows">
<ItemsControl <ItemsControl
HorizontalAlignment="Right" HorizontalAlignment="Right"
HorizontalContentAlignment="Right" HorizontalContentAlignment="Right"
ItemTemplateSelector="{Binding Template}" ItemTemplateSelector="{Binding ViewModelTemplateSelector}"
ItemsSource="{x:Bind ViewModel}"> ItemsSource="{x:Bind ViewModel}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@@ -7,14 +7,14 @@ namespace Hyperbar.Widget.MediaController.Windows;
public class MediaControllerViewModel : public class MediaControllerViewModel :
ObservableCollectionViewModel<WidgetComponentViewModel> ObservableCollectionViewModel<WidgetComponentViewModel>
{ {
public MediaControllerViewModel(IViewModelTemplate template, public MediaControllerViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{ {
Template = template; viewModelTemplateSelector = viewModelTemplateSelector;
Add<MediaInformationViewModel>(); Add<MediaInformationViewModel>();
@@ -28,5 +28,5 @@ public class MediaControllerViewModel :
await publisher.PublishAsync<Request<MediaNext>>())); await publisher.PublishAsync<Request<MediaNext>>()));
} }
public IViewModelTemplate Template { get; } public IViewModelTemplateSelector ViewModelTemplateSelector { get; }
} }
@@ -5,6 +5,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FlipView <FlipView
Width="360" Width="360"
ItemTemplateSelector="{Binding Template}" ItemTemplateSelector="{Binding ViewModelTemplateSelector}"
ItemsSource="{Binding}" /> ItemsSource="{Binding}" />
</UserControl> </UserControl>
@@ -2,7 +2,7 @@
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public class MediaControllerWidgetViewModel(IViewModelTemplate template, public class MediaControllerWidgetViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
@@ -12,5 +12,5 @@ public class MediaControllerWidgetViewModel(IViewModelTemplate template,
ObservableCollectionViewModel<MediaControllerViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, items), ObservableCollectionViewModel<MediaControllerViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, items),
IWidgetViewModel IWidgetViewModel
{ {
public IViewModelTemplate Template => template; public IViewModelTemplateSelector ViewModelTemplateSelector => viewModelTemplateSelector;
} }
@@ -12,14 +12,14 @@ public static class IServiceCollectionExtensions
// We need to feed information to the Widgets about our Windows host, // We need to feed information to the Widgets about our Windows host,
// so the Windows host can make discussions how to display and interact with the widgets. // so the Windows host can make discussions how to display and interact with the widgets.
services.AddTransient<IProxyServiceCollection<IWidgetBuilder>>(provider => services.AddTransient((Func<IServiceProvider, IProxyServiceCollection<IWidgetBuilder>>)(provider =>
new ProxyServiceCollection<IWidgetBuilder>(services => new ProxyServiceCollection<IWidgetBuilder>(services =>
{ {
services.AddSingleton(provider.GetRequiredService<IList<IXamlMetadataProvider>>()); services.AddSingleton(provider.GetRequiredService<IList<IXamlMetadataProvider>>());
services.AddSingleton(provider.GetRequiredService<IDispatcher>()); services.AddSingleton(provider.GetRequiredService<IDispatcher>());
services.AddTransient<IViewModelTemplate, ViewModelTemplate>(); services.AddTransient<IViewModelTemplateSelector, UI.Windows.ViewModelTemplateSelector>();
services.AddTransient<IViewModelTemplateDescriptorProvider, ViewModelTemplateDescriptorProvider>(); services.AddTransient<IViewModelTemplateProvider, ViewModelTemplateProvider>();
services.AddScoped<IVirtualKeyboard, VirtualKeyboard>(); services.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
services.AddHandler<KeyAcceleratorHandler>(); services.AddHandler<KeyAcceleratorHandler>();
@@ -33,7 +33,7 @@ public static class IServiceCollectionExtensions
services.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>(); services.AddContentTemplate<WidgetButtonViewModel, WidgetButtonView>();
services.AddContentTemplate<WidgetSplitButtonViewModel, WidgetSplitButtonView>(); services.AddContentTemplate<WidgetSplitButtonViewModel, WidgetSplitButtonView>();
})); })));
return services; return services;
} }
+2 -3
View File
@@ -4,10 +4,9 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core" xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity">
xmlns:ui="using:Hyperbar.UI.Windows">
<Grid> <Grid>
<ItemsControl ItemTemplateSelector="{Binding Template}" ItemsSource="{Binding Mode=TwoWay}"> <ItemsControl ItemTemplateSelector="{Binding ViewModelTemplateSelector}" ItemsSource="{Binding Mode=TwoWay}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="8" /> <StackPanel Orientation="Horizontal" Spacing="8" />
@@ -33,7 +33,7 @@ public static class IServiceCollectionExtensions
services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType); services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType);
services.TryAddKeyedTransient(key, (provider, key) => provider.GetService<IWidgetView>()!); services.TryAddKeyedTransient(key, (provider, key) => provider.GetService<IWidgetView>()!);
services.AddTransient<IViewModelTemplateDescriptor>(provider => new ViewModelTemplateDescriptor { ViewModelType = contentType, TemplateType = templateType, Key = key }); services.AddTransient<IViewModelTemplate>(provider => new ViewModelTemplate { ViewModelType = contentType, TemplateType = templateType, Key = key });
return services; return services;
} }
@@ -54,8 +54,8 @@ public static class IServiceCollectionExtensions
services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType); services.AddKeyedTransient(typeof(IWidgetViewModel), key, contentType);
services.TryAddKeyedTransient(templateType, key); services.TryAddKeyedTransient(templateType, key);
services.AddTransient<IViewModelTemplateDescriptor>(provider => services.AddTransient<IViewModelTemplate>(provider =>
new ViewModelTemplateDescriptor { ViewModelType = contentType, new ViewModelTemplate { ViewModelType = contentType,
TemplateType = templateType, Key = key }); TemplateType = templateType, Key = key });
return services; return services;
+8
View File
@@ -29,8 +29,16 @@ public class WidgetBuilder :
services.AddScoped<IServiceFactory>(provider => services.AddScoped<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => new ServiceFactory((type, parameters) =>
ActivatorUtilities.CreateInstance(provider, type, parameters!))); ActivatorUtilities.CreateInstance(provider, type, parameters!)));
services.AddSingleton<SubscriptionCollection>();
services.AddSingleton<ISubscriptionManager, SubscriptionManager>();
services.AddTransient<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>();
services.AddScoped<IMediator, Mediator>(); services.AddScoped<IMediator, Mediator>();
services.AddScoped<IDisposer, Disposer>(); services.AddScoped<IDisposer, Disposer>();
services.AddHandler<WidgetAvailabilityChangedHandler>(); services.AddHandler<WidgetAvailabilityChangedHandler>();
services.AddValueChangedNotification<WidgetConfiguration, services.AddValueChangedNotification<WidgetConfiguration,
WidgetAvailability>((config) => (args) => WidgetAvailability>((config) => (args) =>
+12 -5
View File
@@ -21,7 +21,7 @@ public partial class App :
base.OnLaunched(args); base.OnLaunched(args);
IHost? host = new HostBuilder() IHost? host = new HostBuilder()
.UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), .UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Assembly.GetEntryAssembly()?.GetName().Name!), true) Assembly.GetEntryAssembly()?.GetName().Name!), true)
.ConfigureAppConfiguration(config => .ConfigureAppConfiguration(config =>
{ {
@@ -36,18 +36,24 @@ public partial class App :
services.AddHostedService<AppService>(); services.AddHostedService<AppService>();
services.AddSingleton<IDispatcher>(new Dispatcher(DispatcherQueue.GetForCurrentThread())); services.AddSingleton((IDispatcher)new Dispatcher(DispatcherQueue.GetForCurrentThread()));
services.AddTransient<IViewModelTemplate, ViewModelTemplate>(); services.AddTransient<INavigationProvider, NavigationProvider>();
services.AddTransient<IViewModelTemplateDescriptorProvider, ViewModelTemplateDescriptorProvider>(); services.AddSingleton<NavigationTargetCollection>();
services.AddTransient<INavigationTargetProvider, NavigationTargetProvider>();
services.AddTransient<IViewModelContentBinder, ViewModelContentBinder>();
services.AddTransient<IViewModelTemplateSelector, ViewModelTemplateSelector>();
services.AddTransient<IViewModelTemplateProvider, ViewModelTemplateProvider>();
services.AddHandler<AppConfigurationChangedHandler>(); services.AddHandler<AppConfigurationChangedHandler>();
services.AddConfiguration<AppConfiguration>(args => services.AddConfiguration((AppConfiguration args) =>
{ {
args.Placement = DesktopApplicationBarPlacemenet.Top; args.Placement = DesktopApplicationBarPlacemenet.Top;
}); });
services.AddNavigationHandler<WindowHandler>(); services.AddNavigationHandler<WindowHandler>();
services.AddNavigationHandler<ContentControlHandler>();
services.AddSingleton<DesktopApplicationBar>(); services.AddSingleton<DesktopApplicationBar>();
services.AddContentTemplate<ApplicationBarViewModel, ApplicationBarView>(); services.AddContentTemplate<ApplicationBarViewModel, ApplicationBarView>();
@@ -59,6 +65,7 @@ public partial class App :
services.AddContentTemplate<GeneralSettingsNavigationViewModel, GeneralSettingsNavigationView>(); services.AddContentTemplate<GeneralSettingsNavigationViewModel, GeneralSettingsNavigationView>();
services.AddContentTemplate<WidgetSettingsNavigationViewModel, WidgetSettingsNavigationView>(); services.AddContentTemplate<WidgetSettingsNavigationViewModel, WidgetSettingsNavigationView>();
services.AddContentTemplate<WidgetSettingsViewModel, WidgetSettingsView>("WidgetSettings");
services.AddTransient<IInitializer, AppInitializer>(); services.AddTransient<IInitializer, AppInitializer>();
}) })
+1 -1
View File
@@ -17,7 +17,7 @@
</UserControl.Resources> </UserControl.Resources>
<ItemsControl <ItemsControl
Margin="6,0,6,0" Margin="6,0,6,0"
ItemTemplateSelector="{Binding Template}" ItemTemplateSelector="{Binding ViewModelTemplateSelector}"
ItemsSource="{x:Bind ViewModel, Mode=OneWay}"> ItemsSource="{x:Bind ViewModel, Mode=OneWay}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
+3 -3
View File
@@ -7,18 +7,18 @@ namespace Hyperbar.Widget;
public partial class ApplicationBarViewModel : public partial class ApplicationBarViewModel :
ObservableCollectionViewModel<IDisposable> ObservableCollectionViewModel<IDisposable>
{ {
public ApplicationBarViewModel(IViewModelTemplate template, public ApplicationBarViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{ {
Template = template; ViewModelTemplateSelector = viewModelTemplateSelector;
Add<PrimaryViewModel>(0); Add<PrimaryViewModel>(0);
Add<SecondaryViewModel>(1); Add<SecondaryViewModel>(1);
} }
public IViewModelTemplate Template { get; } public IViewModelTemplateSelector ViewModelTemplateSelector { get; }
} }
@@ -6,6 +6,4 @@ public class GeneralSettingsNavigationViewModel(IServiceProvider serviceProvider
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
string text) : string text) :
NavigationViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer, text) NavigationViewModel(serviceProvider, serviceFactory, publisher, subscriber, disposer, text);
{
}
+6
View File
@@ -21,6 +21,7 @@
<None Remove="SettingsButtonView.xaml" /> <None Remove="SettingsButtonView.xaml" />
<None Remove="SettingsView.xaml" /> <None Remove="SettingsView.xaml" />
<None Remove="WidgetSettingsNavigationView.xaml" /> <None Remove="WidgetSettingsNavigationView.xaml" />
<None Remove="WidgetSettingsView.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\SplashScreen.scale-200.png" />
@@ -57,6 +58,11 @@
<ProjectReference Include="..\Hyperbar.Widget\Hyperbar.Widget.csproj" /> <ProjectReference Include="..\Hyperbar.Widget\Hyperbar.Widget.csproj" />
<ProjectReference Include="..\Hyperbar\Hyperbar.csproj" /> <ProjectReference Include="..\Hyperbar\Hyperbar.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="WidgetSettingsView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="GeneralSettingsNavigationView.xaml"> <Page Update="GeneralSettingsNavigationView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
+2 -3
View File
@@ -2,11 +2,10 @@
<UserControl <UserControl
x:Class="Hyperbar.Windows.PrimaryView" x:Class="Hyperbar.Windows.PrimaryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:ui="using:Hyperbar.UI.Windows">
<ItemsControl <ItemsControl
HorizontalAlignment="Center" HorizontalAlignment="Center"
ItemTemplateSelector="{Binding Template}" ItemTemplateSelector="{Binding ViewModelTemplateSelector}"
ItemsSource="{x:Bind ViewModel}"> ItemsSource="{x:Bind ViewModel}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
+2 -2
View File
@@ -4,7 +4,7 @@ using Hyperbar.UI.Windows;
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
[NotificationHandler(nameof(IWidgetHostViewModel))] [NotificationHandler(nameof(IWidgetHostViewModel))]
public partial class PrimaryViewModel(IViewModelTemplate template, public partial class PrimaryViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
@@ -17,6 +17,6 @@ public partial class PrimaryViewModel(IViewModelTemplate template,
[ObservableProperty] [ObservableProperty]
private int index = index; private int index = index;
public IViewModelTemplate Template => template; public IViewModelTemplateSelector ViewModelTemplateSelector => viewModelTemplateSelector;
} }
+2 -3
View File
@@ -2,10 +2,9 @@
<UserControl <UserControl
x:Class="Hyperbar.Windows.SecondaryView" x:Class="Hyperbar.Windows.SecondaryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:ui="using:Hyperbar.UI.Windows">
<Grid> <Grid>
<ItemsControl ItemTemplateSelector="{Binding Template}" ItemsSource="{x:Bind ViewModel, Mode=OneWay}"> <ItemsControl ItemTemplateSelector="{Binding ViewModelTemplateSelector}" ItemsSource="{x:Bind ViewModel, Mode=OneWay}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" /> <ItemsStackPanel Orientation="Horizontal" />
+3 -3
View File
@@ -10,7 +10,7 @@ public partial class SecondaryViewModel :
[ObservableProperty] [ObservableProperty]
private int index; private int index;
public SecondaryViewModel(IViewModelTemplate template, public SecondaryViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
@@ -18,12 +18,12 @@ public partial class SecondaryViewModel :
IDisposer disposer, IDisposer disposer,
int index) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer) int index) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{ {
Template = template; ViewModelTemplateSelector = viewModelTemplateSelector;
this.index = index; this.index = index;
Add<SettingsButtonViewModel>(); Add<SettingsButtonViewModel>();
} }
public IViewModelTemplate Template { get; } public IViewModelTemplateSelector ViewModelTemplateSelector { get; }
} }
+1 -1
View File
@@ -26,7 +26,7 @@
FontSize="16"> FontSize="16">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<interactions:EventTriggerBehavior EventName="Click"> <interactions:EventTriggerBehavior EventName="Click">
<windows:NavigateAction Path="Settings" /> <windows:NavigateAction Name="Settings" />
</interactions:EventTriggerBehavior> </interactions:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</Button> </Button>
+2 -2
View File
@@ -2,12 +2,12 @@
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
public partial class SettingsButtonViewModel(IViewModelTemplate template, public partial class SettingsButtonViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
IDisposer disposer) : IDisposer disposer) :
ObservableViewModel(serviceProvider, serviceFactory, publisher, disposer) ObservableViewModel(serviceProvider, serviceFactory, publisher, disposer)
{ {
public IViewModelTemplate Template => template; public IViewModelTemplateSelector ViewModelTemplateSelector => viewModelTemplateSelector;
} }
+2 -1
View File
@@ -7,9 +7,10 @@
<MicaBackdrop /> <MicaBackdrop />
</Window.SystemBackdrop> </Window.SystemBackdrop>
<NavigationView <NavigationView
x:Name="Settings"
IsBackButtonVisible="Collapsed" IsBackButtonVisible="Collapsed"
IsPaneToggleButtonVisible="False" IsPaneToggleButtonVisible="False"
IsSettingsVisible="False" IsSettingsVisible="False"
MenuItemTemplateSelector="{Binding Template}" MenuItemTemplateSelector="{Binding ViewModelTemplateSelector}"
MenuItemsSource="{x:Bind ViewModel, Mode=OneWay}" /> MenuItemsSource="{x:Bind ViewModel, Mode=OneWay}" />
</Window> </Window>
+1
View File
@@ -4,6 +4,7 @@ using Microsoft.UI.Xaml;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
[NavigationTarget("Settings")]
public partial class SettingsView : public partial class SettingsView :
Window Window
{ {
+3 -3
View File
@@ -5,18 +5,18 @@ namespace Hyperbar.Windows;
public partial class SettingsViewModel : public partial class SettingsViewModel :
ObservableCollectionViewModel<INavigationViewModel> ObservableCollectionViewModel<INavigationViewModel>
{ {
public SettingsViewModel(IViewModelTemplate template, public SettingsViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IPublisher publisher, IPublisher publisher,
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{ {
Template = template; ViewModelTemplateSelector = viewModelTemplateSelector;
Add<GeneralSettingsNavigationViewModel>("General"); Add<GeneralSettingsNavigationViewModel>("General");
Add<WidgetSettingsNavigationViewModel>("Widgets"); Add<WidgetSettingsNavigationViewModel>("Widgets");
} }
public IViewModelTemplate Template { get; } public IViewModelTemplateSelector ViewModelTemplateSelector { get; }
} }
@@ -9,7 +9,7 @@
Content="Widgets"> Content="Widgets">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<interactions:EventTriggerBehavior EventName="Tapped"> <interactions:EventTriggerBehavior EventName="Tapped">
<ui:NavigateAction /> <ui:NavigateAction Name="WidgetSettings" TargetName="Settings" />
</interactions:EventTriggerBehavior> </interactions:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</NavigationViewItem> </NavigationViewItem>
@@ -6,7 +6,4 @@ public class WidgetSettingsNavigationViewModel(IServiceProvider serviceProvider,
ISubscriber subscriber, ISubscriber subscriber,
IDisposer disposer, IDisposer disposer,
string text) : string text) :
NavigationViewModel<WidgetNavigationViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, text) NavigationViewModel<WidgetNavigationViewModel>(serviceProvider, serviceFactory, publisher, subscriber, disposer, text);
{
}
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="Hyperbar.Windows.WidgetSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Button>Test</Button>
</Grid>
</UserControl>
@@ -0,0 +1,12 @@
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows;
public partial class WidgetSettingsView : UserControl
{
public WidgetSettingsView() =>
InitializeComponent();
protected WidgetSettingsViewModel ViewModel =>
(WidgetSettingsViewModel)DataContext;
}
@@ -0,0 +1,19 @@
using Hyperbar.UI.Windows;
namespace Hyperbar.Windows;
public class WidgetSettingsViewModel :
ObservableCollectionViewModel<INavigationViewModel>
{
public WidgetSettingsViewModel(IViewModelTemplateSelector viewModelTemplateSelector,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IPublisher publisher,
ISubscriber subscriber,
IDisposer disposer) : base(serviceProvider, serviceFactory, publisher, subscriber, disposer)
{
ViewModelTemplateSelector = viewModelTemplateSelector;
}
public IViewModelTemplateSelector ViewModelTemplateSelector { get; }
}
@@ -1,6 +1,6 @@
namespace Hyperbar; namespace Hyperbar;
public interface INavigationDescriptor public interface INavigation
{ {
Type Type { get; set; } Type Type { get; set; }
} }
+6
View File
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface INavigationProvider
{
INavigation? Get(Type type);
}
+6
View File
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface INavigationTargetProvider
{
object? Get(string name);
}
@@ -3,8 +3,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical; using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using System.Diagnostics.CodeAnalysis;
using System.Net.Mime;
using System.Text.Json; using System.Text.Json;
namespace Hyperbar; namespace Hyperbar;
@@ -140,7 +138,7 @@ public static class IServiceCollectionExtensions
if (contract?.GetGenericArguments() is { Length: 1 } arguments) if (contract?.GetGenericArguments() is { Length: 1 } arguments)
{ {
services.AddTransient<INavigationDescriptor>(provider => new NavigationDescriptor services.AddTransient<INavigation>(provider => new Navigation
{ {
Type = arguments[0] Type = arguments[0]
}); });
@@ -164,7 +162,7 @@ public static class IServiceCollectionExtensions
services.AddKeyedTransient(contentType, key); services.AddKeyedTransient(contentType, key);
services.AddKeyedTransient(templateType, key); services.AddKeyedTransient(templateType, key);
services.AddTransient<IViewModelTemplateDescriptor>(provider => new ViewModelTemplateDescriptor services.AddTransient<IViewModelTemplate>(provider => new ViewModelTemplate
{ {
ViewModelType = contentType, ViewModelType = contentType,
TemplateType = templateType, TemplateType = templateType,
@@ -179,7 +177,13 @@ public static class IServiceCollectionExtensions
services.AddSingleton<IServiceFactory>(provider => services.AddSingleton<IServiceFactory>(provider =>
new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!))); new ServiceFactory((type, parameters) => ActivatorUtilities.CreateInstance(provider, type, parameters!)));
services.AddSingleton<SubscriptionCollection>();
services.AddSingleton<ISubscriptionManager, SubscriptionManager>();
services.AddTransient<ISubscriber, Subscriber>();
services.AddTransient<IPublisher, Publisher>();
services.AddSingleton<IMediator, Mediator>(); services.AddSingleton<IMediator, Mediator>();
services.AddSingleton<IProxyService<IMediator>>(provider => services.AddSingleton<IProxyService<IMediator>>(provider =>
new ProxyService<IMediator>(provider.GetRequiredService<IMediator>())); new ProxyService<IMediator>(provider.GetRequiredService<IMediator>()));
@@ -1,6 +1,6 @@
namespace Hyperbar; namespace Hyperbar;
public interface IViewModelTemplateDescriptor public interface IViewModelTemplate
{ {
object Key { get; set; } object Key { get; set; }
+6
View File
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface IViewModelTemplateProvider
{
IViewModelTemplate? Get(object key);
}
-8
View File
@@ -1,8 +0,0 @@
namespace Hyperbar;
public record Navigate(object Key) :
INotification;
public record Navigate<TView>(TView View, object ViewModel) :
INotification;
-54
View File
@@ -1,54 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar;
public class NavigateHandler :
INotificationHandler<Navigate>
{
private readonly IViewModelTemplateDescriptorProvider contentTemplateDescriptors;
private readonly IServiceProvider provider;
private readonly IPublisher publisher;
private readonly IEnumerable<INavigationDescriptor> navigationDescriptors;
public NavigateHandler(IServiceProvider provider,
IPublisher publisher,
IEnumerable<INavigationDescriptor> navigationDescriptors,
IViewModelTemplateDescriptorProvider contentTemplateDescriptors)
{
this.provider = provider;
this.publisher = publisher;
this.navigationDescriptors = navigationDescriptors;
this.contentTemplateDescriptors = contentTemplateDescriptors;
}
public async Task Handle(Navigate args,
CancellationToken cancellationToken)
{
if (contentTemplateDescriptors.Get(args.Key)
is IViewModelTemplateDescriptor contentTemplateDescriptor)
{
if (navigationDescriptors.FirstOrDefault(x => contentTemplateDescriptor.TemplateType == x.Type ||
contentTemplateDescriptor.TemplateType.BaseType == x.Type) is { } navigationDescriptor)
{
if (contentTemplateDescriptor.TemplateType == navigationDescriptor.Type ||
contentTemplateDescriptor.TemplateType.BaseType == navigationDescriptor.Type)
{
if (provider.GetRequiredKeyedService(contentTemplateDescriptor.TemplateType,
contentTemplateDescriptor.Key) is { } template &&
provider.GetRequiredKeyedService(contentTemplateDescriptor.ViewModelType,
contentTemplateDescriptor.Key) is { } content)
{
Type navigateType = typeof(Navigate<>)
.MakeGenericType(navigationDescriptor.Type);
if (Activator.CreateInstance(navigateType,
new object[] { template, content }) is object navigate)
{
await publisher.PublishAsync(navigate, cancellationToken);
}
}
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More