CmdPal: Fix FiltersViewModel binding (#42467)

## Summary of the Pull Request

This PR resolves crashes on pages with filters, such as Windows Terminal
profiles or Windows Services, when compiled with trimming/AOT.

It removes runtime binding from the FiltersDropDown control, effectively
preventing crashes caused by trimming/AOT dropping binding metadata for
FilterItemViewModel.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #42428
- [x] Closes: #42482
- [x] Related to: #42458
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Jiří Polášek
2025-10-17 16:48:48 +02:00
committed by GitHub
parent 92d9f1061c
commit 55e974dea4
2 changed files with 41 additions and 15 deletions

View File

@@ -2,8 +2,10 @@
// 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.Generic;
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Core.ViewModels;
@@ -13,6 +15,8 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
public string CurrentFilterId { get; private set; } = string.Empty;
public IFilterItemViewModel? CurrentFilter { get; private set; }
public IFilterItemViewModel[] Filters { get; private set; } = [];
public bool ShouldShowFilters => Filters.Length > 0;
@@ -30,11 +34,13 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
if (_filtersModel.Unsafe is not null)
{
var filters = _filtersModel.Unsafe.GetFilters();
Filters = BuildFilters(filters ?? []);
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
var currentFilterId = _filtersModel.Unsafe.CurrentFilterId ?? string.Empty;
CurrentFilterId = _filtersModel.Unsafe.CurrentFilterId ?? string.Empty;
UpdateProperty(nameof(CurrentFilterId));
var result = BuildFilters(filters ?? [], currentFilterId);
Filters = result.Items;
CurrentFilterId = currentFilterId;
CurrentFilter = result.Selected;
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters), nameof(CurrentFilterId), nameof(CurrentFilter));
return;
}
@@ -45,27 +51,48 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
}
Filters = [];
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
CurrentFilterId = string.Empty;
UpdateProperty(nameof(CurrentFilterId));
CurrentFilter = null;
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters), nameof(CurrentFilterId), nameof(CurrentFilter));
}
private IFilterItemViewModel[] BuildFilters(IFilterItem[] filters)
private (IFilterItemViewModel[] Items, IFilterItemViewModel? Selected) BuildFilters(IFilterItem[] filters, string currentFilterId)
{
return [..filters.Select<IFilterItem, IFilterItemViewModel>(filter =>
if (filters is null || filters.Length == 0)
{
return ([], null);
}
var items = new List<IFilterItemViewModel>(filters.Length);
FilterItemViewModel? firstFilterItem = null;
FilterItemViewModel? selectedFilterItem = null;
foreach (var filter in filters)
{
if (filter is IFilter filterItem)
{
var filterItemViewModel = new FilterItemViewModel(filterItem!, PageContext);
var filterItemViewModel = new FilterItemViewModel(filterItem, PageContext);
filterItemViewModel.InitializeProperties();
return filterItemViewModel;
if (firstFilterItem is null)
{
firstFilterItem = filterItemViewModel;
}
if (selectedFilterItem is null && filterItemViewModel.Id == currentFilterId)
{
selectedFilterItem = filterItemViewModel;
}
items.Add(filterItemViewModel);
}
else
{
return new SeparatorViewModel();
items.Add(new SeparatorViewModel());
}
})];
}
return (items.ToArray(), selectedFilterItem ?? firstFilterItem);
}
public override void SafeCleanup()

View File

@@ -74,8 +74,7 @@
ItemsSource="{x:Bind ViewModel.Filters, Mode=OneWay}"
PlaceholderText="Filters"
PreviewKeyDown="FiltersComboBox_PreviewKeyDown"
SelectedValue="{x:Bind ViewModel.CurrentFilterId, Mode=OneWay}"
SelectedValuePath="Id"
SelectedValue="{x:Bind ViewModel.CurrentFilter, Mode=OneWay}"
SelectionChanged="FiltersComboBox_SelectionChanged"
Style="{StaticResource ComboBoxStyle}"
Visibility="{x:Bind ViewModel.ShouldShowFilters, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">