Skip to content

Commit baf6970

Browse files
timuniepunker76
authored andcommitted
Implement DropDown Header and Footer and Select All Command
With this change the user can implement quite easy a select all /select none button or other header or footer content
1 parent 7f04eff commit baf6970

File tree

2 files changed

+258
-6
lines changed

2 files changed

+258
-6
lines changed

src/MahApps.Metro/Controls/MultiSelectionComboBox/MultiSelectionComboBox.cs

Lines changed: 233 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static MultiSelectionComboBox()
3737
TextProperty.OverrideMetadata(typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnTextChanged));
3838
CommandManager.RegisterClassCommandBinding(typeof(MultiSelectionComboBox), new CommandBinding(ClearContentCommand, ExecutedClearContentCommand, CanExecuteClearContentCommand));
3939
CommandManager.RegisterClassCommandBinding(typeof(MultiSelectionComboBox), new CommandBinding(RemoveItemCommand, RemoveItemCommand_Executed, RemoveItemCommand_CanExecute));
40+
41+
CommandManager.RegisterClassCommandBinding(typeof(MultiSelectionComboBox), new CommandBinding(SelectAllCommand, new ExecutedRoutedEventHandler(OnSelectAll), new CanExecuteRoutedEventHandler(OnQueryStatusSelectAll)));
4042
}
4143

4244
public MultiSelectionComboBox()
@@ -68,6 +70,11 @@ public MultiSelectionComboBox()
6870
private bool shouldAddItems; // Defines if the MSCB should add new items from text input. Don't set this to true while input is pending. We cannot know how long the user needs for typing.
6971
private bool IsSyncingSelectedItems; // true if syncing in one or the other direction already running
7072
private DispatcherTimer? _updateSelectedItemsFromTextTimer;
73+
private static readonly RoutedUICommand SelectAllCommand
74+
= new RoutedUICommand("Select All",
75+
nameof(SelectAllCommand),
76+
typeof(MultiSelectionComboBox),
77+
new InputGestureCollection() { new KeyGesture(Key.A, ModifierKeys.Control) });
7178

7279
#endregion
7380

@@ -547,6 +554,193 @@ public void ResetEditableText(bool forceUpdate = false)
547554
}
548555
}
549556

