2025-07-09 14:53:47 -05:00
|
|
|
<?xml version="1.0" encoding="utf-8" ?>
|
|
|
|
|
<UserControl
|
|
|
|
|
x:Class="Microsoft.CmdPal.UI.Controls.ContextMenu"
|
|
|
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
|
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
|
|
|
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
|
|
|
|
xmlns:cmdpalUI="using:Microsoft.CmdPal.UI"
|
|
|
|
|
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
2025-07-15 12:21:44 -05:00
|
|
|
xmlns:coreViewModels="using:Microsoft.CmdPal.Core.ViewModels"
|
2025-07-09 14:53:47 -05:00
|
|
|
xmlns:cpcontrols="using:Microsoft.CmdPal.UI.Controls"
|
|
|
|
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
|
|
|
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
|
|
|
|
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
|
|
|
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
|
|
|
|
|
xmlns:ui="using:CommunityToolkit.WinUI"
|
2025-07-15 12:21:44 -05:00
|
|
|
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
|
2025-07-09 14:53:47 -05:00
|
|
|
Background="Transparent"
|
2025-09-02 17:53:53 -07:00
|
|
|
PreviewKeyDown="UserControl_PreviewKeyDown"
|
2025-07-09 14:53:47 -05:00
|
|
|
mc:Ignorable="d">
|
|
|
|
|
|
|
|
|
|
<UserControl.Resources>
|
|
|
|
|
<ResourceDictionary>
|
|
|
|
|
<cmdpalUI:KeyChordToStringConverter x:Key="KeyChordToStringConverter" />
|
2025-07-16 06:25:24 -05:00
|
|
|
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
2025-07-09 14:53:47 -05:00
|
|
|
|
|
|
|
|
<cmdpalUI:ContextItemTemplateSelector
|
|
|
|
|
x:Key="ContextItemTemplateSelector"
|
|
|
|
|
Critical="{StaticResource CriticalContextMenuViewModelTemplate}"
|
|
|
|
|
Default="{StaticResource DefaultContextMenuViewModelTemplate}"
|
|
|
|
|
Separator="{StaticResource SeparatorContextMenuViewModelTemplate}" />
|
|
|
|
|
|
|
|
|
|
<!-- Template for context items in the context item menu -->
|
2025-07-15 12:21:44 -05:00
|
|
|
<DataTemplate x:Key="DefaultContextMenuViewModelTemplate" x:DataType="coreViewModels:CommandContextItemViewModel">
|
2025-07-09 14:53:47 -05:00
|
|
|
<Grid AutomationProperties.Name="{x:Bind Title}">
|
|
|
|
|
<Grid.ColumnDefinitions>
|
|
|
|
|
<ColumnDefinition Width="32" />
|
|
|
|
|
<ColumnDefinition Width="*" />
|
|
|
|
|
<ColumnDefinition Width="Auto" />
|
|
|
|
|
</Grid.ColumnDefinitions>
|
|
|
|
|
<cpcontrols:IconBox
|
|
|
|
|
Width="16"
|
|
|
|
|
Height="16"
|
|
|
|
|
Margin="4,0,0,0"
|
|
|
|
|
HorizontalAlignment="Left"
|
|
|
|
|
SourceKey="{x:Bind Icon}"
|
|
|
|
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
|
|
|
|
<TextBlock
|
2025-07-16 06:25:24 -05:00
|
|
|
x:Name="TitleTextBlock"
|
2025-07-09 14:53:47 -05:00
|
|
|
Grid.Column="1"
|
2025-07-16 06:25:24 -05:00
|
|
|
MaxWidth="200"
|
|
|
|
|
HorizontalAlignment="Left"
|
2025-07-09 14:53:47 -05:00
|
|
|
VerticalAlignment="Center"
|
2025-09-26 12:04:47 -05:00
|
|
|
MaxLines="1"
|
2025-07-16 06:25:24 -05:00
|
|
|
Text="{x:Bind Title}"
|
|
|
|
|
TextTrimming="WordEllipsis"
|
|
|
|
|
TextWrapping="NoWrap">
|
|
|
|
|
<ToolTipService.ToolTip>
|
|
|
|
|
<ToolTip Content="{x:Bind Title}" Visibility="{Binding IsTextTrimmed, ElementName=TitleTextBlock, Converter={StaticResource BoolToVisibilityConverter}}" />
|
|
|
|
|
</ToolTipService.ToolTip>
|
|
|
|
|
</TextBlock>
|
2025-07-09 14:53:47 -05:00
|
|
|
<TextBlock
|
|
|
|
|
Grid.Column="2"
|
|
|
|
|
Margin="16,0,0,0"
|
|
|
|
|
HorizontalAlignment="Right"
|
|
|
|
|
VerticalAlignment="Center"
|
|
|
|
|
Foreground="{ThemeResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
|
|
|
|
|
Style="{StaticResource CaptionTextBlockStyle}"
|
|
|
|
|
Text="{x:Bind RequestedShortcut, Converter={StaticResource KeyChordToStringConverter}}" />
|
|
|
|
|
</Grid>
|
|
|
|
|
</DataTemplate>
|
|
|
|
|
|
|
|
|
|
<!-- Template for context items flagged as critical -->
|
2025-07-15 12:21:44 -05:00
|
|
|
<DataTemplate x:Key="CriticalContextMenuViewModelTemplate" x:DataType="coreViewModels:CommandContextItemViewModel">
|
2025-07-09 14:53:47 -05:00
|
|
|
<Grid AutomationProperties.Name="{x:Bind Title}">
|
|
|
|
|
<Grid.ColumnDefinitions>
|
|
|
|
|
<ColumnDefinition Width="32" />
|
|
|
|
|
<ColumnDefinition Width="*" />
|
|
|
|
|
<ColumnDefinition Width="Auto" />
|
|
|
|
|
</Grid.ColumnDefinitions>
|
|
|
|
|
<cpcontrols:IconBox
|
|
|
|
|
Width="16"
|
|
|
|
|
Height="16"
|
|
|
|
|
Margin="4,0,0,0"
|
|
|
|
|
HorizontalAlignment="Left"
|
|
|
|
|
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
|
|
|
|
SourceKey="{x:Bind Icon}"
|
|
|
|
|
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
|
|
|
|
|
<TextBlock
|
2025-07-16 06:25:24 -05:00
|
|
|
x:Name="TitleTextBlock"
|
2025-07-09 14:53:47 -05:00
|
|
|
Grid.Column="1"
|
2025-07-16 06:25:24 -05:00
|
|
|
MaxWidth="200"
|
|
|
|
|
HorizontalAlignment="Left"
|
2025-07-09 14:53:47 -05:00
|
|
|
VerticalAlignment="Center"
|
2025-09-26 12:04:47 -05:00
|
|
|
MaxLines="1"
|
2025-07-09 14:53:47 -05:00
|
|
|
Style="{StaticResource ContextItemTitleTextBlockCriticalStyle}"
|
2025-07-16 06:25:24 -05:00
|
|
|
Text="{x:Bind Title}"
|
|
|
|
|
TextTrimming="WordEllipsis"
|
|
|
|
|
TextWrapping="NoWrap">
|
|
|
|
|
<ToolTipService.ToolTip>
|
|
|
|
|
<ToolTip Content="{x:Bind Title}" Visibility="{Binding IsTextTrimmed, ElementName=TitleTextBlock, Converter={StaticResource BoolToVisibilityConverter}}" />
|
|
|
|
|
</ToolTipService.ToolTip>
|
|
|
|
|
</TextBlock>
|
2025-07-09 14:53:47 -05:00
|
|
|
<TextBlock
|
|
|
|
|
Grid.Column="2"
|
|
|
|
|
Margin="16,0,0,0"
|
|
|
|
|
HorizontalAlignment="Right"
|
|
|
|
|
VerticalAlignment="Center"
|
|
|
|
|
Style="{StaticResource ContextItemCaptionTextBlockCriticalStyle}"
|
|
|
|
|
Text="{x:Bind RequestedShortcut, Converter={StaticResource KeyChordToStringConverter}}" />
|
|
|
|
|
</Grid>
|
|
|
|
|
</DataTemplate>
|
|
|
|
|
|
|
|
|
|
<!-- Template for context item separators -->
|
CmdPal: Filters for DynamicListPage? Yes, please. (#40783)
Closes: #40382
## To-do list
- [x] Add support for "single-select" filters to DynamicListPage
- [x] Filters can contain icons
- [x] Filter list can contain separators
- [x] Update Windows Services built-in extension to support filtering by
all, started, stopped, and pending services
- [x] Update SampleExtension dynamic list sample to filter.
## Example of filters in use
```C#
internal sealed partial class ServicesListPage : DynamicListPage
{
public ServicesListPage()
{
Icon = Icons.ServicesIcon;
Name = "Windows Services";
var filters = new ServiceFilters();
filters.PropChanged += Filters_PropChanged;
Filters = filters;
}
private void Filters_PropChanged(object sender, IPropChangedEventArgs args) => RaiseItemsChanged();
public override void UpdateSearchText(string oldSearch, string newSearch) => RaiseItemsChanged();
public override IListItem[] GetItems()
{
// ServiceHelper.Search knows how to filter based on the CurrentFilterIds provided
var items = ServiceHelper.Search(SearchText, Filters.CurrentFilterIds).ToArray();
return items;
}
}
public partial class ServiceFilters : Filters
{
public ServiceFilters()
{
// This would be a default selection. Not providing this will cause the filter
// control to display the "Filter" placeholder text.
CurrentFilterIds = ["all"];
}
public override IFilterItem[] GetFilters()
{
return [
new Filter() { Id = "all", Name = "All Services" },
new Separator(),
new Filter() { Id = "running", Name = "Running", Icon = Icons.GreenCircleIcon },
new Filter() { Id = "stopped", Name = "Stopped", Icon = Icons.RedCircleIcon },
new Filter() { Id = "paused", Name = "Paused", Icon = Icons.PauseIcon },
];
}
}
```
## Current example of behavior
https://github.com/user-attachments/assets/2e325763-ad3a-4445-bbe2-a840df08d0b3
---------
Co-authored-by: Mike Griese <migrie@microsoft.com>
2025-08-21 05:40:09 -05:00
|
|
|
<DataTemplate x:Key="SeparatorContextMenuViewModelTemplate" x:DataType="coreViewModels:SeparatorViewModel">
|
2025-07-09 14:53:47 -05:00
|
|
|
<Rectangle
|
|
|
|
|
Height="1"
|
|
|
|
|
Margin="-16,-12,-12,-12"
|
2025-08-14 17:52:37 +02:00
|
|
|
Fill="{ThemeResource MenuFlyoutSeparatorBackground}" />
|
2025-07-09 14:53:47 -05:00
|
|
|
</DataTemplate>
|
|
|
|
|
</ResourceDictionary>
|
|
|
|
|
</UserControl.Resources>
|
|
|
|
|
|
|
|
|
|
<Grid x:Name="ContextMenuGrid">
|
|
|
|
|
<Grid.RowDefinitions>
|
|
|
|
|
<RowDefinition />
|
|
|
|
|
<RowDefinition />
|
|
|
|
|
</Grid.RowDefinitions>
|
|
|
|
|
|
|
|
|
|
<StackPanel x:Name="CommandsPanel">
|
|
|
|
|
<ListView
|
|
|
|
|
x:Name="CommandsDropdown"
|
|
|
|
|
MinWidth="248"
|
|
|
|
|
IsItemClickEnabled="True"
|
|
|
|
|
ItemClick="CommandsDropdown_ItemClick"
|
|
|
|
|
ItemTemplateSelector="{StaticResource ContextItemTemplateSelector}"
|
|
|
|
|
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
2025-07-28 20:17:27 -05:00
|
|
|
PreviewKeyDown="CommandsDropdown_PreviewKeyDown"
|
2025-07-09 14:53:47 -05:00
|
|
|
SelectionMode="Single">
|
|
|
|
|
<ListView.ItemContainerStyle>
|
|
|
|
|
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
|
|
|
|
|
<Setter Property="MinHeight" Value="0" />
|
|
|
|
|
<Setter Property="Padding" Value="12,8" />
|
|
|
|
|
</Style>
|
|
|
|
|
</ListView.ItemContainerStyle>
|
|
|
|
|
<ListView.ItemContainerTransitions>
|
|
|
|
|
<TransitionCollection />
|
|
|
|
|
</ListView.ItemContainerTransitions>
|
|
|
|
|
</ListView>
|
|
|
|
|
</StackPanel>
|
|
|
|
|
<TextBox
|
|
|
|
|
x:Name="ContextFilterBox"
|
|
|
|
|
x:Uid="ContextFilterBox"
|
|
|
|
|
Margin="4"
|
2025-07-16 06:25:24 -05:00
|
|
|
IsTextScaleFactorEnabled="True"
|
2025-07-09 14:53:47 -05:00
|
|
|
KeyDown="ContextFilterBox_KeyDown"
|
|
|
|
|
PreviewKeyDown="ContextFilterBox_PreviewKeyDown"
|
|
|
|
|
TextChanged="ContextFilterBox_TextChanged" />
|
|
|
|
|
<VisualStateManager.VisualStateGroups>
|
|
|
|
|
<VisualStateGroup x:Name="ContextMenuOrder">
|
|
|
|
|
<VisualState x:Name="FilterOnTop">
|
|
|
|
|
<VisualState.StateTriggers>
|
|
|
|
|
<ui:IsEqualStateTrigger Value="{x:Bind ViewModel.FilterOnTop, Mode=OneWay}" To="True" />
|
|
|
|
|
</VisualState.StateTriggers>
|
|
|
|
|
<VisualState.Setters>
|
|
|
|
|
<Setter Target="CommandsPanel.(Grid.Row)" Value="1" />
|
|
|
|
|
<Setter Target="ContextFilterBox.(Grid.Row)" Value="0" />
|
|
|
|
|
<Setter Target="CommandsDropdown.Margin" Value="0, 0, 0, 4" />
|
|
|
|
|
</VisualState.Setters>
|
|
|
|
|
</VisualState>
|
|
|
|
|
<VisualState x:Name="FilterOnBottom">
|
|
|
|
|
<VisualState.StateTriggers>
|
|
|
|
|
<ui:IsEqualStateTrigger Value="{x:Bind ViewModel.FilterOnTop, Mode=OneWay}" To="False" />
|
|
|
|
|
</VisualState.StateTriggers>
|
|
|
|
|
<VisualState.Setters>
|
|
|
|
|
<Setter Target="CommandsPanel.(Grid.Row)" Value="0" />
|
|
|
|
|
<Setter Target="ContextFilterBox.(Grid.Row)" Value="1" />
|
|
|
|
|
<Setter Target="CommandsDropdown.Margin" Value="0, 4, 0, 0" />
|
|
|
|
|
</VisualState.Setters>
|
|
|
|
|
</VisualState>
|
|
|
|
|
</VisualStateGroup>
|
|
|
|
|
</VisualStateManager.VisualStateGroups>
|
|
|
|
|
</Grid>
|
|
|
|
|
</UserControl>
|