Skip to content

Commit 470cd94

Browse files
authored
Fix for 2495 (#2502)
1 parent fbc9705 commit 470cd94

File tree

11 files changed

+223
-52
lines changed

11 files changed

+223
-52
lines changed

MainDemo.Wpf/Pickers.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<DatePicker
5151
Width="100"
5252
materialDesign:HintAssist.Hint="Pick Date"
53+
materialDesign:TextFieldAssist.HasClearButton="True"
5354
Style="{StaticResource MaterialDesignFloatingHintDatePicker}"/>
5455
</smtx:XamlDisplay>
5556

MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,44 @@ public async Task OnFilledComboBoxHelperTextFontSize_ChangesHelperTextFontSize()
5454
Assert.Equal(20, fontSize);
5555
recorder.Success();
5656
}
57+
58+
[Fact]
59+
[Description("Issue 2495")]
60+
public async Task OnComboBox_WithClearButton_ClearsSelection()
61+
{
62+
await using var recorder = new TestRecorder(App);
63+
64+
var stackPanel = await LoadXaml<StackPanel>($@"
65+
<StackPanel>
66+
<ComboBox materialDesign:HintAssist.Hint=""OS""
67+
materialDesign:TextFieldAssist.HasClearButton=""True""
68+
SelectedIndex=""1"">
69+
<ComboBoxItem Content=""Android"" />
70+
<ComboBoxItem Content=""iOS"" />
71+
<ComboBoxItem Content=""Linux"" />
72+
<ComboBoxItem Content=""Windows"" />
73+
</ComboBox>
74+
</StackPanel>");
75+
var comboBox = await stackPanel.GetElement<ComboBox>("/ComboBox");
76+
var clearButton = await comboBox.GetElement<Button>("PART_ClearButton");
77+
78+
int? selectedIndex = await comboBox.GetSelectedIndex();
79+
object? text = await comboBox.GetText();
80+
81+
Assert.True(selectedIndex >= 0);
82+
Assert.NotNull(text);
83+
84+
await clearButton.LeftClick();
85+
86+
await Wait.For(async () =>
87+
{
88+
text = await comboBox.GetText();
89+
Assert.Null(text);
90+
selectedIndex = await comboBox.GetSelectedIndex();
91+
Assert.False(selectedIndex >= 0);
92+
});
93+
94+
recorder.Success();
95+
}
5796
}
5897
}

MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.ComponentModel;
1+
using System;
2+
using System.ComponentModel;
23
using System.Threading.Tasks;
34
using System.Windows.Controls;
45
using XamlTest;
@@ -19,7 +20,7 @@ public DatePickerTests(ITestOutputHelper output)
1920
public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize()
2021
{
2122
await using var recorder = new TestRecorder(App);
22-
23+
2324
var stackPanel = await LoadXaml<StackPanel>(@"
2425
<StackPanel>
2526
<DatePicker materialDesign:HintAssist.HelperTextFontSize=""20""/>
@@ -33,5 +34,33 @@ public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize()
3334
Assert.Equal(20, fontSize);
3435
recorder.Success();
3536
}
37+
38+
[Fact]
39+
[Description("Issue 2495")]
40+
public async Task OnDatePicker_WithClearButton_ClearsSelectedDate()
41+
{
42+
await using var recorder = new TestRecorder(App);
43+
44+
var stackPanel = await LoadXaml<StackPanel>($@"
45+
<StackPanel>
46+
<DatePicker SelectedDate=""{DateTime.Today:d}"" materialDesign:TextFieldAssist.HasClearButton=""True""/>
47+
</StackPanel>");
48+
var datePicker = await stackPanel.GetElement<DatePicker>("/DatePicker");
49+
var clearButton = await datePicker.GetElement<Button>("PART_ClearButton");
50+
51+
DateTime? selectedDate = await datePicker.GetSelectedDate();
52+
53+
Assert.NotNull(selectedDate);
54+
55+
await clearButton.LeftClick();
56+
57+
await Wait.For(async () =>
58+
{
59+
selectedDate = await datePicker.GetSelectedDate();
60+
Assert.Null(selectedDate);
61+
});
62+
63+
recorder.Success();
64+
}
3665
}
3766
}