557+
558+
/// <summary>
559+
/// Identifies the <see cref="IsDropDownHeaderVisible"/> dependency property.
560+
/// </summary>
561+
public static readonly DependencyProperty IsDropDownHeaderVisibleProperty =
562+
DependencyProperty.Register(nameof(IsDropDownHeaderVisible),
563+
typeof(bool),
564+
typeof(MultiSelectionComboBox),
565+
new PropertyMetadata(BooleanBoxes.FalseBox));
566+
567+
/// <summary>
568+
/// Gets or Sets if the Header in the DropDown is visible
569+
/// </summary>
570+
public bool IsDropDownHeaderVisible
571+
{
572+
get { return (bool)GetValue(IsDropDownHeaderVisibleProperty); }
573+
set { SetValue(IsDropDownHeaderVisibleProperty, value); }
574+
}
575+
576+
577+
/// <summary>
578+
/// Identifies the <see cref="DropDownHeaderContent"/> dependency property.
579+
/// </summary>
580+
public static readonly DependencyProperty DropDownHeaderContentProperty =
581+
DependencyProperty.Register(nameof(DropDownHeaderContent),
582+
typeof(object),
583+
typeof(MultiSelectionComboBox),
584+
new PropertyMetadata(null));
585+
586+
/// <summary>
587+
/// Gets or Sets the content of the DropDown-Header
588+
/// </summary>
589+
public object? DropDownHeaderContent
590+
{
591+
get { return (object?)GetValue(DropDownHeaderContentProperty); }
592+
set { SetValue(DropDownHeaderContentProperty, value); }
593+
}
594+
595+
596+
/// <summary>
597+
/// Identifies the <see cref="DropDownHeaderContentTemplate"/> dependency property.
598+
/// </summary>
599+
public static readonly DependencyProperty DropDownHeaderContentTemplateProperty =
600+
DependencyProperty.Register(nameof(DropDownHeaderContentTemplate),
601+
typeof(DataTemplate),
602+
typeof(MultiSelectionComboBox),
603+
new PropertyMetadata(null));
604+
605+
/// <summary>
606+
/// Gets or Sets the content template of the DropDown-Header
607+
/// </summary>
608+
public DataTemplate? DropDownHeaderContentTemplate
609+
{
610+
get { return (DataTemplate?)GetValue(DropDownHeaderContentTemplateProperty); }
611+
set { SetValue(DropDownHeaderContentTemplateProperty, value); }
612+
}
613+
614+
/// <summary>
615+
/// Identifies the <see cref="DropDownHeaderContentTemplateSelector"/> dependency property.
616+
/// </summary>
617+
public static readonly DependencyProperty DropDownHeaderContentTemplateSelectorProperty =
618+
DependencyProperty.Register(nameof(DropDownHeaderContentTemplateSelector),
619+
typeof(DataTemplateSelector),
620+
typeof(MultiSelectionComboBox),
621+
new PropertyMetadata(null));
622+
623+
/// <summary>
624+
/// Gets or Sets the content template selector of the DropDown-Header
625+
/// </summary>
626+
public DataTemplateSelector? DropDownHeaderContentTemplateSelector
627+
{
628+
get { return (DataTemplateSelector?)GetValue(DropDownHeaderContentTemplateSelectorProperty); }
629+
set { SetValue(DropDownHeaderContentTemplateSelectorProperty, value); }
630+
}
631+
632+
/// <summary>
633+
/// Identifies the <see cref="DropDownHeaderContentStringFormat"/> dependency property.
634+
/// </summary>
635+
public static readonly DependencyProperty DropDownHeaderContentStringFormatProperty =
636+
DependencyProperty.Register(nameof(DropDownHeaderContentStringFormat),
637+
typeof(string),
638+
typeof(MultiSelectionComboBox),
639+
new PropertyMetadata(null));
640+
641+
/// <summary>
642+
/// Gets or Sets the content string format of the DropDown-Header
643+
/// </summary>
644+
public string? DropDownHeaderContentStringFormat
645+
{
646+
get { return (string?)GetValue(DropDownHeaderContentStringFormatProperty); }
647+
set { SetValue(DropDownHeaderContentStringFormatProperty, value); }
648+
}
649+
650+
651+
/// <summary>
652+
/// Identifies the <see cref="IsDropDownFooterVisible"/> dependency property.
653+
/// </summary>
654+
public static readonly DependencyProperty IsDropDownFooterVisibleProperty =
655+
DependencyProperty.Register(nameof(IsDropDownFooterVisible),
656+
typeof(bool),
657+
typeof(MultiSelectionComboBox),
658+
new PropertyMetadata(BooleanBoxes.FalseBox));
659+
660+
/// <summary>
661+
/// Gets or Sets if the Footer in the DropDown is visible
662+
/// </summary>
663+
public bool IsDropDownFooterVisible
664+
{
665+
get { return (bool)GetValue(IsDropDownFooterVisibleProperty); }
666+
set { SetValue(IsDropDownFooterVisibleProperty, value); }
667+
}
668+
669+
670+
/// <summary>
671+
/// Identifies the <see cref="DropDownFooterContent"/> dependency property.
672+
/// </summary>
673+
public static readonly DependencyProperty DropDownFooterContentProperty =
674+
DependencyProperty.Register(nameof(DropDownFooterContent),
675+
typeof(object),
676+
typeof(MultiSelectionComboBox),
677+
new PropertyMetadata(null));
678+
679+
/// <summary>
680+
/// Gets or Sets the content of the DropDown-Footer
681+
/// </summary>
682+
public object? DropDownFooterContent
683+
{
684+
get { return (object?)GetValue(DropDownFooterContentProperty); }
685+
set { SetValue(DropDownFooterContentProperty, value); }
686+
}
687+
688+
689+
/// <summary>
690+
/// Identifies the <see cref="DropDownFooterContentTemplate"/> dependency property.
691+
/// </summary>
692+
public static readonly DependencyProperty DropDownFooterContentTemplateProperty =
693+
DependencyProperty.Register(nameof(DropDownFooterContentTemplate),
694+
typeof(DataTemplate),
695+
typeof(MultiSelectionComboBox),
696+
new PropertyMetadata(null));
697+
698+
/// <summary>
699+
/// Gets or Sets the content template of the DropDown-Footer
700+
/// </summary>
701+
public DataTemplate? DropDownFooterContentTemplate
702+
{
703+
get { return (DataTemplate?)GetValue(DropDownFooterContentTemplateProperty); }
704+
set { SetValue(DropDownFooterContentTemplateProperty, value); }
705+
}
706+
707+
708+
/// <summary>
709+
/// Identifies the <see cref="DropDownFooterContentTemplateSelector"/> dependency property.
710+
/// </summary>
711+
public static readonly DependencyProperty DropDownFooterContentTemplateSelectorProperty =
712+
DependencyProperty.Register(nameof(DropDownFooterContentTemplateSelector),
713+
typeof(DataTemplateSelector),
714+
typeof(MultiSelectionComboBox),
715+
new PropertyMetadata(null));
716+
717+
/// <summary>
718+
/// Gets or Sets the content template selector of the DropDown-Footer
719+
/// </summary>
720+
public DataTemplateSelector? DropDownFooterContentTemplateSelector
721+
{
722+
get { return (DataTemplateSelector?)GetValue(DropDownFooterContentTemplateSelectorProperty); }
723+
set { SetValue(DropDownFooterContentTemplateSelectorProperty, value); }
724+
}
725+
726+
/// <summary>
727+
/// Identifies the <see cref="DropDownFooterContentStringFormat"/> dependency property.
728+
/// </summary>
729+
public static readonly DependencyProperty DropDownFooterContentStringFormatProperty =
730+
DependencyProperty.Register(nameof(DropDownFooterContentStringFormat),
731+
typeof(string),
732+
typeof(MultiSelectionComboBox),
733+
new PropertyMetadata(null));
734+
735+
/// <summary>
736+
/// Gets or Sets the content string format of the DropDown-Footer
737+
/// </summary>
738+
public string? DropDownFooterContentStringFormat
739+
{
740+
get { return (string?)GetValue(DropDownFooterContentStringFormatProperty); }
741+
set { SetValue(DropDownFooterContentStringFormatProperty, value); }
742+
}
743+
550744
#endregion
551745

