From 53bb471449ba0f6379a3d8997c5ad3505efe1146 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 15 Jul 2025 09:33:28 -0500 Subject: [PATCH] CmdPal: Add a viewmodel factory for pages (#40504) _targets #40482_ ref #40113 A smaller refactor, to be sure. This just moves the instantiation of PageViewModel objects out of the ShellViewModel, and into its own class. The idea being that other page types could be added, just by extending that factory (or implementing your own), and then also handling those new VMs in your ShellPage.xaml.cs equivalent. --- .../PageViewModel.cs | 16 +++++++++-- .../PageViewModelFactory.cs | 27 +++++++++++++++++++ .../ShellViewModel.cs | 19 +++---------- .../cmdpal/Microsoft.CmdPal.UI/App.xaml.cs | 1 + 4 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModelFactory.cs diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs index 126efa83ca..681f7bc7f1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs @@ -249,7 +249,19 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext public interface IPageContext { - public void ShowException(Exception ex, string? extensionHint = null); + void ShowException(Exception ex, string? extensionHint = null); - public TaskScheduler Scheduler { get; } + TaskScheduler Scheduler { get; } +} + +public interface IPageViewModelFactoryService +{ + /// + /// Creates a new instance of the page view model for the given page type. + /// + /// The page for which to create the view model. + /// Indicates whether the page is not the top-level page. + /// The command palette host that will host the page (for status messages) + /// A new instance of the page view model. + PageViewModel? TryCreatePageViewModel(IPage page, bool nested, CommandPaletteHost host); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModelFactory.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModelFactory.cs new file mode 100644 index 0000000000..83dbe51624 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModelFactory.cs @@ -0,0 +1,27 @@ +// 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.CommandPalette.Extensions; + +namespace Microsoft.CmdPal.UI.ViewModels; + +public class PageViewModelFactory : IPageViewModelFactoryService +{ + private readonly TaskScheduler _scheduler; + + public PageViewModelFactory(TaskScheduler scheduler) + { + _scheduler = scheduler; + } + + public PageViewModel? TryCreatePageViewModel(IPage page, bool nested, CommandPaletteHost host) + { + return page switch + { + IListPage listPage => new ListViewModel(listPage, _scheduler, host) { IsNested = nested }, + IContentPage contentPage => new ContentPageViewModel(contentPage, _scheduler, host), + _ => null, + }; + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs index 112c2b67df..294b0021c7 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs @@ -21,6 +21,7 @@ public partial class ShellViewModel : ObservableObject, { private readonly IRootPageService _rootPageService; private readonly TaskScheduler _scheduler; + private readonly IPageViewModelFactoryService _pageViewModelFactory; private readonly Lock _invokeLock = new(); private Task? _handleInvokeTask; @@ -65,8 +66,9 @@ public partial class ShellViewModel : ObservableObject, public bool IsNested { get => _isNested; } - public ShellViewModel(TaskScheduler scheduler, IRootPageService rootPageService) + public ShellViewModel(TaskScheduler scheduler, IRootPageService rootPageService, IPageViewModelFactoryService pageViewModelFactory) { + _pageViewModelFactory = pageViewModelFactory; _scheduler = scheduler; _rootPageService = rootPageService; _currentPage = new LoadingPageViewModel(null, _scheduler); @@ -252,7 +254,7 @@ public partial class ShellViewModel : ObservableObject, _isNested = !isMainPage; // Construct our ViewModel of the appropriate type and pass it the UI Thread context. - var pageViewModel = GetViewModelForPage(page, _isNested, host); + var pageViewModel = _pageViewModelFactory.TryCreatePageViewModel(page, _isNested, host); if (pageViewModel == null) { Logger.LogError($"Failed to create ViewModel for page {page.GetType().Name}"); @@ -397,19 +399,6 @@ public partial class ShellViewModel : ObservableObject, } } - private PageViewModel? GetViewModelForPage(IPage page, bool nested, CommandPaletteHost host) - { - return page switch - { - IListPage listPage => new ListViewModel(listPage, _scheduler, host) - { - IsNested = nested, - }, - IContentPage contentPage => new ContentPageViewModel(contentPage, _scheduler, host), - _ => null, - }; - } - public void SetActiveExtension(IExtensionWrapper? extension) { if (extension != _activeExtension) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs index a9f416483a..1007998f7c 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/App.xaml.cs @@ -149,6 +149,7 @@ public partial class App : Application // ViewModels services.AddSingleton(); + services.AddSingleton(); return services.BuildServiceProvider(); }