Merge pull request #68 from zadjii-msft/llama/di+vm

Setup DependencyInjection
This commit is contained in:
Michael Hawker MSFT (XAML Llama)
2024-09-20 15:24:16 -07:00
committed by GitHub
19 changed files with 472 additions and 53 deletions

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Extensions.Helpers;
using Microsoft.CmdPal.UI.ViewModels;
namespace Microsoft.CmdPal.UI.Pages;
/// <summary>
/// This class encapsulates the data we load from built-in providers and extensions to use within the same extension-UI system for a <see cref="ListPage"/>.
/// TODO: Need to think about how we structure/interop for the page -> section -> item between the main setup, the extensions, and our viewmodels.
/// </summary>
public partial class MainListPage : DynamicListPage
{
private readonly ISection[] _sections;
// TODO: Thinking we may want a separate MainViewModel from the ShellViewModel and/or a CommandService/Provider
// which holds the TopLevelCommands and anything that needs to access those functions...
public MainListPage(ShellViewModel shellViewModel)
{
_sections = [new MainListSection()
{
Items = shellViewModel.TopLevelCommands.Select(w => w.Unsafe).Where(li => li != null).ToArray(),
}
];
}
public override ISection[] GetItems() => _sections;
}
//// TODO: Temporary until we sort out proper PageViewModel and SectionViewModel containers/setup
#pragma warning disable SA1402 // File may only contain a single type
public partial class MainListSection : ISection
#pragma warning restore SA1402 // File may only contain a single type
{
public required IListItem[] Items { get; set; }
public string Title => "Commands"; // TODO: Localization
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
/// <summary>
/// Used to navigate to the next command in the page when pressing the Down key in the SearchBox.
/// </summary>
public record NavigateNextCommand
{
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
/// <summary>
/// Used to navigate to the previous command in the page when pressing the Down key in the SearchBox.
/// </summary>
public record NavigatePreviousCommand
{
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
// Want to know what a record is? here is a TLDR
// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record
/// <summary>
/// Message to instruct UI to navigate to a list page.
/// </summary>
/// <param name="ViewModel">The <see cref="ListViewModel"/> for the list page to navigate to.</param>
public record NavigateToListMessage(ListViewModel ViewModel)
{
}

View File

@@ -9,4 +9,9 @@
<PackageReference Include="CommunityToolkit.Mvvm" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
<ProjectReference Include="..\extensionsdk\Microsoft.CmdPal.Extensions.Helpers\Microsoft.CmdPal.Extensions.Helpers.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.Models;
public class ExtensionObject<T> // where T : IInspectable
{
private readonly T _value;
public ExtensionObject(T value)
{
_value = value;
}
// public T? Safe {
// get {
// try {
// if (_value!.Equals(_value)) return _value;
// } catch (COMException){ /* log something */ }
// return default;
// }
// }
public T Unsafe => _value;
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels;
public sealed class CommandProviderWrapper
{
public bool IsExtension => extensionWrapper != null;
private readonly bool isValid;
private readonly ICommandProvider _commandProvider;
private readonly IExtensionWrapper? extensionWrapper;
private IListItem[] _topLevelItems = [];
public IListItem[] TopLevelItems => _topLevelItems;
public CommandProviderWrapper(ICommandProvider provider)
{
_commandProvider = provider;
isValid = true;
}
public CommandProviderWrapper(IExtensionWrapper extension)
{
extensionWrapper = extension;
var extensionImpl = extension.GetExtensionObject();
if (extensionImpl?.GetProvider(ProviderType.Commands) is not ICommandProvider provider)
{
throw new ArgumentException("extension didn't actually implement ICommandProvider");
}
_commandProvider = provider;
isValid = true;
}
public async Task LoadTopLevelCommands()
{
if (!isValid)
{
return;
}
var t = new Task<IListItem[]>(() => _commandProvider.TopLevelCommands());
t.Start();
var commands = await t.ConfigureAwait(false);
// On a BG thread here
if (commands != null)
{
_topLevelItems = commands;
}
}
/* This is a View/ExtensionHost piece
* public void AllowSetForeground(bool allow)
{
if (!IsExtension)
{
return;
}
var iextn = extensionWrapper?.GetExtensionObject();
unsafe
{
PInvoke.CoAllowSetForegroundWindow(iextn);
}
}*/
public override bool Equals(object? obj) => obj is CommandProviderWrapper wrapper && isValid == wrapper.isValid;
public override int GetHashCode() => _commandProvider.GetHashCode();
}

View File

@@ -3,15 +3,33 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Models;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ListItemViewModel : ObservableObject
{
// Observable from MVVM Toolkit will auto create public properties that use INotifyPropertyChange
[ObservableProperty]
private string _header = string.Empty;
private readonly ExtensionObject<IListItem> _listItemModel;
[ObservableProperty]
private string _subHeader = string.Empty;
public string Title => _listItemModel.Unsafe.Title;
public string Subtitle => _listItemModel.Unsafe.Subtitle;
/// <summary>
/// Gets the path for the icon to load in the View layer. TODO: Converter/Cache
/// </summary>
public string IconUri => _listItemModel.Unsafe.Icon.Icon;
public ITag[] Tags => _listItemModel.Unsafe.Tags;
public bool HasTags => Tags.Length > 0;
public ListItemViewModel(IListItem model)
{
_listItemModel = new(model);
_listItemModel.Unsafe.PropChanged += Model_PropChanged;
}
private void Model_PropChanged(object sender, PropChangedEventArgs args) => OnPropertyChanged(args.PropertyName);
}

View File

@@ -2,27 +2,41 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using System.Diagnostics;
using CommunityToolkit.Mvvm.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.UI.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ListViewModel : ObservableObject
{
// Observable from MVVM Toolkit will auto create public properties that use INotifyPropertyChange
// Observable from MVVM Toolkit will auto create public properties that use INotifyPropertyChange change
// https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/observablegroupedcollections for grouping support
[ObservableProperty]
private ObservableCollection<ListItemViewModel> _items = [];
private ObservableGroupedCollection<string, ListItemViewModel> _items = [];
public ListViewModel(IListPage model)
{
foreach (var section in model.GetItems())
{
ObservableGroup<string, ListItemViewModel> group = new(section.Title);
foreach (var item in section.Items)
{
group.Add(new(item));
}
Items.AddGroup(group);
}
}
// InvokeItemCommand is what this will be in Xaml due to source generator
[RelayCommand]
private void InvokeItem(ListItemViewModel item)
{
WeakReferenceMessenger.Default.Send<NavigateToDetailsMessage>(new(item));
Debug.WriteLine("Hello!" + item.Header);
}
}

View File

@@ -2,18 +2,58 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.Models;
using Microsoft.CmdPal.UI.Pages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ShellViewModel : ObservableObject
{
[ObservableProperty]
private bool _isLoaded = false;
public ObservableCollection<CommandProviderWrapper> ActionsProvider { get; set; } = [];
public ObservableCollection<ExtensionObject<IListItem>> TopLevelCommands { get; set; } = [];
private readonly IEnumerable<ICommandProvider> _builtInCommands;
public ShellViewModel(IEnumerable<ICommandProvider> builtInCommands)
{
_builtInCommands = builtInCommands;
}
[RelayCommand]
public async Task<bool> LoadAsync()
{
await Task.Delay(2000);
// Load Built In Commands First
foreach (var provider in _builtInCommands)
{
CommandProviderWrapper wrapper = new(provider);
ActionsProvider.Add(wrapper);
await LoadTopLevelCommandsFromProvider(wrapper);
}
IsLoaded = true;
// TODO: would want to hydrate this from our services provider in the View layer, need to think about construction here...
WeakReferenceMessenger.Default.Send<NavigateToListMessage>(new(new(new MainListPage(this))));
return true;
}
private async Task LoadTopLevelCommandsFromProvider(CommandProviderWrapper commandProvider)
{
await commandProvider.LoadTopLevelCommands();
foreach (var i in commandProvider.TopLevelItems)
{
TopLevelCommands.Add(new(i));
}
}
}

View File

@@ -2,6 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Ext.Bookmarks;
using Microsoft.CmdPal.Ext.Calc;
using Microsoft.CmdPal.Ext.Settings;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
@@ -13,6 +19,16 @@ namespace Microsoft.CmdPal.UI;
/// </summary>
public partial class App : Application
{
/// <summary>
/// Gets the current <see cref="App"/> instance in use.
/// </summary>
public static new App Current => (App)Application.Current;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
/// Initializes the singleton application object. This is the first line of authored code
@@ -20,6 +36,8 @@ public partial class App : Application
/// </summary>
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
}
@@ -27,11 +45,29 @@ public partial class App : Application
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
_window = new MainWindow();
_window.Activate();
}
private Window? _window;
/// <summary>
/// Configures the services for the application
/// </summary>
private static ServiceProvider ConfigureServices()
{
ServiceCollection services = new();
// Built-in Commands
services.AddSingleton<ICommandProvider, BookmarksCommandProvider>();
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
services.AddSingleton<ICommandProvider, SettingsCommandProvider>();
// ViewModels
services.AddSingleton<ShellViewModel>((services) => new(services.GetServices<ICommandProvider>()));
return services.BuildServiceProvider();
}
}

View File

@@ -2,8 +2,13 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using CoreVirtualKeyStates = Windows.UI.Core.CoreVirtualKeyStates;
using VirtualKey = Windows.System.VirtualKey;
namespace Microsoft.CmdPal.UI.Controls;
@@ -18,12 +23,46 @@ public sealed partial class SearchBar : UserControl
private void BackButton_Tapped(object sender, TappedRoutedEventArgs e)
{
// TODO
WeakReferenceMessenger.Default.Send<NavigateBackMessage>();
}
private void FilterBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
// TODO
if (e.Handled)
{
return;
}
var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
if (e.Key == VirtualKey.Down)
{
WeakReferenceMessenger.Default.Send<NavigateNextCommand>();
e.Handled = true;
}
else if (e.Key == VirtualKey.Up)
{
WeakReferenceMessenger.Default.Send<NavigatePreviousCommand>();
e.Handled = true;
}
else if (e.Key == VirtualKey.Enter)
{
// TODO: ExecuteCommandMessage?
e.Handled = true;
} // ctrl+k
else if (ctrlPressed && e.Key == VirtualKey.K)
{
// TODO: ShowActionsMessage?
// Move code below to ActionBar
/*FlyoutShowOptions options = new FlyoutShowOptions
{
ShowMode = FlyoutShowMode.Standard,
};
MoreCommandsButton.Flyout.ShowAt(MoreCommandsButton, options);
ActionsDropdown.SelectedIndex = 0;
ActionsDropdown.Focus(FocusState.Programmatic);*/
}
}
private void FilterBox_TextChanged(object sender, TextChangedEventArgs e)

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<Page
x:Class="Microsoft.CmdPal.UI.ListDetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.CmdPal.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.CmdPal.UI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="Transparent"
mc:Ignorable="d">
<Grid>
<!-- not using Interactivity:Interaction.Behaviors due to wanting to do AoT -->
<TextBlock Text="{x:Bind ViewModel.Header}" />
<!-- not using Interactivity:Interaction.Behaviors due to wanting to do AoT -->
<TextBlock Text="{x:Bind ViewModel.Title}" />
<Button Click="Button_Click">Go Back</Button>
</Grid>
</Page>

View File

@@ -16,7 +16,7 @@ namespace Microsoft.CmdPal.UI;
/// </summary>
public sealed partial class ListDetailPage : Page
{
public ListItemViewModel ViewModel { get; set; } = new ListItemViewModel();
public ListItemViewModel? ViewModel { get; set; }
public ListDetailPage()
{

View File

@@ -13,24 +13,46 @@
mc:Ignorable="d">
<Page.Resources>
<!-- https://learn.microsoft.com/en-us/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
<DataTemplate x:Key="ListItemViewModelTemplate" x:DataType="viewmodels:ListItemViewModel">
<ItemContainer AutomationProperties.Name="{x:Bind Header}">
<!-- https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.data.collectionviewsource -->
<CollectionViewSource
x:Name="ItemsCVS"
IsSourceGrouped="True"
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />
<StackPanel Background="{ThemeResource SystemControlBackgroundBaseMediumBrush}" Orientation="Horizontal">
<TextBlock Text="{x:Bind Header}" />
<TextBlock Foreground="{ThemeResource SystemAccentColor}" Text="{x:Bind SubHeader}" />
</StackPanel>
</ItemContainer>
<!-- https://learn.microsoft.com/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
<DataTemplate x:Key="ListItemViewModelTemplate" x:DataType="viewmodels:ListItemViewModel">
<StackPanel
AutomationProperties.Name="{x:Bind Title}"
Orientation="Horizontal"
Spacing="8">
<TextBlock Text="{x:Bind Title}" />
<TextBlock Foreground="{ThemeResource SystemAccentColor}" Text="{x:Bind Subtitle}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<!-- not using Interactivity:Interaction.Behaviors due to wanting to do AoT -->
<ItemsView
IsItemInvokedEnabled="True"
ItemInvoked="ItemsView_ItemInvoked"
<!-- sticking with ListView as ItemsView doesn't have grouping built-in, could investigate coordinating
keyboards between them and using ItemsRepeater for group headers, though that wouldn't use CVS either -->
<ListView
x:Name="ItemsList"
IsItemClickEnabled="True"
ItemClick="ListView_ItemClick"
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
ItemsSource="{x:Bind ViewModel.Items}" />
ItemsSource="{x:Bind ItemsCVS.View, Mode=OneWay}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
FontFamily="Segoe UI"
FontSize="16"
Text="{Binding Key}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
</Page>

View File

@@ -2,31 +2,67 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.CmdPal.UI;
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ListPage : Page
public sealed partial class ListPage : Page,
IRecipient<NavigateNextCommand>,
IRecipient<NavigatePreviousCommand>
{
public ListViewModel ViewModel { get; set; } = new();
public ListViewModel? ViewModel { get; set; }
public ListPage()
{
this.InitializeComponent();
ViewModel.Items.Add(new ListItemViewModel { Header = "Hello", SubHeader = "World" });
ViewModel.Items.Add(new ListItemViewModel { Header = "Clint", SubHeader = "Rutkas" });
ViewModel.Items.Add(new ListItemViewModel { Header = "Michael", SubHeader = "Hawker" });
WeakReferenceMessenger.Default.RegisterAll(this);
}
private void ItemsView_ItemInvoked(ItemsView sender, ItemsViewItemInvokedEventArgs args)
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (args.InvokedItem is ListItemViewModel item)
if (e.Parameter is ListViewModel lvm)
{
ViewModel.InvokeItemCommand.Execute(item);
ViewModel = lvm;
}
base.OnNavigatedTo(e);
}
private void ListView_ItemClick(object sender, ItemClickEventArgs e)
{
if (e.ClickedItem is ListItemViewModel item)
{
ViewModel?.InvokeItemCommand.Execute(item);
}
}
public void Receive(NavigateNextCommand message)
{
// Note: We may want to just have the notion of a 'SelectedCommand' in our VM
// And then have these commands manipulate that state being bound to the UI instead
// We may want to see how other non-list UIs need to behave to make this decision
// At least it's decoupled from the SearchBox now :)
if (ItemsList.SelectedIndex < ItemsList.Items.Count - 1)
{
ItemsList.SelectedIndex++;
ItemsList.ScrollIntoView(ItemsList.SelectedItem);
}
}
public void Receive(NavigatePreviousCommand message)
{
if (ItemsList.SelectedIndex > 0)
{
ItemsList.SelectedIndex--;
ItemsList.ScrollIntoView(ItemsList.SelectedItem);
}
}
}

View File

@@ -34,15 +34,10 @@ public sealed partial class LoadingPage : Page
{
await shellVM.LoadCommand.ExecutionTask!;
if (shellVM.LoadCommand.ExecutionTask.Status == TaskStatus.RanToCompletion)
if (shellVM.LoadCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
await _queue.EnqueueAsync(() =>
{
Frame.Navigate(typeof(ListPage), new ListViewModel(), new DrillInNavigationTransitionInfo());
});
// TODO: Handle failure case
}
// TODO: Handle failure case
});
}