552746
#region Methods
@@ -1541,7 +1735,7 @@ private void SelectItemHelper(int startIndex, int increment, int stopIndex)
15411735
// IsSelectable and IsEnabled on the item and wrapper.
15421736
var item = this.Items[i];
15431737
var container = this.ItemContainerGenerator.ContainerFromIndex(i);
1544-
if (this.IsSelectableHelper(item) && this.IsSelectableHelper(container))
1738+
if (IsSelectableHelper(item) && IsSelectableHelper(container))
15451739
{
15461740
this.SelectedIndex = i;
15471741
this.UpdateEditableText(true); // We force the update of the text
@@ -1552,7 +1746,7 @@ private void SelectItemHelper(int startIndex, int increment, int stopIndex)
15521746
}
15531747

15541748
// adopted from original ComoBox
1555-
private bool IsSelectableHelper(object o)
1749+
private static bool IsSelectableHelper(object o)
15561750
{
15571751
var d = o as DependencyObject;
15581752
// If o is not a DependencyObject, it is just a plain
@@ -1566,9 +1760,44 @@ private bool IsSelectableHelper(object o)
15661760
return (bool)d.GetValue(IsEnabledProperty);
15671761
}
15681762

1569-
#endregion
1763+
/// <summary>
1764+
/// Select all the items
1765+
/// </summary>
1766+
public void SelectAll()
1767+
{
1768+
PART_PopupListBox?.SelectAll();
1769+
}
1770+
1771+
private static void OnSelectAll(object target, ExecutedRoutedEventArgs args)
1772+
{
1773+
if (target is MultiSelectionComboBox comboBox)
1774+
{
1775+
comboBox.SelectAll();
1776+
}
1777+
}
1778+
1779+
private static void OnQueryStatusSelectAll(object target, CanExecuteRoutedEventArgs args)
1780+
{
1781+
if (target is MultiSelectionComboBox comboBox)
1782+
{
1783+
args.CanExecute
1784+
= comboBox.SelectionMode == SelectionMode.Extended
1785+
&& comboBox.IsDropDownOpen
1786+
&& !(comboBox.PART_EditableTextBox?.IsKeyboardFocused ?? false);
1787+
}
1788+
}
1789+
1790+
/// <summary>
1791+
/// Clears all of the selected items.
1792+
/// </summary>
1793+
public void UnselectAll()
1794+
{
1795+
PART_PopupListBox?.UnselectAll();
1796+
}
1797+
1798+
#endregion
15701799