MaterialDesignThemes.UITests/WPF/PasswordBoxes/PasswordBoxTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using XamlTest;
55
using Xunit;
66
using Xunit.Abstractions;
7-
using Controls = System.Windows.Controls;
87

98
namespace MaterialDesignThemes.UITests.WPF.PasswordBoxes
109
{
@@ -57,5 +56,35 @@ public async Task OnPasswordBoxHelperTextFontSize_ChangesHelperTextFontSize()
5756
Assert.Equal(20, fontSize);
5857
recorder.Success();
5958
}
59+
60+
[Fact]
61+
[Description("Issue 2495")]
62+
public async Task OnPasswordBox_WithClearButton_ClearsPassword()
63+
{
64+
await using var recorder = new TestRecorder(App);
65+
66+
var grid = await LoadXaml<Grid>(@"
67+
<Grid Margin=""30"">
68+
<PasswordBox materialDesign:TextFieldAssist.HasClearButton=""True"" />
69+
</Grid>");
70+
var passwordBox = await grid.GetElement<PasswordBox>("/PasswordBox");
71+
var clearButton = await passwordBox.GetElement<Button>("PART_ClearButton");
72+
73+
await passwordBox.SendKeyboardInput($"Test");
74+
75+
string? password = await passwordBox.GetPassword();
76+
77+
Assert.NotNull(password);
78+
79+
await clearButton.LeftClick();
80+
81+
await Wait.For(async () =>
82+
{
83+
password = await passwordBox.GetPassword();
84+
Assert.Null(password);
85+
});
86+
87+
recorder.Success();
88+
}
6089
}
6190
}

MaterialDesignThemes.UITests/WPF/TextBoxes/TextBoxTests.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public TextBoxTests(ITestOutputHelper output)
2121

