[EditableComboBox] Add grouping support#591
[EditableComboBox] Add grouping support#591Xavier Fortin (xfortin-devolutions) wants to merge 7 commits into
Conversation
Rename GroupHeader -> ComboBoxGroupHeader and GroupedItemsBuilder -> ComboBoxGroupedItemsBuilder to scope these shared, combo-box-only helpers and avoid implying a general-purpose grouping utility.
5dc4d2f to
3f9af66
Compare
There was a problem hiding this comment.
Pull request overview
Adds optional grouping support to EditableComboBox using shared grouping infrastructure from GroupedComboBox.
Changes:
- Extracts shared group-header and item-building logic.
- Adds grouping, ordering, templating, filtering, and selection safeguards.
- Expands the SampleApp grouping demonstrations.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Devolutions.AvaloniaControls/README.md |
Documents grouping support. |
src/Devolutions.AvaloniaControls/Helpers/ComboBoxGroupedItemsBuilder.cs |
Builds grouped display lists. |
src/Devolutions.AvaloniaControls/Controls/GroupedComboBox/GroupedComboBox.axaml.cs |
Uses shared grouping infrastructure. |
src/Devolutions.AvaloniaControls/Controls/EditableComboBox/InnerComboBox.cs |
Creates and styles header containers. |
src/Devolutions.AvaloniaControls/Controls/EditableComboBox/EditableComboBox.axaml.cs |
Implements grouping behavior and APIs. |
src/Devolutions.AvaloniaControls/Controls/ComboBoxGroupHeader.cs |
Defines shared header pseudo-items. |
samples/SampleApp/ViewModels/EditableComboBoxViewModel.cs |
Supplies grouping demo data and commands. |
samples/SampleApp/DemoPages/EditableComboBoxDemo.axaml |
Adds grouping demonstrations and live tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
GroupOrderBinding's getter was built against the item type but invoked
with the group-key string, so a path binding like {Binding Group.SortOrder}
threw InvalidCastException; it also formatted the value to a string, sorting
numeric orders lexicographically (10 before 2).
The shared builder now evaluates the order selector against a representative
item of each group and keeps the raw value (BuildRawGetter), so it works for
any IComparable and sorts numerically. The key-based GroupOrderSelector
delegate is adapted onto the item, so its behavior is unchanged. Applied to
both EditableComboBox and GroupedComboBox, and documented on the
GroupOrderBinding property.
Model the sample food data with a shared FoodGroup(Name, SortOrder) that items reference, and add an "Order via binding" case (GroupOrderBinding bound to Group.SortOrder) to both the EditableComboBox and GroupedComboBox grouping demos.
| <EditableComboBox.ItemTemplate> | ||
| <EditableComboBoxDataTemplate x:DataType="viewModels:FoodItem" SelectedItemValue="{Binding Name}"> | ||
| <TextBlock Text="{Binding Name}" /> | ||
| </EditableComboBoxDataTemplate> | ||
| </EditableComboBox.ItemTemplate> |
There was a problem hiding this comment.
It's good to be able to supply template (and kinda required for any ItemsControl), but we should also support ItemsControl's DisplayMemberBinding property, that would be used as a fallback when there's no template (both for the item in the dropdown and the displayed value; if there's a template but no SelectedItemValue binding then it would use that too).
In fact, wouldn't that render SelectedItemValue redundant / unneeded?
There was a problem hiding this comment.
But since that's already like this, that would be a separate task.
| else if (this.innerComboBox.SelectedIndex == -1) | ||
| { | ||
| this.innerComboBox.SelectedIndex = 0; | ||
| // Skip a leading group header so the first arrow-down lands on a selectable item. |
There was a problem hiding this comment.
Shouldn't that be just when using grouping?
Summary
Adds optional item grouping to
EditableComboBox. Drop-down items can be grouped under headers, with a customizable group order and an empty group key that renders headerless (its items appear at the top with no header).The feature reuses the proven
GroupedComboBoxdesign (interleave non-selectable header pseudo-items into the drop-down; empty group key renders headerless) rather than inventing a new one. Grouping is inert unless a group selector is configured, so existingEditableComboBoxusage is unaffected.What changed
Shared grouping core (combo-box-scoped names, since only the combo box controls use them)
Controls/ComboBoxGroupHeader.cs(new) — the header pseudo-item, extracted fromGroupedComboBoxso both controls share it.Helpers/ComboBoxGroupedItemsBuilder.cs(new) — the group → order → interleave logic, extracted fromGroupedComboBox.RebuildDisplayItems.GroupedComboBoxnow delegates to it (behavior-preserving).EditableComboBox
GroupedComboBox:GroupBinding/GroupSelector,GroupOrderAlphabetical,GroupOrderSelector/GroupOrderBinding,HeaderForeground,HeaderMargin,HeaderTemplate,EmptyGroupName.filteredItems) — the publicItemsSourceis never mutated.Filtermode re-groups the filtered subset, so groups with no matches drop out along with their headers.GroupedComboBox).InnerComboBoxrenders the reusedGroupedComboBoxHeaderItemcontainer for header rows (no new per-theme styling).Sample
EditableComboBoxDemogains a Grouping section mirroring theGroupedComboBoxdemo set (named groups, empty group with/withoutEmptyGroupName, scrolling/virtualized,HeaderTemplate, inner slots) plus a Live-tests panel. Page wrapped in aScrollViewerso it scrolls.Notes / intentional differences from GroupedComboBox
EditableComboBoxgrouping is data-driven (GroupBinding/GroupSelector), so headers aren't declared inline as items.EditableComboBoxDataTemplatewithSelectedItemValueso the editable textbox shows a clean value.PropertyChanged(collection add/remove is reactive; editing an item's group key updates on next open). Can be added if needed.Testing
PageCatalogTests+MainWindowNavigationTestspass (demo page + view model instantiate in all themes).