1571-
#region Events
1800+
#region Events
15721801

15731802
#if NET5_0_OR_GREATER
15741803
private void SelectedItemsImpl_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)

src/MahApps.Metro/Themes/MultiSelectionComboBox.xaml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,19 @@
656656
BorderThickness="{DynamicResource ComboBoxPopupBorderThemeThickness}"
657657
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
658658
<Grid>
659+
<Grid.RowDefinitions>
660+
<RowDefinition Height="Auto" />
661+
<RowDefinition Height="*" />
662+
<RowDefinition Height="Auto" />
663+
</Grid.RowDefinitions>
664+
665+
<ContentControl Content="{TemplateBinding DropDownHeaderContent}"
666+
ContentTemplate="{TemplateBinding DropDownHeaderContentTemplate}"
667+
ContentTemplateSelector="{TemplateBinding DropDownHeaderContentTemplateSelector}"
668+
ContentStringFormat="{TemplateBinding DropDownHeaderContentStringFormat}"
669+
Visibility="{TemplateBinding IsDropDownHeaderVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
670+
Grid.Row="0" />
671+
659672
<ListBox x:Name="PART_PopupListBox"
660673
AlternationCount="{TemplateBinding AlternationCount}"
661674
Focusable="False"
@@ -670,13 +683,23 @@
670683
SelectedValue="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=SelectedValue, Mode=TwoWay}"
671684
SelectedValuePath="{TemplateBinding SelectedValuePath}"
672685
SelectionMode="{TemplateBinding SelectionMode}"
673-
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
686+
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
687+
Grid.Row="1" />
688+
689+
<ContentControl Content="{TemplateBinding DropDownFooterContent}"
690+
ContentTemplate="{TemplateBinding DropDownFooterContentTemplate}"
691+
ContentTemplateSelector="{TemplateBinding DropDownFooterContentTemplateSelector}"
692+
ContentStringFormat="{TemplateBinding DropDownFooterContentStringFormat}"
693+
Visibility="{TemplateBinding IsDropDownFooterVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
694+
Grid.Row="2" />
695+
674696
<ContentControl x:Name="PART_PopupOverlay"
675697
HorizontalAlignment="Stretch"
676698
VerticalAlignment="Stretch"
677699
Content="{TemplateBinding DisabledPopupOverlayContent}"
678700
ContentTemplate="{TemplateBinding DisabledPopupOverlayContentTemplate}"
679-
Visibility="Collapsed" />
701+
Visibility="Collapsed"
702+
Grid.RowSpan="3" />
680703
</Grid>
681704
</Border>
682705
</Grid>

0 commit comments

Comments
 (0)