Skip to content

Commit f3652ba

Browse files
committed
Correct implementation for readonly DependcyProperties
Additionally make these minor improvements - make Command fully static - restore selection after clear text - better check if ItemsSource is in use (PopupListBox)
1 parent b4ca923 commit f3652ba

File tree

6 files changed

+92
-48
lines changed

6 files changed

+92
-48
lines changed

src/MahApps.Metro.Samples/MahApps.Metro.Demo/MainWindowViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ private void ToggleIconScaling(object obj)
537537

538538
public bool IsToggleSwitchVisible { get; set; }
539539

540-
public ObservableCollection<string> Animals { get; } = new ObservableCollection<string>()
540+
public ObservableCollection<string> Animals { get; } = new ObservableCollection<string>
541541
{
542542
"African elephant",
543543
"Ant",

src/MahApps.Metro.Samples/MahApps.Metro.Demo/Models/ObjectParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public object CreateObjectFromString(string input, CultureInfo culture, string s
3232
return null;
3333
}
3434

35-
MetroDialogSettings dialogSettings = new MetroDialogSettings()
35+
MetroDialogSettings dialogSettings = new MetroDialogSettings
3636
{
3737
AffirmativeButtonText = "Yes",
3838
NegativeButtonText = "No",

src/MahApps.Metro/Controls/Helper/BindingHelper.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ public static object Eval(object source, string expression, string format)
6464
/// <returns></returns>
6565
public static object Eval(Binding binding, object source)
6666
{
67-
if (binding is null) throw new ArgumentNullException(nameof(binding));
67+
if (binding is null)
68+
{
69+
throw new ArgumentNullException(nameof(binding));
70+
}
6871

69-
Binding newBinding = new Binding()
72+
Binding newBinding = new Binding
7073
{
7174
Source = source,
7275
AsyncState = binding.AsyncState,
@@ -91,7 +94,7 @@ public static object Eval(Binding binding, object source)
9194
/// <param name="binding">The <see cref="Binding"/> to evaluate</param>
9295
/// <param name="dependencyObject">optional: The <see cref="DependencyObject"/> to evalutate</param>
9396
/// <returns>The resulting object</returns>
94-
public static object Eval(Binding binding, DependencyObject dependencyObject = null)
97+
public static object Eval(Binding binding, DependencyObject dependencyObject)
9598
{
9699
dependencyObject ??= new DependencyObject();
97100

@@ -106,5 +109,15 @@ public static object Eval(Binding binding, DependencyObject dependencyObject = n
106109
return dependencyObject.GetValue(DummyTextProperty);
107110
}
108111
}
112+
113+
/// <summary>
114+
/// Evaluates a defined <see cref="Binding"/> on the given <see cref="DependencyObject"/>
115+
/// </summary>
116+
/// <param name="binding">The <see cref="Binding"/> to evaluate</param>
117+
/// <returns>The resulting object</returns>
118+
public static object Eval(Binding binding)
119+
{
120+
return Eval(binding, null);
121+
}
109122
}
110123
}

src/MahApps.Metro/Controls/Helper/TextBoxHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,8 @@ public static void ButtonClicked(object sender, RoutedEventArgs e)
954954
case SelectionMode.Extended:
955955
multiSelectionComboBox.SelectedItems.Clear();
956956
break;
957+
default:
958+
throw new NotSupportedException("Unknown SelectionMode");
957959
}
958960
}
959961
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public interface ICompareObjectToString
2828
[MarkupExtensionReturnType(typeof(DefaultObjectToStringComparer))]
2929
public class DefaultObjectToStringComparer : MarkupExtension, ICompareObjectToString
3030
{
31-
static DefaultObjectToStringComparer _Instance;
31+
private static DefaultObjectToStringComparer _Instance;
3232

3333
/// <inheritdoc/>
3434
public bool CheckIfStringMatchesObject(string input, object objectToCompare, StringComparison stringComparison, string stringFormat)
@@ -48,7 +48,7 @@ public bool CheckIfStringMatchesObject(string input, object objectToCompare, Str
4848
{
4949
objectText = objectToCompare.ToString();
5050
}
51-
else if (stringFormat.Contains('{') && stringFormat.Contains('{'))
51+
else if (stringFormat.Contains('{') && stringFormat.Contains('}'))
5252
{
5353
objectText = string.Format(stringFormat, objectToCompare);
5454
}

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

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ static MultiSelectionComboBox()
3333
{
3434
DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(typeof(MultiSelectionComboBox)));
3535
TextProperty.OverrideMetadata(typeof(MultiSelectionComboBox), new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnTextChanged)));
36+
CommandManager.RegisterClassCommandBinding(typeof(MultiSelectionComboBox), new CommandBinding(ClearContentCommand, ExecutedClearContentCommand, CanExecuteClearContentCommand));
37+
CommandManager.RegisterClassCommandBinding(typeof(MultiSelectionComboBox), new CommandBinding(RemoveItemCommand, RemoveItemCommand_Executed, RemoveItemCommand_CanExecute));
3638
}
3739

