mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-12 07:16:44 +01:00
Merge pull request #68 from zadjii-msft/llama/di+vm
Setup DependencyInjection
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user