From ec0cc46e4b84cfa3f0ccfd41643028438bae11c4 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 15 Dec 2020 15:37:59 +0100 Subject: [PATCH 01/23] Implement MultiSelectionComboBox --- .../Controls/Helper/BindingHelper.cs | 93 +++ .../MultiSelectionComboBox.cs | 555 +++++++++++++ .../OrderSelectedItemsBy.cs | 24 + src/MahApps.Metro/Styles/Controls.xaml | 3 + src/MahApps.Metro/Themes/Generic.xaml | 3 + .../Themes/MultiSelectionComboBox.xaml | 730 ++++++++++++++++++ 6 files changed, 1408 insertions(+) create mode 100644 src/MahApps.Metro/Controls/Helper/BindingHelper.cs create mode 100644 src/MahApps.Metro/Controls/MultiSelectionComboBox/MultiSelectionComboBox.cs create mode 100644 src/MahApps.Metro/Controls/MultiSelectionComboBox/OrderSelectedItemsBy.cs create mode 100644 src/MahApps.Metro/Themes/MultiSelectionComboBox.xaml diff --git a/src/MahApps.Metro/Controls/Helper/BindingHelper.cs b/src/MahApps.Metro/Controls/Helper/BindingHelper.cs new file mode 100644 index 0000000000..a4e1083ab8 --- /dev/null +++ b/src/MahApps.Metro/Controls/Helper/BindingHelper.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace MahApps.Metro.Controls.Helper +{ + /// + /// A helper class to evaluate Bindings in code behind + /// + public static class BindingHelper + { + /// + /// A dummy property to initialize the binding to evaluate + /// + private static readonly DependencyProperty DummyProperty = DependencyProperty.RegisterAttached( + "Dummy", + typeof(object), + typeof(BindingHelper), + new UIPropertyMetadata(null)); + + + /// + /// Evaluates a defined -path on the given object + /// + /// the object to evaluate + /// the binding expression to evaluate + /// the result of the + public static object Eval(object source, string expression) + { + Binding binding = new Binding(expression) { Source = source }; + return Eval(binding); + } + + /// + /// Evaluates a defined -path on the given object + /// + /// the object to evaluate + /// the binding expression to evaluate + /// the stringformat to use + /// the result of the + public static object Eval(object source, string expression, string format) + { + Binding binding = new Binding(expression) { Source = source, StringFormat = format }; + return Eval(binding); + } + + /// + /// Evaluates a defined on the given object + /// + /// The to evaluate + /// the object to evaluate + /// + public static object Eval(Binding binding, object source) + { + if (binding is null) throw new ArgumentNullException(nameof(binding)); + + Binding newBinding = new Binding() + { + Source = source, + AsyncState = binding.AsyncState, + BindingGroupName = binding.BindingGroupName, + BindsDirectlyToSource = binding.BindsDirectlyToSource, + Path = binding.Path, + Converter = binding.Converter, + ConverterCulture = binding.ConverterCulture, + ConverterParameter = binding.ConverterParameter, + FallbackValue = binding.FallbackValue, + IsAsync = binding.IsAsync, + Mode = BindingMode.OneWay, + StringFormat = binding.StringFormat, + TargetNullValue = binding.TargetNullValue + }; + return Eval(newBinding); + } + + /// + /// Evaluates a defined on the given + /// + /// The to evaluate + /// optional: The to evalutate + /// + public static object Eval(Binding binding, DependencyObject dependencyObject = null) + { + dependencyObject ??= new DependencyObject(); + BindingOperations.SetBinding(dependencyObject, DummyProperty, binding); + return dependencyObject.GetValue(DummyProperty); + } + } +} diff --git a/src/MahApps.Metro/Controls/MultiSelectionComboBox/MultiSelectionComboBox.cs b/src/MahApps.Metro/Controls/MultiSelectionComboBox/MultiSelectionComboBox.cs new file mode 100644 index 0000000000..fb91872b33 --- /dev/null +++ b/src/MahApps.Metro/Controls/MultiSelectionComboBox/MultiSelectionComboBox.cs @@ -0,0 +1,555 @@ +using ControlzEx; +using MahApps.Metro.Controls.Helper; +using MahApps.Metro.ValueBoxes; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Threading; + +namespace MahApps.Metro.Controls +{ + [TemplatePart(Name = nameof(PART_PopupListBox), Type = typeof(ListBox))] + [TemplatePart(Name = nameof(PART_Popup), Type = typeof(Popup))] + + public class MultiSelectionComboBox : ComboBox + { + #region Constructors + + static MultiSelectionComboBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(typeof(MultiSelectionComboBox))); + } + + #endregion + + //------------------------------------------------------------------- + // + // Private Members + // + //------------------------------------------------------------------- + + #region private Members + + private Popup PART_Popup; + private ListBox PART_PopupListBox; + private TextBox PART_EditableTextBox; + + #endregion + + //------------------------------------------------------------------- + // + // Public Properties + // + //------------------------------------------------------------------- + + #region Public Properties + + /// Identifies the dependency property. + public static readonly DependencyProperty SelectionModeProperty = + DependencyProperty.Register( + nameof(SelectionMode), + typeof(SelectionMode), + typeof(MultiSelectionComboBox), + new PropertyMetadata(SelectionMode.Single), + new ValidateValueCallback(IsValidSelectionMode)); + + /// + /// Indicates the selection behavior for the ListBox. + /// + public SelectionMode SelectionMode + { + get { return (SelectionMode)GetValue(SelectionModeProperty); } + set { SetValue(SelectionModeProperty, value); } + } + + + /// Identifies the dependency property. + public static new readonly DependencyProperty SelectedItemProperty = + DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public new object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + + /// Identifies the dependency property. + public static new readonly DependencyProperty SelectedIndexProperty = + DependencyProperty.Register(nameof(SelectedIndex), typeof(int), typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(-1, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + public new int SelectedIndex + { + get { return (int)GetValue(SelectedIndexProperty); } + set { SetValue(SelectedIndexProperty, value); } + } + + public new object SelectedValue + { + get { return (object)GetValue(SelectedValueProperty); } + set { SetValue(SelectedValueProperty, value); } + } + + /// Identifies the dependency property. + public static new readonly DependencyProperty SelectedValueProperty = + DependencyProperty.Register(nameof(SelectedValue), typeof(object), typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + private static bool IsValidSelectionMode(object o) + { + SelectionMode value = (SelectionMode)o; + return value == SelectionMode.Single + || value == SelectionMode.Multiple + || value == SelectionMode.Extended; + } + + /// Identifies the dependency property. + public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(nameof(SelectedItems), typeof(IList), typeof(MultiSelectionComboBox), new PropertyMetadata((IList)null)); + + /// + /// The currently selected items. + /// + [Bindable(true), Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IList SelectedItems + { + get + { + return PART_PopupListBox?.SelectedItems; + } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty DisplaySelectedItemsProperty = + DependencyProperty.Register(nameof(DisplaySelectedItems), typeof(IEnumerable), typeof(MultiSelectionComboBox), new PropertyMetadata((IEnumerable)null)); + + public IEnumerable DisplaySelectedItems + { + get { return (IEnumerable)GetValue(DisplaySelectedItemsProperty); } + } + + private void UpdateDisplaySelectedItems() + { + switch (OrderSelectedItemsBy) + { + case OrderSelectedItemsBy.SelectedOrder: + SetCurrentValue(DisplaySelectedItemsProperty, SelectedItems); + break; + case OrderSelectedItemsBy.ItemsSourceOrder: + SetCurrentValue(DisplaySelectedItemsProperty, ((IEnumerable)PART_PopupListBox.SelectedItems).OrderBy(o => Items.IndexOf(o))); + break; + } + } + + + /// Identifies the dependency property. + public static readonly DependencyProperty OrderSelectedItemsByProperty = + DependencyProperty.Register(nameof(OrderSelectedItemsBy), typeof(OrderSelectedItemsBy), typeof(MultiSelectionComboBox), new PropertyMetadata(OrderSelectedItemsBy.SelectedOrder, new PropertyChangedCallback(OnOrderSelectedItemsByChanged))); + + public OrderSelectedItemsBy OrderSelectedItemsBy + { + get { return (OrderSelectedItemsBy)GetValue(OrderSelectedItemsByProperty); } + set { SetValue(OrderSelectedItemsByProperty, value); } + } + + private static void OnOrderSelectedItemsByChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is MultiSelectionComboBox multiSelectionComboBox && !multiSelectionComboBox.HasCustomText) + { + multiSelectionComboBox.UpdateDisplaySelectedItems(); + multiSelectionComboBox.UpdateEditableText(); + } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty SeparatorProperty = DependencyProperty.Register(nameof(Separator), typeof(object), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the Separator Content. ToString() will be used if the ComboBox is editable. + /// + public object Separator + { + get { return (object)GetValue(SeparatorProperty); } + set { SetValue(SeparatorProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty SeparatorTemplateProperty = DependencyProperty.Register(nameof(SeparatorTemplate), typeof(DataTemplate), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the SeparatorTemplate. Gets only applied if the MultiselectionComboBox is not editable + /// + public DataTemplate SeparatorTemplate + { + get { return (DataTemplate)GetValue(SeparatorTemplateProperty); } + set { SetValue(SeparatorTemplateProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty HasCustomTextProperty = DependencyProperty.Register(nameof(HasCustomText), typeof(bool), typeof(MultiSelectionComboBox), new PropertyMetadata(false)); + + /// + /// Indicates if the text is userdefined + /// + public bool HasCustomText + { + get { return (bool)GetValue(HasCustomTextProperty); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(typeof(MultiSelectionComboBox), + new FrameworkPropertyMetadata(TextWrapping.NoWrap, FrameworkPropertyMetadataOptions.AffectsMeasure)); + + /// + /// The TextWrapping property controls whether or not text wraps + /// when it reaches the flow edge of its containing block box. + /// + public TextWrapping TextWrapping + { + get { return (TextWrapping)GetValue(TextWrappingProperty); } + set { SetValue(TextWrappingProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty AcceptsReturnProperty = TextBoxBase.AcceptsReturnProperty.AddOwner(typeof(MultiSelectionComboBox)); + + /// + /// The TextWrapping property controls whether or not text wraps + /// when it reaches the flow edge of its containing block box. + /// + public bool AcceptsReturn + { + get { return (bool)GetValue(AcceptsReturnProperty); } + set { SetValue(AcceptsReturnProperty, value); } + } + + /// + /// Updates the Text of the editable Textbox. + /// Sets the custom Text if any otherwise the concatenated string. + /// + public void UpdateEditableText() + { + if (PART_EditableTextBox is null || SelectedItems is null) + return; + + SetCurrentValue(TextProperty, GetSelectedItemsText()); + + UpdateHasCustomText(); + } + + public string GetSelectedItemsText() + { + switch (SelectionMode) + { + case SelectionMode.Single: + if (ReadLocalValue(DisplayMemberPathProperty) != DependencyProperty.UnsetValue || ReadLocalValue(ItemStringFormatProperty) != DependencyProperty.UnsetValue) + { + return BindingHelper.Eval(SelectedItem, DisplayMemberPath ?? "", ItemStringFormat)?.ToString(); + } + else + { + return SelectedItem?.ToString(); + } + + case SelectionMode.Multiple: + case SelectionMode.Extended: + IEnumerable values; + + if (ReadLocalValue(DisplayMemberPathProperty) != DependencyProperty.UnsetValue || ReadLocalValue(ItemStringFormatProperty) != DependencyProperty.UnsetValue) + { + values = ((IEnumerable)DisplaySelectedItems)?.Select(o => BindingHelper.Eval(o, DisplayMemberPath ?? string.Empty, ItemStringFormat)); + } + else + { + values = (IEnumerable)DisplaySelectedItems; + } + + return values is null ? null : string.Join(Separator?.ToString(), values); + + default: + return null; + } + } + + private void UpdateHasCustomText() + { + string selectedItemsText = GetSelectedItemsText(); + + bool hasCustomText = !((string.IsNullOrEmpty(selectedItemsText) && string.IsNullOrEmpty(Text)) || string.Equals(Text, selectedItemsText, StringComparison.Ordinal)); + + SetCurrentValue(HasCustomTextProperty, BooleanBoxes.Box(hasCustomText)); + } + + /// Identifies the dependency property. + public static readonly DependencyProperty DisabledPopupOverlayContentProperty = DependencyProperty.Register(nameof(DisabledPopupOverlayContent), typeof(object), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the DisabledPopupOverlayContent + /// + public object DisabledPopupOverlayContent + { + get { return (object)GetValue(DisabledPopupOverlayContentProperty); } + set { SetValue(DisabledPopupOverlayContentProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty DisabledPopupOverlayContentTemplateProperty = DependencyProperty.Register(nameof(DisabledPopupOverlayContentTemplate), typeof(DataTemplate), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the DisabledPopupOverlayContentTemplate + /// + public DataTemplate DisabledPopupOverlayContentTemplate + { + get { return (DataTemplate)GetValue(DisabledPopupOverlayContentTemplateProperty); } + set { SetValue(DisabledPopupOverlayContentTemplateProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty SelectedItemsTemplateProperty = DependencyProperty.Register(nameof(SelectedItemsTemplate), typeof(DataTemplate), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the SelectedItemsTemplate + /// + public DataTemplate SelectedItemsTemplate + { + get { return (DataTemplate)GetValue(SelectedItemsTemplateProperty); } + set { SetValue(SelectedItemsTemplateProperty, value); } + } + + /// Identifies the dependency property. + public static readonly DependencyProperty SelectedItemsTemplateSelectorProperty = DependencyProperty.Register(nameof(SelectedItemsTemplateSelector), typeof(DataTemplateSelector), typeof(MultiSelectionComboBox), new PropertyMetadata(null)); + + /// + /// Gets or Sets the SelectedItemsTemplateSelector + /// + public DataTemplateSelector SelectedItemsTemplateSelector + { + get { return (DataTemplateSelector)GetValue(SelectedItemsTemplateSelectorProperty); } + set { SetValue(SelectedItemsTemplateSelectorProperty, value); } + } + + #endregion + + #region Commands + + // Clear Text Command + public static RoutedUICommand ClearContentCommand { get; } = new RoutedUICommand("ClearContent", nameof(ClearContentCommand), typeof(MultiSelectionComboBox)); + + private void ExecutedClearContentCommand(object sender, ExecutedRoutedEventArgs e) + { + if (sender is MultiSelectionComboBox multiSelectionCombo) + { + if (multiSelectionCombo.HasCustomText) + { + multiSelectionCombo.UpdateEditableText(); + } + else + { + switch (multiSelectionCombo.SelectionMode) + { + case SelectionMode.Single: + multiSelectionCombo.SelectedItem = null; + break; + case SelectionMode.Multiple: + case SelectionMode.Extended: + multiSelectionCombo.SelectedItems.Clear(); + break; + } + } + } + } + + private void CanExecuteClearContentCommand(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = false; + if (sender is MultiSelectionComboBox multiSelectionComboBox) + { + e.CanExecute = multiSelectionComboBox.Text != null || multiSelectionComboBox.SelectedItems?.Count > 0; + } + } + + public static RoutedUICommand RemoveItemCommand { get; } = new RoutedUICommand("Remove item", nameof(RemoveItemCommand), typeof(MultiSelectionComboBox)); + + private void RemoveItemCommand_Executed(object sender, ExecutedRoutedEventArgs e) + { + if (sender is MultiSelectionComboBox multiSelectionCombo && multiSelectionCombo.SelectedItems.Contains(e.Parameter)) + { + if (multiSelectionCombo.SelectionMode == SelectionMode.Single) + { + multiSelectionCombo.SelectedItem = null; + return; + } + multiSelectionCombo.SelectedItems.Remove(e.Parameter); + } + } + + private void RemoveItemCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = false; + if (sender is MultiSelectionComboBox) + { + e.CanExecute = e.Parameter != null; + } + } + + #endregion + + #region Overrides + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + PART_EditableTextBox = GetTemplateChild(nameof(PART_EditableTextBox)) as TextBox; + PART_EditableTextBox.TextChanged += PART_EditableTextBox_TextChanged; + + PART_Popup = GetTemplateChild(nameof(PART_Popup)) as Popup; + + PART_PopupListBox = GetTemplateChild(nameof(PART_PopupListBox)) as ListBox; + PART_PopupListBox.SelectionChanged += PART_PopupListBox_SelectionChanged; + + CommandBindings.Add(new CommandBinding(ClearContentCommand, ExecutedClearContentCommand, CanExecuteClearContentCommand)); + CommandBindings.Add(new CommandBinding(RemoveItemCommand, RemoveItemCommand_Executed, RemoveItemCommand_CanExecute)); + + // Do update the text + UpdateEditableText(); + UpdateDisplaySelectedItems(); + } + + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + base.OnSelectionChanged(e); + UpdateEditableText(); + UpdateDisplaySelectedItems(); + } + + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + + if (!IsLoaded) + { + Loaded += MultiSelectionComboBox_Loaded; + return; + } + + // If we have the ItemsSource set, we need to exit here. + if (ReadLocalValue(ItemsSourceProperty) != DependencyProperty.UnsetValue) return; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var item in e.NewItems) + { + PART_PopupListBox.Items.Add(item); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems) + { + PART_PopupListBox.Items.Remove(item); + } + break; + + case NotifyCollectionChangedAction.Replace: + // TODO Add Handler + break; + case NotifyCollectionChangedAction.Move: + // TODO Add Handler + break; + case NotifyCollectionChangedAction.Reset: + PART_PopupListBox.Items.Clear(); + foreach (var item in Items) + { + PART_PopupListBox.Items.Add(item); + } + break; + default: + break; + } + } + + private void MultiSelectionComboBox_Loaded(object sender, EventArgs e) + { + Loaded -= MultiSelectionComboBox_Loaded; + + // If we have the ItemsSource set, we need to exit here. + if (ReadLocalValue(ItemsSourceProperty) != DependencyProperty.UnsetValue) return; + + PART_PopupListBox.Items.Clear(); + foreach (var item in Items) + { + PART_PopupListBox.Items.Add(item); + } + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + + // For now we only want to update our poition if the height changed. Else we will get a flickering in SharedGridColumns + if (IsDropDownOpen && sizeInfo.HeightChanged && !(PART_Popup is null)) + { + this.Dispatcher.BeginInvoke(DispatcherPriority.Background, + (DispatcherOperationCallback)((object arg) => + { + MultiSelectionComboBox mscb = (MultiSelectionComboBox)arg; + mscb.PART_Popup.HorizontalOffset++; + mscb.PART_Popup.HorizontalOffset--; + + return null; + }), this); + } + } + + protected override void OnDropDownOpened(EventArgs e) + { + base.OnDropDownOpened(e); + + + PART_PopupListBox.Focus(); + + if (PART_PopupListBox.Items.Count == 0) return; + + var index = PART_PopupListBox.SelectedIndex; + if (index < 0) index = 0; + + Action action = () => + { + PART_PopupListBox.ScrollIntoView(PART_PopupListBox.SelectedItem); + + if (PART_PopupListBox.ItemContainerGenerator.ContainerFromIndex(index) is ListBoxItem item) + { + item.Focus(); + KeyboardNavigationEx.Focus(item); + } + }; + + Dispatcher.BeginInvoke(DispatcherPriority.Background, action); + } + + #endregion + + #region Events + + private void PART_EditableTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + UpdateHasCustomText(); + } + + private void PART_PopupListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + UpdateDisplaySelectedItems(); + UpdateEditableText(); + } + + #endregion + } +} diff --git a/src/MahApps.Metro/Controls/MultiSelectionComboBox/OrderSelectedItemsBy.cs b/src/MahApps.Metro/Controls/MultiSelectionComboBox/OrderSelectedItemsBy.cs new file mode 100644 index 0000000000..af59e40a29 --- /dev/null +++ b/src/MahApps.Metro/Controls/MultiSelectionComboBox/OrderSelectedItemsBy.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MahApps.Metro.Controls +{ + /// + /// Defines how the selected Items should be arranged for display + /// + public enum OrderSelectedItemsBy + { + /// + /// Displays the selected items in the same order as they were selected + /// + SelectedOrder, + + /// + /// Displays the selected items in the same order as they are stored in the ItemsSource + /// + ItemsSourceOrder + } +} diff --git a/src/MahApps.Metro/Styles/Controls.xaml b/src/MahApps.Metro/Styles/Controls.xaml index 62927f8b96..64fd3cb4c9 100644 --- a/src/MahApps.Metro/Styles/Controls.xaml +++ b/src/MahApps.Metro/Styles/Controls.xaml @@ -53,6 +53,9 @@ + + + diff --git a/src/MahApps.Metro/Themes/Generic.xaml b/src/MahApps.Metro/Themes/Generic.xaml index a296f451b9..bf8d86906c 100644 --- a/src/MahApps.Metro/Themes/Generic.xaml +++ b/src/MahApps.Metro/Themes/Generic.xaml @@ -44,6 +44,7 @@ + @@ -74,4 +75,6 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 5ac9b60f2e3ecf211e3ba63d41eaa5eff8582096 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 16 Dec 2020 10:06:43 +0100 Subject: [PATCH 02/23] Add MultiSelectionComboBox-Demo - Add ToString() for the sample data - ClearTextButton - AutoWatermark --- .../MultiSelectionComboBoxExample.xaml | 113 ++++++++++++++++++ .../MultiSelectionComboBoxExample.xaml.cs | 28 +++++ .../MahApps.Metro.Demo/MainWindow.xaml | 64 +++++----- .../MahApps.Metro.Demo/MainWindowViewModel.cs | 9 ++ .../MahApps.Metro.Demo/Models/Album.cs | 7 ++ .../MahApps.Metro.Demo/Models/Artist.cs | 5 + .../MahApps.Metro.Demo/Models/Genre.cs | 5 + .../Controls/Helper/TextBoxHelper.cs | 38 +++++- .../Themes/MultiSelectionComboBox.xaml | 3 +- 9 files changed, 238 insertions(+), 34 deletions(-) create mode 100644 src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml create mode 100644 src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml.cs diff --git a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml new file mode 100644 index 0000000000..dd90feb22d --- /dev/null +++ b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Title + Artist + Genre + + + + + + + + + + + + + + + + diff --git a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml.cs b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml.cs new file mode 100644 index 0000000000..7363a7ced5 --- /dev/null +++ b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/MultiSelectionComboBoxExample.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace MetroDemo.ExampleViews +{ + /// + /// Interaction logic for MultiSelectionComboBoxExample.xaml + /// + public partial class MultiSelectionComboBoxExample : UserControl + { + public MultiSelectionComboBoxExample() + { + InitializeComponent(); + } + } +} diff --git a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/MainWindow.xaml b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/MainWindow.xaml index a524d41798..5fc0b71533 100644 --- a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/MainWindow.xaml +++ b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/MainWindow.xaml @@ -80,8 +80,7 @@ - @@ -91,12 +90,12 @@ - -