2222
[Fact]
2323
[Description("Issue 1883")]
24-
public async Task OnClearButtonShown_ControlHeighDoesNotChange()
24+
public async Task OnClearButtonShown_ControlHeightDoesNotChange()
2525
{
2626
await using var recorder = new TestRecorder(App);
2727

@@ -56,7 +56,7 @@ public async Task OnClearButtonShown_ControlHeighDoesNotChange()
5656

5757
[Fact]
5858
[Description("Issue 1883")]
59-
public async Task OnClearButtonWithHintShown_ControlHeighDoesNotChange()
59+
public async Task OnClearButtonWithHintShown_ControlHeightDoesNotChange()
6060
{
6161
await using var recorder = new TestRecorder(App);
6262

@@ -125,6 +125,37 @@ public async Task OnTextCleared_MultilineTextBox()
125125
recorder.Success();
126126
}
127127

128+
[Fact]
129+
[Description("Issue 2495")]
130+
public async Task OnTextBox_WithClearButton_ClearsText()
131+
{
132+
await using var recorder = new TestRecorder(App);
133+
134+
var grid = await LoadXaml<Grid>(@"
135+
<Grid Margin=""30"">
136+
<TextBox VerticalAlignment=""Top""
137+
Text=""Some Text""
138+
materialDesign:TextFieldAssist.HasClearButton=""True"">
139+
</TextBox>
140+
</Grid>");
141+
var textBox = await grid.GetElement<TextBox>("/TextBox");
142+
var clearButton = await textBox.GetElement<Button>("PART_ClearButton");
143+
144+
string? text = await textBox.GetText();
145+
146+
Assert.NotNull(text);
147+
148+
await clearButton.LeftClick();
149+
150+
await Wait.For(async () =>
151+
{
152+
text = await textBox.GetText();
153+
Assert.Null(text);
154+
});
155+
156+
recorder.Success();
157+
}
158+
128159
[Fact]
129160
[Description("Issue 2002")]
130161
public async Task OnTextBoxDisabled_FloatingHintBackgroundIsOpaque()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Windows;
2+
using System.Windows.Controls;
3+
using System.Windows.Controls.Primitives;
4+
using System.Windows.Input;
5+
6+
namespace MaterialDesignThemes.Wpf.Internal
7+
{
8+
public static class ClearText
9+
{
10+
public static readonly RoutedCommand ClearCommand = new();
11+
12+
public static bool GetHandlesClearCommand(DependencyObject obj)
13+
=> (bool)obj.GetValue(HandlesClearCommandProperty);
14+
15+
public static void SetHandlesClearCommand(DependencyObject obj, bool value)
16+
=> obj.SetValue(HandlesClearCommandProperty, value);
17+
18+
public static readonly DependencyProperty HandlesClearCommandProperty =
19+
DependencyProperty.RegisterAttached("HandlesClearCommand", typeof(bool), typeof(ClearText), new PropertyMetadata(false, OnHandlesClearCommandChanged));
20+
21+
private static void OnHandlesClearCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
22+
{
23+
if (d is UIElement element)
24+
{
25+
if ((bool)e.NewValue)
26+
{
27+
element.CommandBindings.Add(new CommandBinding(ClearCommand, OnClearCommand));
28+
}
29+
else
30+
{
31+
for (int i = element.CommandBindings.Count - 1; i >= 0; i--)
32+
{
33+
if (element.CommandBindings[i].Command == ClearCommand)
34+
{
35+
element.CommandBindings.RemoveAt(i);
36+
}
37+
}
38+
}
39+
}
40+
41+
static void OnClearCommand(object sender, ExecutedRoutedEventArgs e)
42+
{
43+
switch (e.Source)
44+
{
45+
case DatePicker datePicker:
46+
datePicker.SetCurrentValue(DatePicker.SelectedDateProperty, null);
47+
break;
48+
case TextBox textBox:
49+
textBox.SetCurrentValue(TextBox.TextProperty, null);
50+
break;
51+
case ComboBox comboBox:
52+
comboBox.SetCurrentValue(ComboBox.TextProperty, null);
53+
comboBox.SetCurrentValue(Selector.SelectedItemProperty, null);
54+
break;
55+
case PasswordBox passwordBox:
56+
passwordBox.Password = null;
57+
break;
58+
}
59+
e.Handled = true;
60+
}
61+
}
62+
63+
}
64+
}

MaterialDesignThemes.Wpf/TextFieldAssist.cs

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public static void SetPrefixText(DependencyObject element, string? value)
172172
/// Controls the visbility of the clear button.
173173
/// </summary>
174174
public static readonly DependencyProperty HasClearButtonProperty = DependencyProperty.RegisterAttached(
175-
"HasClearButton", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(false, HasClearButtonChanged));
175+
"HasClearButton", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(false));
176176

177177
public static void SetHasClearButton(DependencyObject element, bool value)
178178
=> element.SetValue(HasClearButtonProperty, value);
@@ -376,51 +376,15 @@ private static void TextBoxOnContextMenuClosing(object sender, ContextMenuEventA
376376

377377
private static void RemoveSpellingSuggestions(ContextMenu menu)
378378
{
379-
foreach (FrameworkElement item in (from item in menu.Items.OfType<FrameworkElement>()
380-
where ReferenceEquals(item.Tag, typeof(Spelling))
381-
select item).ToList())
379+
foreach (FrameworkElement item in
380+
(from item in menu.Items.OfType<FrameworkElement>()
381+
where ReferenceEquals(item.Tag, typeof(Spelling))
382+
select item).ToList())
382383
{
383384
menu.Items.Remove(item);
384385
}
385-
}
386-
387-
private static void HasClearButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
388-
{
389-
var box = d as Control; //could be a text box or password box
390-
if (box == null)
391-
{
392-
return;
393-
}
394-
395-
if (box.IsLoaded)
396-
SetClearHandler(box);
397-
else
398-
box.Loaded += (sender, args) =>
399-
SetClearHandler(box);
400386
}
401387

402-
private static void SetClearHandler(Control box)
403-
{
404-
var bValue = GetHasClearButton(box);
405-
var clearButton = box.Template.FindName("PART_ClearButton", box) as Button;
406-
if (clearButton != null)
407-
{
408-
RoutedEventHandler handler = (sender, args) =>
409-
{
410-
(box as TextBox)?.SetCurrentValue(TextBox.TextProperty, null);
411-
(box as ComboBox)?.SetCurrentValue(ComboBox.TextProperty, null);
412-
if (box is PasswordBox passwordBox)
413-
{
414-
passwordBox.Password = null;
415-
}
416-
};
417-
if (bValue)
418-
clearButton.Click += handler;
419-
else
420-
clearButton.Click -= handler;
421-
}
422-
}
423-
424388
/// <summary>
425389
/// Applies the text box view margin.
426390
/// </summary>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ComboBox.xaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters"
44
xmlns:system="clr-namespace:System;assembly=mscorlib"
5-
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf">
5+
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
6+
xmlns:internal="clr-namespace:MaterialDesignThemes.Wpf.Internal">
67

78
<ResourceDictionary.MergedDictionaries>
89
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml" />
@@ -474,7 +475,8 @@
474475
VerticalAlignment="Center"
475476
HorizontalAlignment="Right"
476477
Padding="0"
477-
Focusable="False">
478+
Focusable="False"
479+
Command="{x:Static internal:ClearText.ClearCommand}">
478480
<Button.Margin>
479481
<MultiBinding Converter="{StaticResource ComboBoxClearButtonMarginConverter}">
480482
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Padding" />
@@ -868,6 +870,7 @@
868870
<Setter Property="wpf:ColorZoneAssist.Mode" Value="Standard" />
869871
<Setter Property="wpf:HintAssist.Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
870872
<Setter Property="Template" Value="{StaticResource MaterialDesignFloatingHintComboBoxTemplate}" />
873+
<Setter Property="internal:ClearText.HandlesClearCommand" Value="True" />
871874
<Style.Triggers>
872875
<Trigger Property="IsEditable" Value="True">
873876
<Setter Property="IsTabStop" Value="False" />

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DatePicker.xaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
4+
xmlns:internal="clr-namespace:MaterialDesignThemes.Wpf.Internal"
45
xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters">
56
<ResourceDictionary.MergedDictionaries>
67
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Calendar.xaml" />
@@ -185,7 +186,8 @@
185186
Height="Auto"
186187
Padding="2 0 0 0"
187188
Focusable="False"
188-
Style="{DynamicResource MaterialDesignToolButton}">
189+
Style="{DynamicResource MaterialDesignToolButton}"
190+
Command="{x:Static internal:ClearText.ClearCommand}">
189191
<Button.Visibility>
190192
<MultiBinding Converter="{StaticResource ClearButtonVisibilityConverter}">
191193
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:TextFieldAssist.HasClearButton)" />
@@ -439,6 +441,7 @@
439441
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource MaterialDesignValidationErrorTemplate}" />
440442
<Setter Property="wpf:TextFieldAssist.UnderlineBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
441443
<Setter Property="wpf:HintAssist.Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
444+
<Setter Property="internal:ClearText.HandlesClearCommand" Value="True" />
442445
<Setter Property="Template">
443446
<Setter.Value>
444447
<ControlTemplate TargetType="{x:Type DatePicker}">
@@ -457,7 +460,7 @@
457460
Grid.Column="0"
458461
Grid.Row="0"
459462
Focusable="{TemplateBinding Focusable}"
460-
Style="{DynamicResource MaterialDesignDatePickerTextBox}"
463+
Style="{StaticResource MaterialDesignDatePickerTextBox}"
461464
VerticalContentAlignment="Center"
462465
HorizontalAlignment="Stretch"
463466
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"

0 commit comments

Comments
 (0)