3840
#endregion
@@ -138,40 +140,47 @@ private static bool IsValidSelectionMode(object o)
138140
|| value == SelectionMode.Extended;
139141
}
140142

141-
/// <summary>Identifies the <see cref="SelectedItems"/> dependency property.</summary>
142-
public static readonly DependencyProperty SelectedItemsProperty =
143-
DependencyProperty.Register(
143+
144+
internal static readonly DependencyPropertyKey SelectedItemsPropertyKey =
145+
DependencyProperty.RegisterReadOnly(
144146
nameof(SelectedItems),
145147
typeof(IList),
146148
typeof(MultiSelectionComboBox),
147149
new PropertyMetadata((IList)null));
148150

151+
/// <summary>Identifies the <see cref="SelectedItems"/> dependency property.</summary>
152+
public static readonly DependencyProperty SelectedItemsProperty = SelectedItemsPropertyKey.DependencyProperty;
153+
154+
149155
/// <summary>
150156
/// The currently selected items.
151157
/// </summary>
152158
[Bindable(true), Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
153159
public IList SelectedItems
154160
{
155-
get
156-
{
157-
return PART_PopupListBox?.SelectedItems;
158-
}
161+
get { return (IList)GetValue(SelectedItemsProperty); }
162+
protected set { SetValue(SelectedItemsPropertyKey, value); }
159163
}
160164

161-
/// <summary>Identifies the <see cref="DisplaySelectedItems"/> dependency property.</summary>
162-
public static readonly DependencyProperty DisplaySelectedItemsProperty =
163-
DependencyProperty.Register(
165+
166+
167+
internal static readonly DependencyPropertyKey DisplaySelectedItemsPropertyKey =
168+
DependencyProperty.RegisterReadOnly(
164169
nameof(DisplaySelectedItems),
165170
typeof(IEnumerable),
166171
typeof(MultiSelectionComboBox),
167172
new PropertyMetadata((IEnumerable)null));
168173

174+
/// <summary>Identifies the <see cref="DisplaySelectedItems"/> dependency property.</summary>
175+
public static readonly DependencyProperty DisplaySelectedItemsProperty = DisplaySelectedItemsPropertyKey.DependencyProperty;
176+
169177
/// <summary>
170178
/// Gets the <see cref="SelectedItems"/> in the specified order which was set via <see cref="OrderSelectedItemsBy"/>
171179
/// </summary>
172180
public IEnumerable DisplaySelectedItems
173181
{
174182
get { return (IEnumerable)GetValue(DisplaySelectedItemsProperty); }
183+
protected set { SetValue(DisplaySelectedItemsPropertyKey, value); }
175184
}
176185

177186
/// <summary>Identifies the <see cref="OrderSelectedItemsBy"/> dependency property.</summary>
@@ -246,20 +255,25 @@ public string Separator
246255
set { SetValue(SeparatorProperty, value); }
247256
}
248257

249-
/// <summary>Identifies the <see cref="HasCustomText"/> dependency property.</summary>
250-
public static readonly DependencyProperty HasCustomTextProperty =
251-
DependencyProperty.Register(
258+
259+
internal static readonly DependencyPropertyKey HasCustomTextPropertyKey =
260+
DependencyProperty.RegisterReadOnly(
252261
nameof(HasCustomText),
253262
typeof(bool),
254263
typeof(MultiSelectionComboBox),
255264
new PropertyMetadata(false));
256265

266+
267+
/// <summary>Identifies the <see cref="HasCustomText"/> dependency property.</summary>
268+
public static readonly DependencyProperty HasCustomTextProperty = HasCustomTextPropertyKey.DependencyProperty;
269+
257270
/// <summary>
258271
/// Indicates if the text is userdefined
259272
/// </summary>
260273
public bool HasCustomText
261274
{
262275
get { return (bool)GetValue(HasCustomTextProperty); }
276+
protected set { SetValue(HasCustomTextPropertyKey, BooleanBoxes.Box(value)); }
263277
}
264278

265279
/// <summary>Identifies the <see cref="TextWrapping"/> dependency property.</summary>
@@ -351,8 +365,14 @@ public IParseStringToObject StringToObjectParser
351365
/// </summary>
352366
public void ResetEditableText()
353367
{
354-
SetCurrentValue(HasCustomTextProperty, BooleanBoxes.FalseBox);
368+
var oldSelectionStart = PART_EditableTextBox.SelectionStart;
369+
var oldSelectionLength = PART_EditableTextBox.SelectionLength;
370+
371+
HasCustomText = false;
355372
UpdateEditableText();
373+
374+
PART_EditableTextBox.SelectionStart = oldSelectionStart;
375+
PART_EditableTextBox.SelectionLength = oldSelectionLength;
356376
}
357377

358378
/// <summary>Identifies the <see cref="DisabledPopupOverlayContent"/> dependency property.</summary>
@@ -519,7 +539,9 @@ public int SelectItemsFromTextInputDelay
519539
private void UpdateEditableText()
520540
{
521541
if (PART_EditableTextBox is null || SelectedItems is null)
542+
{
522543
return;
544+
}
523545

524546
var selectedItemsText = GetSelectedItemsText();
525547

@@ -578,19 +600,18 @@ private void UpdateHasCustomText(string selectedItemsText)
578600

579601
bool hasCustomText = !((string.IsNullOrEmpty(selectedItemsText) && string.IsNullOrEmpty(Text)) || string.Equals(Text, selectedItemsText, EditableTextStringComparision));
580602

581-
SetCurrentValue(HasCustomTextProperty, BooleanBoxes.Box(hasCustomText));
603+
HasCustomText = hasCustomText;
582604
}
583605

584606
private void UpdateDisplaySelectedItems(OrderSelectedItemsBy orderBy)
585607
{
586-
switch (orderBy)
608+
if (orderBy == OrderSelectedItemsBy.SelectedOrder)
587609
{
588-
case OrderSelectedItemsBy.SelectedOrder:
589-
SetCurrentValue(DisplaySelectedItemsProperty, SelectedItems);
590-
break;
591-
case OrderSelectedItemsBy.ItemsSourceOrder:
592-
SetCurrentValue(DisplaySelectedItemsProperty, ((IEnumerable<object>)PART_PopupListBox.SelectedItems).OrderBy(o => Items.IndexOf(o)));
593-
break;
610+
DisplaySelectedItems = SelectedItems;
611+
}
612+
else if (orderBy == OrderSelectedItemsBy.ItemsSourceOrder)
613+
{
614+
DisplaySelectedItems= ((IEnumerable<object>)SelectedItems).OrderBy(o => Items.IndexOf(o));
594615
}
595616
}
596617

@@ -638,7 +659,7 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
638659
SelectedItems.Clear();
639660
break;
640661
default:
641-
break;
662+
throw new NotSupportedException("Unknown SelectionMode");
642663
}
643664
return;
644665
}
@@ -671,7 +692,7 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
671692
case SelectionMode.Multiple:
672693
case SelectionMode.Extended:
673694

674-
var strings = Text.Split(new string[] { Separator }, StringSplitOptions.RemoveEmptyEntries);
695+
var strings = Text.Split(new [] { Separator }, StringSplitOptions.RemoveEmptyEntries);
675696

676697
SelectedItems.Clear();
677698

@@ -698,7 +719,7 @@ private void UpdateSelectedItemsFromTextTimer_Tick(object sender, EventArgs e)
698719
}
699720
break;
700721
default:
701-
break;
722+
throw new NotSupportedException("Unknown SelectionMode");
702723
}
703724

