Bunch of insane work

This commit is contained in:
TheXamlGuy
2024-02-08 22:16:58 +00:00
parent 28d79f77d0
commit 2aeb4d1b54
58 changed files with 547 additions and 326 deletions
@@ -1,15 +0,0 @@
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.UI.Windows;
public class DataTemplateConverter :
ValueConverter<object, DataTemplateSelector>
{
protected override DataTemplateSelector? ConvertTo(object value,
Type? targetType,
object? parameter,
string? language)
{
return new TemplateGenerator();
}
}
-23
View File
@@ -1,23 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar.UI.Windows;
public class TemplateFactory(IEnumerable<IContentTemplateDescriptor> descriptors,
IServiceProvider provider) :
ITemplateFactory
{
public object? Create(object key)
{
if (descriptors.FirstOrDefault(x => x.Key == key)
is IContentTemplateDescriptor descriptor)
{
if (provider.GetRequiredKeyedService(descriptor.TemplateType,
descriptor.Key) is { } template)
{
return template;
}
}
return default;
}
}
-36
View File
@@ -1,36 +0,0 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
namespace Hyperbar.UI.Windows;
public class TemplateGenerator : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item)
{
string xamlString = @"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:ui=""using:Hyperbar.UI.Windows"">
<ui:TemplateGeneratorControl VerticalContentAlignment=""Stretch""
HorizontalContentAlignment=""Stretch""
HorizontalAlignment=""Stretch""
VerticalAlignment=""Stretch""/>
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
string xamlString = @"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:ui=""using:Hyperbar.UI.Windows"">
<ui:TemplateGeneratorControl VerticalContentAlignment=""Stretch""
HorizontalContentAlignment=""Stretch""
HorizontalAlignment=""Stretch""
VerticalAlignment=""Stretch""/>
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
}
@@ -1,22 +0,0 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.UI.Windows;
public class TemplateGeneratorControl :
ContentControl
{
public TemplateGeneratorControl()
{
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(FrameworkElement sender,
DataContextChangedEventArgs args)
{
if (DataContext is ITemplatedViewModel templatedViewModel)
{
Content = templatedViewModel.TemplateFactory.Create(DataContext.GetType().Name);
}
}
}
@@ -0,0 +1,23 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.UI.Windows;
public class ViewModelTemplatePresenter :
ContentPresenter
{
public ViewModelTemplatePresenter()
{
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(FrameworkElement sender,
DataContextChangedEventArgs args)
{
//if (DataContext is IViewModelTemplate templatedViewModel)
//{
// Content = templatedViewModel.TemplateFactory
// .Create(DataContext.GetType().Name);
//}
}
}
@@ -0,0 +1,92 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
namespace Hyperbar.UI.Windows;
public class ImplicitTemplate :
MarkupExtension
{
protected override object ProvideValue(IXamlServiceProvider serviceProvider) =>
new ImplicitTemplateSelector();
internal class ImplicitTemplateSelector :
DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item,
DependencyObject container)
{
string xamlString = @"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:ui=""using:Hyperbar.UI.Windows"">
<ui:ViewModelTemplatePresenter VerticalContentAlignment=""Stretch""
HorizontalContentAlignment=""Stretch""
HorizontalAlignment=""Stretch""
VerticalAlignment=""Stretch""/>
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
}
}
public class ViewModelTemplate :
MarkupExtension
{
protected override object ProvideValue(IXamlServiceProvider serviceProvider) =>
new ViewModelTemplateSelector();
internal class ViewModelTemplateSelector :
DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is IObservableViewModel observableViewModel)
{
if (observableViewModel.ServiceProvider.GetService<IViewModelTemplateDescriptorProvider>()
is ViewModelTemplateDescriptorProvider descriptors)
{
if (descriptors.Get(item.GetType().Name) is IViewModelTemplateDescriptor descriptor)
{
string xamlString = @$"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:ui=""using:{descriptor.TemplateType.Namespace}"">
<ui:{descriptor.TemplateType.Name} />
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
}
}
return new DataTemplate();
}
protected override DataTemplate SelectTemplateCore(object item,
DependencyObject container)
{
if (item is IObservableViewModel observableViewModel)
{
if (observableViewModel.ServiceProvider.GetService<IViewModelTemplateDescriptorProvider>()
is ViewModelTemplateDescriptorProvider descriptors)
{
if (descriptors.Get(item.GetType().Name) is IViewModelTemplateDescriptor descriptor)
{
string xamlString = @$"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:ui=""using:{descriptor.TemplateType.Namespace}"">
<ui:{descriptor.TemplateType.Name} />
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
}
}
return new DataTemplate();
}
}
}
+16
View File
@@ -1,11 +1,17 @@
using Hyperbar.Interop.Windows; using Hyperbar.Interop.Windows;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Graphics; using Windows.Graphics;
using WinRT.Interop; using WinRT.Interop;
namespace Hyperbar.UI.Windows; namespace Hyperbar.UI.Windows;
public class NavigationItemTemplateSelector :
DataTemplateSelector
{
}
public static class WindowExtensions public static class WindowExtensions
{ {
public static WindowMessageListener CreateMessageListener(this Window window) => public static WindowMessageListener CreateMessageListener(this Window window) =>
@@ -36,6 +42,16 @@ public static class WindowExtensions
public static void SetStyle(this Window window, public static void SetStyle(this Window window,
ExtendedWindowStyle style) => window.GetHandle().SetExtendedWindowStyle(style); ExtendedWindowStyle style) => window.GetHandle().SetExtendedWindowStyle(style);
public static void TitleBarConfiguration(this Window window,
Action<AppWindowTitleBar> titleBarDelegate)
{
AppWindow appWindow = window.AppWindow;
if (appWindow.TitleBar is AppWindowTitleBar titleBar)
{
titleBarDelegate.Invoke(titleBar);
}
}
public static void SetTopMost(this Window window, public static void SetTopMost(this Window window,
bool value) bool value)
{ {
@@ -1,13 +1,12 @@
namespace Hyperbar.Widget.Contextual.Windows; namespace Hyperbar.Widget.Contextual.Windows;
public class ContextualWidgetViewModel(ITemplateFactory templateFactory, public class ContextualWidgetViewModel(IViewModelTemplateFactory templateFactory,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
IEnumerable<IWidgetComponentViewModel> items) : IEnumerable<IWidgetComponentViewModel> items) :
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, items), ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer, items),
IWidgetViewModel, IWidgetViewModel
ITemplatedViewModel
{ {
public ITemplateFactory TemplateFactory => templateFactory; public IViewModelTemplateFactory TemplateFactory => templateFactory;
} }
@@ -3,12 +3,13 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public partial class MediaButtonViewModel<TMediaButton>(IServiceFactory serviceFactory, public partial class MediaButtonViewModel<TMediaButton>(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, IViewModelTemplateFactory templateFactory,
IRelayCommand invokeCommand) : IRelayCommand invokeCommand) :
WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory), WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, templateFactory),
INotificationHandler<Changed<MediaButton<TMediaButton>>>, INotificationHandler<Changed<MediaButton<TMediaButton>>>,
IMediaButtonViewModel IMediaButtonViewModel
{ {
@@ -3,18 +3,16 @@
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<Grid> <ItemsControl
<ItemsControl HorizontalAlignment="Right"
HorizontalAlignment="Right" HorizontalContentAlignment="Right"
HorizontalContentAlignment="Right" ItemTemplateSelector="{ui:ViewModelTemplate}"
ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemsSource="{x:Bind ViewModel}">
ItemsSource="{x:Bind ViewModel}"> <ItemsControl.ItemsPanel>
<ItemsControl.ItemsPanel> <ItemsPanelTemplate>
<ItemsPanelTemplate> <StackPanel Orientation="Horizontal" Spacing="8" />
<StackPanel Orientation="Horizontal" Spacing="8" /> </ItemsPanelTemplate>
</ItemsPanelTemplate> </ItemsControl.ItemsPanel>
</ItemsControl.ItemsPanel> </ItemsControl>
</ItemsControl>
</Grid>
</UserControl> </UserControl>
@@ -2,17 +2,15 @@
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
[NotificationHandler(nameof(MediaControllerViewModel))] [NotificationHandler(nameof(MediaControllerViewModel))]
public class MediaControllerViewModel : public class MediaControllerViewModel :
ObservableCollectionViewModel<WidgetComponentViewModel>, ObservableCollectionViewModel<WidgetComponentViewModel>
ITemplatedViewModel
{ {
public MediaControllerViewModel(ITemplateFactory templateFactory, public MediaControllerViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : base(serviceFactory, mediator, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
@@ -28,5 +26,5 @@ public class MediaControllerViewModel :
await mediator.PublishAsync<Request<MediaNext>>())); await mediator.PublishAsync<Request<MediaNext>>()));
} }
public ITemplateFactory TemplateFactory { get; set; } public IViewModelTemplateFactory TemplateFactory { get; set; }
} }
@@ -3,9 +3,9 @@
x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerWidgetView" x:Class="Hyperbar.Widget.MediaController.Windows.MediaControllerWidgetView"
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<FlipView <FlipView
Width="360" Width="360"
ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemTemplateSelector="{ui:ViewModelTemplate}"
ItemsSource="{Binding}" /> ItemsSource="{Binding}" />
</UserControl> </UserControl>
@@ -1,15 +1,13 @@
using Hyperbar.Widget; namespace Hyperbar.Widget.MediaController.Windows;
namespace Hyperbar.Widget.MediaController.Windows; public class MediaControllerWidgetViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
public class MediaControllerWidgetViewModel(ITemplateFactory templateFactory,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
IEnumerable<MediaControllerViewModel> items) : IEnumerable<MediaControllerViewModel> items) :
ObservableCollectionViewModel<MediaControllerViewModel>(serviceFactory, mediator, disposer, items), ObservableCollectionViewModel<MediaControllerViewModel>(serviceProvider, serviceFactory, mediator, disposer, items),
IWidgetViewModel, IWidgetViewModel
ITemplatedViewModel
{ {
public ITemplateFactory TemplateFactory => templateFactory; public IViewModelTemplateFactory TemplateFactory => templateFactory;
} }
@@ -2,11 +2,12 @@
namespace Hyperbar.Widget.MediaController.Windows; namespace Hyperbar.Widget.MediaController.Windows;
public partial class MediaInformationViewModel(IServiceFactory serviceFactory, public partial class MediaInformationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory) : IViewModelTemplateFactory templateFactory) :
WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory), WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, templateFactory),
INotificationHandler<Changed<MediaInformation>> INotificationHandler<Changed<MediaInformation>>
{ {
[ObservableProperty] [ObservableProperty]
@@ -3,13 +3,13 @@
namespace Hyperbar.Widget.Primary.Windows; namespace Hyperbar.Widget.Primary.Windows;
[NotificationHandler(nameof(PrimaryWidgetViewModel))] [NotificationHandler(nameof(PrimaryWidgetViewModel))]
public class PrimaryWidgetViewModel(ITemplateFactory templateFactory, public class PrimaryWidgetViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : IDisposer disposer) :
ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceFactory, mediator, disposer), ObservableCollectionViewModel<IWidgetComponentViewModel>(serviceProvider, serviceFactory, mediator, disposer),
IWidgetViewModel, IWidgetViewModel
ITemplatedViewModel
{ {
public ITemplateFactory TemplateFactory => templateFactory; public IViewModelTemplateFactory TemplateFactory => templateFactory;
} }
@@ -1,5 +1,4 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Hyperbar.Widget;
namespace Hyperbar.Widget.Primary.Windows; namespace Hyperbar.Widget.Primary.Windows;
@@ -19,7 +19,8 @@ public static class IServiceCollectionExtensions
services.AddSingleton(provider.GetRequiredService<IList<IXamlMetadataProvider>>()); services.AddSingleton(provider.GetRequiredService<IList<IXamlMetadataProvider>>());
services.AddSingleton(provider.GetRequiredService<IDispatcher>()); services.AddSingleton(provider.GetRequiredService<IDispatcher>());
services.AddTransient<ITemplateFactory, TemplateFactory>(); services.AddTransient<IViewModelTemplateDescriptorProvider, ViewModelTemplateDescriptorProvider>();
services.AddTransient<IViewModelTemplateFactory, ViewModelTemplateFactory>();
services.AddScoped<IVirtualKeyboard, VirtualKeyboard>(); services.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
services.AddHandler<KeyAcceleratorHandler>(); services.AddHandler<KeyAcceleratorHandler>();
+2 -2
View File
@@ -5,9 +5,9 @@
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<Grid> <Grid>
<ItemsControl ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemsSource="{Binding Mode=TwoWay}"> <ItemsControl ItemTemplateSelector="{ui:ViewModelTemplate}" 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<IContentTemplateDescriptor>(provider => new ContentTemplateDescriptor { ContentType = contentType, TemplateType = templateType, Key = key }); services.AddTransient<IViewModelTemplateDescriptor>(provider => new ViewModelTemplateDescriptor { 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<IContentTemplateDescriptor>(provider => services.AddTransient<IViewModelTemplateDescriptor>(provider =>
new ContentTemplateDescriptor { ContentType = contentType, new ViewModelTemplateDescriptor { ViewModelType = contentType,
TemplateType = templateType, Key = key }); TemplateType = templateType, Key = key });
return services; return services;
+4 -3
View File
@@ -4,14 +4,15 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))] [NotificationHandler(nameof(Id))]
public partial class WidgetButtonViewModel(IServiceFactory serviceFactory, public partial class WidgetButtonViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, IViewModelTemplateFactory templateFactory,
Guid id, Guid id,
string? text = null, string? text = null,
string? icon = null, string? icon = null,
RelayCommand? invokeCommand = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory) RelayCommand? invokeCommand = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, templateFactory)
{ {
[ObservableProperty] [ObservableProperty]
private string? icon = icon; private string? icon = icon;
+9 -8
View File
@@ -2,25 +2,26 @@
public partial class WidgetComponentViewModel : public partial class WidgetComponentViewModel :
ObservableCollectionViewModel<IWidgetComponentViewModel>, ObservableCollectionViewModel<IWidgetComponentViewModel>,
IWidgetComponentViewModel, IWidgetComponentViewModel
ITemplatedViewModel
{ {
public WidgetComponentViewModel(IServiceFactory serviceFactory, public WidgetComponentViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, IViewModelTemplateFactory templateFactory,
IEnumerable<IWidgetComponentViewModel> items) : base(serviceFactory, mediator, disposer, items) IEnumerable<IWidgetComponentViewModel> items) : base(serviceProvider, serviceFactory, mediator, disposer, items)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
} }
public WidgetComponentViewModel(IServiceFactory serviceFactory, public WidgetComponentViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory) : base(serviceFactory, mediator, disposer) IViewModelTemplateFactory templateFactory) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
} }
public ITemplateFactory TemplateFactory { get; private set; } public IViewModelTemplateFactory TemplateFactory { get; private set; }
} }
+4 -3
View File
@@ -4,14 +4,15 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))] [NotificationHandler(nameof(Id))]
public partial class WidgetMenuViewModel(IServiceFactory serviceFactory, public partial class WidgetMenuViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, IViewModelTemplateFactory templateFactory,
Guid id = default, Guid id = default,
string? text = null, string? text = null,
string? icon = null, string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory) RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, templateFactory)
{ {
[ObservableProperty] [ObservableProperty]
private IRelayCommand? click = command; private IRelayCommand? click = command;
@@ -4,15 +4,16 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
[NotificationHandler(nameof(Id))] [NotificationHandler(nameof(Id))]
public partial class WidgetSplitButtonViewModel(IServiceFactory serviceFactory, public partial class WidgetSplitButtonViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
ITemplateFactory templateFactory, IViewModelTemplateFactory templateFactory,
IEnumerable<IWidgetComponentViewModel> items, IEnumerable<IWidgetComponentViewModel> items,
Guid id = default, Guid id = default,
string? text = null, string? text = null,
string? icon = null, string? icon = null,
RelayCommand? command = null) : WidgetComponentViewModel(serviceFactory, mediator, disposer, templateFactory, items) RelayCommand? command = null) : WidgetComponentViewModel(serviceProvider, serviceFactory, mediator, disposer, templateFactory, items)
{ {
[ObservableProperty] [ObservableProperty]
private IRelayCommand? click = command; private IRelayCommand? click = command;
+1 -1
View File
@@ -10,7 +10,7 @@
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="DefaultDataTemplate"> <DataTemplate x:Key="DefaultDataTemplate">
<ui:TemplateGeneratorControl /> <ui:ViewModelTemplatePresenter />
</DataTemplate> </DataTemplate>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
+6 -1
View File
@@ -37,7 +37,9 @@ 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<ITemplateFactory, TemplateFactory>();
services.AddTransient<IViewModelTemplateDescriptorProvider, ViewModelTemplateDescriptorProvider>();
services.AddTransient<IViewModelTemplateFactory, ViewModelTemplateFactory>();
services.AddHandler<AppConfigurationChangedHandler>(); services.AddHandler<AppConfigurationChangedHandler>();
services.AddConfiguration<AppConfiguration>(args => services.AddConfiguration<AppConfiguration>(args =>
@@ -55,6 +57,9 @@ public partial class App :
services.AddContentTemplate<SettingsButtonViewModel, SettingsButtonView>(); services.AddContentTemplate<SettingsButtonViewModel, SettingsButtonView>();
services.AddContentTemplate<SettingsViewModel, SettingsView>("Settings"); services.AddContentTemplate<SettingsViewModel, SettingsView>("Settings");
services.AddContentTemplate<GeneralSettingsNavigationViewModel, GeneralSettingsNavigationView>();
services.AddContentTemplate<WidgetSettingsNavigationViewModel, WidgetSettingsNavigationView>();
services.AddTransient<IInitializer, AppInitializer>(); services.AddTransient<IInitializer, AppInitializer>();
}) })
.Build(); .Build();
+3 -3
View File
@@ -3,7 +3,7 @@
x:Class="Hyperbar.Windows.ApplicationBarView" x:Class="Hyperbar.Windows.ApplicationBarView"
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<UserControl.Resources> <UserControl.Resources>
<SolidColorBrush x:Key="ButtonBackground" Color="Transparent" /> <SolidColorBrush x:Key="ButtonBackground" Color="Transparent" />
<SolidColorBrush x:Key="ButtonBorderBrush" Color="Transparent" /> <SolidColorBrush x:Key="ButtonBorderBrush" Color="Transparent" />
@@ -17,7 +17,7 @@
</UserControl.Resources> </UserControl.Resources>
<ItemsControl <ItemsControl
Margin="6,0,6,0" Margin="6,0,6,0"
ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemTemplateSelector="{ui:ViewModelTemplate}"
ItemsSource="{x:Bind ViewModel, Mode=OneWay}"> ItemsSource="{x:Bind ViewModel, Mode=OneWay}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@@ -36,7 +36,7 @@
</ItemsControl.ItemContainerTransitions> </ItemsControl.ItemContainerTransitions>
<ItemsControl.ItemContainerStyle> <ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter"> <Style TargetType="ContentPresenter">
<Setter Property="windows:GridExtension.GridColumnBindingPath" Value="Index" /> <Setter Property="ui:GridExtension.GridColumnBindingPath" Value="Index" />
</Style> </Style>
</ItemsControl.ItemContainerStyle> </ItemsControl.ItemContainerStyle>
</ItemsControl> </ItemsControl>
+5 -5
View File
@@ -2,13 +2,13 @@
[NotificationHandler(nameof(IWidgetHostViewModel))] [NotificationHandler(nameof(IWidgetHostViewModel))]
public partial class ApplicationBarViewModel : public partial class ApplicationBarViewModel :
ObservableCollectionViewModel<IDisposable>, ObservableCollectionViewModel<IDisposable>
ITemplatedViewModel
{ {
public ApplicationBarViewModel(ITemplateFactory templateFactory, public ApplicationBarViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : base(serviceFactory, mediator, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
@@ -16,5 +16,5 @@ public partial class ApplicationBarViewModel :
Add<SecondaryViewModel>(1); Add<SecondaryViewModel>(1);
} }
public ITemplateFactory TemplateFactory { get; } public IViewModelTemplateFactory TemplateFactory { get; }
} }
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<NavigationViewItem
x:Class="Hyperbar.Windows.GeneralSettingsNavigationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Content="General" />
@@ -0,0 +1,10 @@
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows;
public sealed partial class GeneralSettingsNavigationView :
NavigationViewItem
{
public GeneralSettingsNavigationView() =>
InitializeComponent();
}
@@ -0,0 +1,10 @@
namespace Hyperbar.Windows;
public class GeneralSettingsNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
string text) :
NavigationViewModel(serviceProvider, serviceFactory, mediator, disposer, text)
{
}
+12
View File
@@ -15,10 +15,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="ApplicationBarView.xaml" /> <None Remove="ApplicationBarView.xaml" />
<None Remove="GeneralSettingsNavigationView.xaml" />
<None Remove="PrimaryView.xaml" /> <None Remove="PrimaryView.xaml" />
<None Remove="SecondaryView.xaml" /> <None Remove="SecondaryView.xaml" />
<None Remove="SettingsButtonView.xaml" /> <None Remove="SettingsButtonView.xaml" />
<None Remove="SettingsView.xaml" /> <None Remove="SettingsView.xaml" />
<None Remove="WidgetSettingsNavigationView.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\SplashScreen.scale-200.png" />
@@ -55,6 +57,16 @@
<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="GeneralSettingsNavigationView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="WidgetSettingsNavigationView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="SettingsView.xaml"> <Page Update="SettingsView.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
+2 -2
View File
@@ -3,10 +3,10 @@
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<ItemsControl <ItemsControl
HorizontalAlignment="Center" HorizontalAlignment="Center"
ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemTemplateSelector="{ui:ViewModelTemplate}"
ItemsSource="{x:Bind ViewModel}"> ItemsSource="{x:Bind ViewModel}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
+5 -5
View File
@@ -3,17 +3,17 @@
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
[NotificationHandler(nameof(IWidgetHostViewModel))] [NotificationHandler(nameof(IWidgetHostViewModel))]
public partial class PrimaryViewModel(ITemplateFactory templateFactory, public partial class PrimaryViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
int index) : int index) :
ObservableCollectionViewModel<IWidgetViewModel>(serviceFactory, mediator, disposer), ObservableCollectionViewModel<IWidgetViewModel>(serviceProvider, serviceFactory, mediator, disposer),
IWidgetHostViewModel, IWidgetHostViewModel
ITemplatedViewModel
{ {
[ObservableProperty] [ObservableProperty]
private int index = index; private int index = index;
public ITemplateFactory TemplateFactory => templateFactory; public IViewModelTemplateFactory TemplateFactory => templateFactory;
} }
+2 -2
View File
@@ -3,9 +3,9 @@
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:windows="using:Hyperbar.UI.Windows"> xmlns:ui="using:Hyperbar.UI.Windows">
<Grid> <Grid>
<ItemsControl ItemTemplateSelector="{Binding Converter={windows:DataTemplateConverter}}" ItemsSource="{x:Bind ViewModel, Mode=OneWay}"> <ItemsControl ItemTemplateSelector="{ui:ViewModelTemplate}" ItemsSource="{x:Bind ViewModel, Mode=OneWay}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" /> <ItemsStackPanel Orientation="Horizontal" />
+5 -5
View File
@@ -4,17 +4,17 @@ using Hyperbar.Windows;
namespace Hyperbar.Widget; namespace Hyperbar.Widget;
public partial class SecondaryViewModel : public partial class SecondaryViewModel :
ObservableCollectionViewModel<IDisposable>, ObservableCollectionViewModel<IDisposable>
ITemplatedViewModel
{ {
[ObservableProperty] [ObservableProperty]
private int index; private int index;
public SecondaryViewModel(ITemplateFactory templateFactory, public SecondaryViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
int index) : base(serviceFactory, mediator, disposer) int index) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
this.index = index; this.index = index;
@@ -22,5 +22,5 @@ public partial class SecondaryViewModel :
Add<SettingsButtonViewModel>(); Add<SettingsButtonViewModel>();
} }
public ITemplateFactory TemplateFactory { get; } public IViewModelTemplateFactory TemplateFactory { get; }
} }
+5 -5
View File
@@ -4,21 +4,21 @@ using CommunityToolkit.Mvvm.Input;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
public partial class SettingsButtonViewModel : public partial class SettingsButtonViewModel :
ObservableViewModel, ObservableViewModel
ITemplatedViewModel
{ {
[ObservableProperty] [ObservableProperty]
private IRelayCommand? invokeCommand; private IRelayCommand? invokeCommand;
public SettingsButtonViewModel(ITemplateFactory templateFactory, public SettingsButtonViewModel(IViewModelTemplateFactory templateFactory,
IServiceProvider serviceProvider,
IServiceFactory serviceFactory, IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : base(serviceFactory, mediator, disposer) IDisposer disposer) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
TemplateFactory = templateFactory; TemplateFactory = templateFactory;
InvokeCommand = new AsyncRelayCommand(async () => InvokeCommand = new AsyncRelayCommand(async () =>
await mediator.PublishAsync(new Navigate("Settings"))); await mediator.PublishAsync(new Navigate("Settings")));
} }
public ITemplateFactory TemplateFactory { get; } public IViewModelTemplateFactory TemplateFactory { get; }
} }
+4 -8
View File
@@ -3,7 +3,8 @@
x:Class="Hyperbar.Windows.SettingsView" x:Class="Hyperbar.Windows.SettingsView"
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:hyperbar="using:Hyperbar"> xmlns:ui="using:Hyperbar.UI.Windows"
xmlns:windows="using:Hyperbar.Windows">
<Window.SystemBackdrop> <Window.SystemBackdrop>
<MicaBackdrop /> <MicaBackdrop />
</Window.SystemBackdrop> </Window.SystemBackdrop>
@@ -11,11 +12,6 @@
IsBackButtonVisible="Collapsed" IsBackButtonVisible="Collapsed"
IsPaneToggleButtonVisible="False" IsPaneToggleButtonVisible="False"
IsSettingsVisible="False" IsSettingsVisible="False"
MenuItemsSource="{x:Bind ViewModel, Mode=OneWay}"> MenuItemTemplateSelector="{ui:ViewModelTemplate}"
<NavigationView.MenuItemTemplate> MenuItemsSource="{x:Bind ViewModel, Mode=OneWay}" />
<DataTemplate x:DataType="hyperbar:NavigationViewModel">
<NavigationViewItem Content="{x:Bind Text, Mode=OneWay}" />
</DataTemplate>
</NavigationView.MenuItemTemplate>
</NavigationView>
</Window> </Window>
+12 -1
View File
@@ -1,3 +1,5 @@
using Hyperbar.UI.Windows;
using Microsoft.UI;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
@@ -5,9 +7,18 @@ namespace Hyperbar.Windows;
public partial class SettingsView : public partial class SettingsView :
Window Window
{ {
public SettingsView() => public SettingsView()
{
InitializeComponent(); InitializeComponent();
this.TitleBarConfiguration(args =>
{
args.ExtendsContentIntoTitleBar = true;
args.ButtonBackgroundColor = Colors.Transparent;
args.ButtonInactiveBackgroundColor = Colors.Transparent;
});
}
protected SettingsViewModel ViewModel => protected SettingsViewModel ViewModel =>
(SettingsViewModel)(Content as FrameworkElement)!.DataContext; (SettingsViewModel)(Content as FrameworkElement)!.DataContext;
} }
+11 -5
View File
@@ -1,13 +1,19 @@
namespace Hyperbar.Windows; namespace Hyperbar.Windows;
public partial class SettingsViewModel : public partial class SettingsViewModel :
ObservableCollectionViewModel<NavigationViewModel> ObservableCollectionViewModel<INavigationViewModel>
{ {
public SettingsViewModel(IServiceFactory serviceFactory, public SettingsViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : base(serviceFactory, mediator, disposer) IDisposer disposer,
IViewModelTemplateFactory templateFactory) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
Add<NavigationViewModel>("General"); Add<GeneralSettingsNavigationViewModel>("General");
Add<NavigationViewModel>("Widgets"); Add<WidgetSettingsNavigationViewModel>("Widgets");
TemplateFactory = templateFactory;
} }
public IViewModelTemplateFactory TemplateFactory { get; }
} }
@@ -0,0 +1,10 @@
namespace Hyperbar.Windows;
public class WidgetNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
string text) :
NavigationViewModel(serviceProvider, serviceFactory, mediator, disposer, text)
{
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<NavigationViewItem
x:Class="Hyperbar.Windows.WidgetSettingsNavigationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Content="Widgets" />
@@ -0,0 +1,10 @@
using Microsoft.UI.Xaml.Controls;
namespace Hyperbar.Windows;
public sealed partial class WidgetSettingsNavigationView :
NavigationViewItem
{
public WidgetSettingsNavigationView() =>
InitializeComponent();
}
@@ -0,0 +1,11 @@
namespace Hyperbar.Windows;
public class WidgetSettingsNavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
string text) :
NavigationViewModel<WidgetNavigationViewModel>(serviceProvider, serviceFactory, mediator, disposer, text)
{
}
@@ -164,9 +164,9 @@ public static class IServiceCollectionExtensions
services.AddKeyedTransient(contentType, key); services.AddKeyedTransient(contentType, key);
services.AddKeyedTransient(templateType, key); services.AddKeyedTransient(templateType, key);
services.AddTransient<IContentTemplateDescriptor>(provider => new ContentTemplateDescriptor services.AddTransient<IViewModelTemplateDescriptor>(provider => new ViewModelTemplateDescriptor
{ {
ContentType = contentType, ViewModelType = contentType,
TemplateType = templateType, TemplateType = templateType,
Key = key Key = key
}); });
@@ -0,0 +1,7 @@
namespace Hyperbar;
public interface INavigationViewModel :
IObservableViewModel
{
string Text { get; set; }
}
+11 -5
View File
@@ -1,8 +1,14 @@
using System.Windows.Input; namespace Hyperbar;
namespace Hyperbar; public interface IObservableViewModel :
IInitialization,
public interface IObservableViewModel IDisposable
{ {
ICommand InitializeCommand { get; } public IDisposer Disposer { get; }
public IMediator Mediator { get; }
public IServiceFactory ServiceFactory { get; }
public IServiceProvider ServiceProvider { get; }
} }
+4 -4
View File
@@ -5,7 +5,7 @@ namespace Hyperbar;
public class NavigateHandler : public class NavigateHandler :
INotificationHandler<Navigate> INotificationHandler<Navigate>
{ {
private readonly IEnumerable<IContentTemplateDescriptor> contentTemplateDescriptors; private readonly IEnumerable<IViewModelTemplateDescriptor> contentTemplateDescriptors;
private readonly IServiceProvider provider; private readonly IServiceProvider provider;
private readonly IMediator mediator; private readonly IMediator mediator;
private readonly IEnumerable<INavigationDescriptor> navigationDescriptors; private readonly IEnumerable<INavigationDescriptor> navigationDescriptors;
@@ -13,7 +13,7 @@ public class NavigateHandler :
public NavigateHandler(IServiceProvider provider, public NavigateHandler(IServiceProvider provider,
IMediator mediator, IMediator mediator,
IEnumerable<INavigationDescriptor> navigationDescriptors, IEnumerable<INavigationDescriptor> navigationDescriptors,
IEnumerable<IContentTemplateDescriptor> contentTemplateDescriptors) IEnumerable<IViewModelTemplateDescriptor> contentTemplateDescriptors)
{ {
this.provider = provider; this.provider = provider;
this.mediator = mediator; this.mediator = mediator;
@@ -25,7 +25,7 @@ public class NavigateHandler :
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (contentTemplateDescriptors.FirstOrDefault(x => x.Key == args.Key) if (contentTemplateDescriptors.FirstOrDefault(x => x.Key == args.Key)
is IContentTemplateDescriptor contentTemplateDescriptor) is IViewModelTemplateDescriptor contentTemplateDescriptor)
{ {
if (navigationDescriptors.FirstOrDefault(x => contentTemplateDescriptor.TemplateType == x.Type || if (navigationDescriptors.FirstOrDefault(x => contentTemplateDescriptor.TemplateType == x.Type ||
contentTemplateDescriptor.TemplateType.BaseType == x.Type) is { } navigationDescriptor) contentTemplateDescriptor.TemplateType.BaseType == x.Type) is { } navigationDescriptor)
@@ -35,7 +35,7 @@ public class NavigateHandler :
{ {
if (provider.GetRequiredKeyedService(contentTemplateDescriptor.TemplateType, if (provider.GetRequiredKeyedService(contentTemplateDescriptor.TemplateType,
contentTemplateDescriptor.Key) is { } template && contentTemplateDescriptor.Key) is { } template &&
provider.GetRequiredKeyedService(contentTemplateDescriptor.ContentType, provider.GetRequiredKeyedService(contentTemplateDescriptor.ViewModelType,
contentTemplateDescriptor.Key) is { } content) contentTemplateDescriptor.Key) is { } content)
{ {
Type navigateType = typeof(Navigate<>) Type navigateType = typeof(Navigate<>)
+19 -3
View File
@@ -3,16 +3,32 @@
namespace Hyperbar; namespace Hyperbar;
public partial class NavigationViewModel : public partial class NavigationViewModel :
ObservableCollectionViewModel<NavigationViewModel> ObservableCollectionViewModel<INavigationViewModel>,
INavigationViewModel
{ {
[ObservableProperty] [ObservableProperty]
private string? text; private string? text;
public NavigationViewModel(IServiceFactory serviceFactory, public NavigationViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
string text) : base(serviceFactory, mediator, disposer) string text) : base(serviceProvider, serviceFactory, mediator, disposer)
{ {
this.text = text; this.text = text;
} }
} }
public partial class NavigationViewModel<TNavigationViewModel>(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator,
IDisposer disposer,
string text) :
ObservableCollectionViewModel<TNavigationViewModel>(serviceProvider, serviceFactory, mediator, disposer),
INavigationViewModel
where TNavigationViewModel :
INavigationViewModel
{
[ObservableProperty]
private string? text = text;
}
@@ -8,31 +8,29 @@ using System.Windows.Input;
namespace Hyperbar; namespace Hyperbar;
public partial class ObservableCollectionViewModel<TItem> : public partial class ObservableCollectionViewModel<TViewModel> :
ObservableObject, ObservableObject,
IObservableCollectionViewModel<TItem>, IObservableCollectionViewModel<TViewModel>,
IList<TItem>, IList<TViewModel>,
IList, IList,
IReadOnlyList<TItem>, IReadOnlyList<TViewModel>,
INotifyCollectionChanged, INotifyCollectionChanged,
INotificationHandler<Remove<TItem>>, INotificationHandler<Remove<TViewModel>>,
INotificationHandler<Create<TItem>>, INotificationHandler<Create<TViewModel>>,
INotificationHandler<Insert<TItem>>, INotificationHandler<Insert<TViewModel>>,
INotificationHandler<Move<TItem>>, INotificationHandler<Move<TViewModel>>,
INotificationHandler<Replace<TItem>>, INotificationHandler<Replace<TViewModel>>
IDisposable,
IInitialization
where TItem :
IDisposable
{ {
private readonly ObservableCollection<TItem> collection = []; private readonly ObservableCollection<TViewModel> collection = [];
private bool isInitialized; private bool isInitialized;
public ObservableCollectionViewModel(IServiceFactory serviceFactory, public ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) IDisposer disposer)
{ {
ServiceProvider = serviceProvider;
ServiceFactory = serviceFactory; ServiceFactory = serviceFactory;
Mediator = mediator; Mediator = mediator;
Disposer = disposer; Disposer = disposer;
@@ -42,11 +40,13 @@ public partial class ObservableCollectionViewModel<TItem> :
collection.CollectionChanged += OnCollectionChanged; collection.CollectionChanged += OnCollectionChanged;
} }
public ObservableCollectionViewModel(IServiceFactory serviceFactory, public ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer, IDisposer disposer,
IEnumerable<TItem> items) IEnumerable<TViewModel> items)
{ {
ServiceProvider = serviceProvider;
ServiceFactory = serviceFactory; ServiceFactory = serviceFactory;
Mediator = mediator; Mediator = mediator;
Disposer = disposer; Disposer = disposer;
@@ -61,28 +61,28 @@ public partial class ObservableCollectionViewModel<TItem> :
public int Count => collection.Count; public int Count => collection.Count;
public IDisposer Disposer { get; private set; }
public ICommand InitializeCommand => public ICommand InitializeCommand =>
new AsyncRelayCommand(CoreInitializeAsync); new AsyncRelayCommand(CoreInitializeAsync);
bool IList.IsFixedSize => false; bool IList.IsFixedSize => false;
bool ICollection<TItem>.IsReadOnly => false; bool ICollection<TViewModel>.IsReadOnly => false;
bool IList.IsReadOnly => false; bool IList.IsReadOnly => false;
bool ICollection.IsSynchronized => false; bool ICollection.IsSynchronized => false;
public IMediator Mediator { get; private set; }
public IServiceFactory ServiceFactory { get; private set; }
object ICollection.SyncRoot => this; object ICollection.SyncRoot => this;
protected IDisposer Disposer { get; private set; } public IServiceProvider ServiceProvider { get; private set; }
protected IList<TItem> Items => collection; public TViewModel this[int index]
protected IMediator Mediator { get; private set; }
protected IServiceFactory ServiceFactory { get; private set; }
public TItem this[int index]
{ {
get => collection[index]; get => collection[index];
set => SetItem(index, value); set => SetItem(index, value);
@@ -93,11 +93,11 @@ public partial class ObservableCollectionViewModel<TItem> :
get => collection[index]; get => collection[index];
set set
{ {
TItem? item = default; TViewModel? item = default;
try try
{ {
item = (TItem)value!; item = (TViewModel)value!;
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
@@ -108,16 +108,16 @@ public partial class ObservableCollectionViewModel<TItem> :
} }
} }
public TItem Add() public TViewModel Add()
{ {
TItem? item = ServiceFactory.Create<TItem>(); TViewModel? item = ServiceFactory.Create<TViewModel>();
Add(item); Add(item);
return item; return item;
} }
public TItem Add<T>(params object?[] parameters) public TViewModel Add<T>(params object?[] parameters)
where T : TItem where T : TViewModel
{ {
T? item = ServiceFactory.Create<T>(parameters); T? item = ServiceFactory.Create<T>(parameters);
Add(item); Add(item);
@@ -125,9 +125,9 @@ public partial class ObservableCollectionViewModel<TItem> :
return item; return item;
} }
public TItem Add<T>() public TViewModel Add<T>()
where T : where T :
TItem TViewModel
{ {
T? item = ServiceFactory.Create<T>(); T? item = ServiceFactory.Create<T>();
Add(item); Add(item);
@@ -135,7 +135,7 @@ public partial class ObservableCollectionViewModel<TItem> :
return item; return item;
} }
public void Add(TItem item) public void Add(TViewModel item)
{ {
int index = collection.Count; int index = collection.Count;
InsertItem(index, item); InsertItem(index, item);
@@ -144,16 +144,16 @@ public partial class ObservableCollectionViewModel<TItem> :
public void Add(object item) public void Add(object item)
{ {
int index = collection.Count; int index = collection.Count;
InsertItem(index, (TItem)item); InsertItem(index, (TViewModel)item);
} }
int IList.Add(object? value) int IList.Add(object? value)
{ {
TItem? item = default; TViewModel? item = default;
try try
{ {
item = (TItem)value!; item = (TViewModel)value!;
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
@@ -164,9 +164,9 @@ public partial class ObservableCollectionViewModel<TItem> :
return Count - 1; return Count - 1;
} }
public void AddRange(IEnumerable<TItem> items) public void AddRange(IEnumerable<TViewModel> items)
{ {
foreach (TItem? item in items) foreach (TViewModel? item in items)
{ {
Add(item); Add(item);
} }
@@ -174,7 +174,7 @@ public partial class ObservableCollectionViewModel<TItem> :
public void Clear() public void Clear()
{ {
foreach (TItem item in collection) foreach (TViewModel item in collection)
{ {
Disposer.Dispose(item); Disposer.Dispose(item);
} }
@@ -182,17 +182,17 @@ public partial class ObservableCollectionViewModel<TItem> :
ClearItems(); ClearItems();
} }
public bool Contains(TItem item) => public bool Contains(TViewModel item) =>
collection.Contains(item); collection.Contains(item);
bool IList.Contains(object? value) => bool IList.Contains(object? value) =>
IsCompatibleObject(value) && Contains((TItem)value!); IsCompatibleObject(value) && Contains((TViewModel)value!);
public void CopyTo(TItem[] array, int index) => public void CopyTo(TViewModel[] array, int index) =>
collection.CopyTo(array, index); collection.CopyTo(array, index);
void ICollection.CopyTo(Array array, int index) => void ICollection.CopyTo(Array array, int index) =>
collection.CopyTo((TItem[])array, index); collection.CopyTo((TViewModel[])array, index);
public virtual void Dispose() public virtual void Dispose()
{ {
@@ -200,16 +200,16 @@ public partial class ObservableCollectionViewModel<TItem> :
Disposer.Dispose(this); Disposer.Dispose(this);
} }
public System.Collections.Generic.IEnumerator<TItem> GetEnumerator() => public System.Collections.Generic.IEnumerator<TViewModel> GetEnumerator() =>
collection.GetEnumerator(); collection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() =>
((IEnumerable)collection).GetEnumerator(); ((IEnumerable)collection).GetEnumerator();
public Task Handle(Remove<TItem> notification, public Task Handle(Remove<TViewModel> notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
foreach (TItem item in this.ToList()) foreach (TViewModel item in this.ToList())
{ {
if (notification.Value is not null && notification.Value.Equals(item)) if (notification.Value is not null && notification.Value.Equals(item))
{ {
@@ -220,10 +220,10 @@ public partial class ObservableCollectionViewModel<TItem> :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(Create<TItem> notification, public Task Handle(Create<TViewModel> notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (notification.Value is TItem item) if (notification.Value is TViewModel item)
{ {
Add(item); Add(item);
} }
@@ -231,10 +231,10 @@ public partial class ObservableCollectionViewModel<TItem> :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(Insert<TItem> notification, public Task Handle(Insert<TViewModel> notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (notification.Value is TItem item) if (notification.Value is TViewModel item)
{ {
Insert(notification.Index, item); Insert(notification.Index, item);
} }
@@ -242,10 +242,10 @@ public partial class ObservableCollectionViewModel<TItem> :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(Move<TItem> notification, public Task Handle(Move<TViewModel> notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (notification.Value is TItem item) if (notification.Value is TViewModel item)
{ {
Move(notification.Index, item); Move(notification.Index, item);
} }
@@ -253,10 +253,10 @@ public partial class ObservableCollectionViewModel<TItem> :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(Replace<TItem> notification, public Task Handle(Replace<TViewModel> notification,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (notification.Value is TItem item) if (notification.Value is TViewModel item)
{ {
Replace(notification.Index, item); Replace(notification.Index, item);
} }
@@ -264,25 +264,27 @@ public partial class ObservableCollectionViewModel<TItem> :
return Task.CompletedTask; return Task.CompletedTask;
} }
public int IndexOf(TItem item) => public int IndexOf(TViewModel item) =>
collection.IndexOf(item); collection.IndexOf(item);
int IList.IndexOf(object? value) => int IList.IndexOf(object? value) =>
IsCompatibleObject(value) ? IsCompatibleObject(value) ?
IndexOf((TItem)value!) : -1; IndexOf((TViewModel)value!) : -1;
public void Insert(int index, TItem item) => public virtual Task InitializeAsync() => Task.CompletedTask;
InsertItem(index, item);
public void Insert(int index, TViewModel item) =>
InsertItem(index, item);
void IList.Insert(int index, void IList.Insert(int index,
object? value) object? value)
{ {
if (value is TItem item) if (value is TViewModel item)
{ {
Insert(index, item); Insert(index, item);
} }
} }
public bool Move(int index, TItem item) public bool Move(int index, TViewModel item)
{ {
int oldIndex = collection.IndexOf(item); int oldIndex = collection.IndexOf(item);
if (oldIndex < 0) if (oldIndex < 0)
@@ -295,13 +297,7 @@ public partial class ObservableCollectionViewModel<TItem> :
return true; return true;
} }
public bool Remove(TViewModel item)
public virtual Task InitializeAsync()
{
return Task.CompletedTask;
}
public bool Remove(TItem item)
{ {
int index = collection.IndexOf(item); int index = collection.IndexOf(item);
if (index < 0) if (index < 0)
@@ -319,14 +315,14 @@ public partial class ObservableCollectionViewModel<TItem> :
{ {
if (IsCompatibleObject(value)) if (IsCompatibleObject(value))
{ {
Remove((TItem)value!); Remove((TViewModel)value!);
} }
} }
public void RemoveAt(int index) => public void RemoveAt(int index) =>
RemoveItem(index); RemoveItem(index);
public bool Replace(int index, TItem item) public bool Replace(int index, TViewModel item)
{ {
if (index <= Count - 1) if (index <= Count - 1)
{ {
@@ -346,7 +342,7 @@ public partial class ObservableCollectionViewModel<TItem> :
collection.Clear(); collection.Clear();
protected virtual void InsertItem(int index, protected virtual void InsertItem(int index,
TItem value) TViewModel value)
{ {
Disposer.Add(this, value); Disposer.Add(this, value);
Disposer.Add(value, value, Disposable.Create(() => Disposer.Add(value, value, Disposable.Create(() =>
@@ -363,11 +359,11 @@ public partial class ObservableCollectionViewModel<TItem> :
protected virtual void RemoveItem(int index) => protected virtual void RemoveItem(int index) =>
collection.RemoveAt(index); collection.RemoveAt(index);
protected virtual void SetItem(int index, TItem item) => protected virtual void SetItem(int index, TViewModel item) =>
collection[index] = item; collection[index] = item;
private static bool IsCompatibleObject(object? value) => private static bool IsCompatibleObject(object? value) =>
(value is TItem) || (value == null && default(TItem) == null); (value is TViewModel) || (value == null && default(TViewModel) == null);
private async Task CoreInitializeAsync() private async Task CoreInitializeAsync()
{ {
@@ -378,7 +374,7 @@ public partial class ObservableCollectionViewModel<TItem> :
isInitialized = true; isInitialized = true;
await Mediator.PublishAsync<Enumerate<TItem>>(); await Mediator.PublishAsync<Enumerate<TViewModel>>();
await InitializeAsync(); await InitializeAsync();
} }
@@ -386,7 +382,8 @@ public partial class ObservableCollectionViewModel<TItem> :
CollectionChanged?.Invoke(this, args); CollectionChanged?.Invoke(this, args);
} }
public class ObservableCollectionViewModel(IServiceFactory serviceFactory, public class ObservableCollectionViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : IDisposer disposer) :
ObservableCollectionViewModel<IDisposable>(serviceFactory, mediator, disposer); ObservableCollectionViewModel<IDisposable>(serviceProvider, serviceFactory, mediator, disposer);
+33 -4
View File
@@ -1,16 +1,45 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
namespace Hyperbar; namespace Hyperbar;
public class ObservableViewModel(IServiceFactory serviceFactory, public class ObservableViewModel(IServiceProvider serviceProvider,
IServiceFactory serviceFactory,
IMediator mediator, IMediator mediator,
IDisposer disposer) : IDisposer disposer) :
ObservableObject, ObservableObject,
IDisposable IObservableViewModel
{ {
public IServiceFactory ServiceFactory => serviceFactory; private bool isInitialized;
public IDisposer Disposer => disposer;
public ICommand InitializeCommand =>
new AsyncRelayCommand(CoreInitializeAsync);
public IMediator Mediator => mediator; public IMediator Mediator => mediator;
public void Dispose() => disposer.Dispose(this); public IServiceFactory ServiceFactory => serviceFactory;
public IServiceProvider ServiceProvider => serviceProvider;
public void Dispose()
{
GC.SuppressFinalize(this);
disposer.Dispose(this);
}
public virtual Task InitializeAsync() => Task.CompletedTask;
private async Task CoreInitializeAsync()
{
if (isInitialized)
{
return;
}
isInitialized = true;
await InitializeAsync();
}
} }
@@ -1,11 +0,0 @@
namespace Hyperbar;
public record ContentTemplateDescriptor :
IContentTemplateDescriptor
{
public required Type ContentType { get; set; }
public required Type TemplateType { get; set; }
public required object Key { get; set; }
}
@@ -1,6 +0,0 @@
namespace Hyperbar;
public interface ITemplatedViewModel
{
ITemplateFactory TemplateFactory { get; }
}
@@ -1,10 +1,10 @@
namespace Hyperbar; namespace Hyperbar;
public interface IContentTemplateDescriptor public interface IViewModelTemplateDescriptor
{ {
Type ContentType { get; set; }
object Key { get; set; } object Key { get; set; }
Type TemplateType { get; set; } Type TemplateType { get; set; }
Type ViewModelType { get; set; }
} }
@@ -0,0 +1,6 @@
namespace Hyperbar;
public interface IViewModelTemplateDescriptorProvider
{
IViewModelTemplateDescriptor? Get(object key);
}
@@ -1,6 +1,6 @@
namespace Hyperbar; namespace Hyperbar;
public interface ITemplateFactory public interface IViewModelTemplateFactory
{ {
object? Create(object key); object? Create(object key);
} }
@@ -0,0 +1,11 @@
namespace Hyperbar;
public record ViewModelTemplateDescriptor :
IViewModelTemplateDescriptor
{
public required Type ViewModelType { get; set; }
public required Type TemplateType { get; set; }
public required object Key { get; set; }
}
@@ -0,0 +1,16 @@
namespace Hyperbar;
public class ViewModelTemplateDescriptorProvider(IEnumerable<IViewModelTemplateDescriptor> descriptors) :
IViewModelTemplateDescriptorProvider
{
public IViewModelTemplateDescriptor? Get(object key)
{
if (descriptors.FirstOrDefault(x => x.Key == key)
is IViewModelTemplateDescriptor descriptor)
{
return descriptor;
}
return default;
}
}
@@ -0,0 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
namespace Hyperbar;
public class ViewModelTemplateFactory(IViewModelTemplateDescriptorProvider descriptors,
IServiceProvider services) :
IViewModelTemplateFactory
{
public object? Create(object key)
{
if (descriptors.Get(key)
is IViewModelTemplateDescriptor descriptor)
{
if (services.GetRequiredKeyedService(descriptor.TemplateType,
descriptor.Key) is { } template)
{
return template;
}
}
return default;
}
}