View File

@@ -35,10 +35,25 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
<Manifest Include="$(ApplicationManifest)" />
<PackageReference Include="Microsoft.Windows.CsWin32">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<!-- The following harcoded package versions are to work around security vulnerbilities in 4.3.0 -->
<PackageReference Include="System.Net.Http" />
<PackageReference Include="System.Private.Uri" />
<PackageReference Include="System.Text.RegularExpressions" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<!--
@@ -51,7 +66,18 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
<ProjectReference Include="..\extensionsdk\Microsoft.CmdPal.Extensions.Helpers\Microsoft.CmdPal.Extensions.Helpers.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.UI.ViewModels\Microsoft.CmdPal.UI.ViewModels.csproj" />
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj" />
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj" />
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.CmdPalSettings\Microsoft.CmdPal.Ext.Settings.csproj" />
<ProjectReference Include="..\Microsoft.Terminal.UI\Microsoft.Terminal.UI.vcxproj">
<ReferenceOutputAssembly>True</ReferenceOutputAssembly>
<Private>True</Private>
<CopyLocalSatelliteAssemblies>True</CopyLocalSatelliteAssemblies>
</ProjectReference>
</ItemGroup>
<ItemGroup>

View File

@@ -5,7 +5,9 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
namespace Microsoft.CmdPal.UI;
@@ -14,10 +16,13 @@ namespace Microsoft.CmdPal.UI;
/// </summary>
public sealed partial class ShellPage :
Page,
IRecipient<NavigateBackMessage>,
IRecipient<NavigateToDetailsMessage>,
IRecipient<NavigateBackMessage>
IRecipient<NavigateToListMessage>
{
public ShellViewModel ViewModel { get; private set; } = new ShellViewModel();
private readonly DrillInNavigationTransitionInfo _drillInNavigationTransitionInfo = new();
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
public ShellPage()
{
@@ -29,10 +34,7 @@ public sealed partial class ShellPage :
RootFrame.Navigate(typeof(LoadingPage), ViewModel);
}
public void Receive(NavigateToDetailsMessage message)
{
RootFrame.Navigate(typeof(ListDetailPage), message.ListItem);
}
public void Receive(NavigateToDetailsMessage message) => RootFrame.Navigate(typeof(ListDetailPage), message.ListItem, _drillInNavigationTransitionInfo);
public void Receive(NavigateBackMessage message)
{
@@ -41,4 +43,6 @@ public sealed partial class ShellPage :
RootFrame.GoBack();
}
}
public void Receive(NavigateToListMessage message) => RootFrame.Navigate(typeof(ListPage), message.ViewModel, _drillInNavigationTransitionInfo);
}