704725

@@ -752,7 +773,7 @@ private object TryAddObjectFromString(string input)
752773
// Clear Text Command
753774
public static RoutedUICommand ClearContentCommand { get; } = new RoutedUICommand("ClearContent", nameof(ClearContentCommand), typeof(MultiSelectionComboBox));
754775

755-
private void ExecutedClearContentCommand(object sender, ExecutedRoutedEventArgs e)
776+
private static void ExecutedClearContentCommand(object sender, ExecutedRoutedEventArgs e)
756777
{
757778
if (sender is MultiSelectionComboBox multiSelectionCombo)
758779
{
@@ -771,12 +792,14 @@ private void ExecutedClearContentCommand(object sender, ExecutedRoutedEventArgs
771792
case SelectionMode.Extended:
772793
multiSelectionCombo.SelectedItems.Clear();
773794
break;
795+
default:
796+
throw new NotSupportedException("Unknown SelectionMode");
774797
}
775798
}
776799
}
777800
}
778801

779-
private void CanExecuteClearContentCommand(object sender, CanExecuteRoutedEventArgs e)
802+
private static void CanExecuteClearContentCommand(object sender, CanExecuteRoutedEventArgs e)
780803
{
781804
e.CanExecute = false;
782805
if (sender is MultiSelectionComboBox multiSelectionComboBox)
@@ -787,7 +810,7 @@ private void CanExecuteClearContentCommand(object sender, CanExecuteRoutedEventA
787810

788811
public static RoutedUICommand RemoveItemCommand { get; } = new RoutedUICommand("Remove item", nameof(RemoveItemCommand), typeof(MultiSelectionComboBox));
789812

790-
private void RemoveItemCommand_Executed(object sender, ExecutedRoutedEventArgs e)
813+
private static void RemoveItemCommand_Executed(object sender, ExecutedRoutedEventArgs e)
791814
{
792815
if (sender is MultiSelectionComboBox multiSelectionCombo && multiSelectionCombo.SelectedItems.Contains(e.Parameter))
793816
{
@@ -800,7 +823,7 @@ private void RemoveItemCommand_Executed(object sender, ExecutedRoutedEventArgs e
800823
}
801824
}
802825

803-
private void RemoveItemCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
826+
private static void RemoveItemCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
804827
{
805828
e.CanExecute = false;
806829
if (sender is MultiSelectionComboBox)
@@ -857,15 +880,13 @@ public override void OnApplyTemplate()
857880
{
858881
PART_PopupListBox.SelectionChanged -= PART_PopupListBox_SelectionChanged;
859882
PART_PopupListBox.SelectionChanged += PART_PopupListBox_SelectionChanged;
883+
SelectedItems = PART_PopupListBox.SelectedItems;
860884
}
861885
else
862886
{
863887
throw new MahAppsException($"The template part \"{nameof(PART_PopupListBox)}\" could not be found.");
864888
}
865889

866-
CommandBindings.Add(new CommandBinding(ClearContentCommand, ExecutedClearContentCommand, CanExecuteClearContentCommand));
867-
CommandBindings.Add(new CommandBinding(RemoveItemCommand, RemoveItemCommand_Executed, RemoveItemCommand_CanExecute));
868-
869890
// Do update the text
870891
UpdateEditableText();
871892
UpdateDisplaySelectedItems();
@@ -889,7 +910,10 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
889910
}
890911

891912
// If we have the ItemsSource set, we need to exit here.
892-
if (ReadLocalValue(ItemsSourceProperty) != DependencyProperty.UnsetValue) return;
913+
if ((PART_PopupListBox?.Items as IList)?.IsReadOnly ?? true)
914+
{
915+
return;
916+
}
893917

894918
switch (e.Action)
895919
{
@@ -908,11 +932,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
908932
break;
909933

910934
case NotifyCollectionChangedAction.Replace:
911-
// TODO Add Handler
912-
break;
913935
case NotifyCollectionChangedAction.Move:
914-
// TODO Add Handler
915-
break;
916936
case NotifyCollectionChangedAction.Reset:
917937
PART_PopupListBox.Items.Clear();
918938
foreach (var item in Items)
@@ -921,7 +941,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
921941
}
922942
break;
923943
default:
924-
break;
944+
throw new NotSupportedException("Unsupported NotifyCollectionChangedAction");
925945
}
926946
}
927947

@@ -951,10 +971,16 @@ protected override void OnDropDownOpened(EventArgs e)
951971

952972
PART_PopupListBox.Focus();
953973

954-
if (PART_PopupListBox.Items.Count == 0) return;
974+
if (PART_PopupListBox.Items.Count == 0)
975+
{
976+
return;
977+
}
955978

956979
var index = PART_PopupListBox.SelectedIndex;
957-
if (index < 0) index = 0;
980+
if (index < 0)
981+
{
982+
index = 0;
983+
}
958984

959985
Action action = () =>
960986
{
@@ -1052,7 +1078,10 @@ private void MultiSelectionComboBox_Loaded(object sender, EventArgs e)
10521078
Loaded -= MultiSelectionComboBox_Loaded;
10531079

10541080
// If we have the ItemsSource set, we need to exit here.
1055-
if (ReadLocalValue(ItemsSourceProperty) != DependencyProperty.UnsetValue) return;
1081+
if ((PART_PopupListBox?.Items as IList)?.IsReadOnly ?? true)
1082+
{
1083+
return;
1084+
}
10561085

10571086
PART_PopupListBox.Items.Clear();
10581087
foreach (var item in Items)

0 commit comments

Comments
 (0)