Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MainDemo.Wpf/Pickers.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<DatePicker
Width="100"
materialDesign:HintAssist.Hint="Pick Date"
materialDesign:TextFieldAssist.HasClearButton="True"
Style="{StaticResource MaterialDesignFloatingHintDatePicker}"/>
</smtx:XamlDisplay>

Expand Down
39 changes: 39 additions & 0 deletions MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,44 @@ public async Task OnFilledComboBoxHelperTextFontSize_ChangesHelperTextFontSize()
Assert.Equal(20, fontSize);
recorder.Success();
}

[Fact]
[Description("Issue 2495")]
public async Task OnComboBox_WithClearButton_ClearsSelection()
{
await using var recorder = new TestRecorder(App);

var stackPanel = await LoadXaml<StackPanel>($@"
<StackPanel>
<ComboBox materialDesign:HintAssist.Hint=""OS""
materialDesign:TextFieldAssist.HasClearButton=""True""
SelectedIndex=""1"">
<ComboBoxItem Content=""Android"" />
<ComboBoxItem Content=""iOS"" />
<ComboBoxItem Content=""Linux"" />
<ComboBoxItem Content=""Windows"" />
</ComboBox>
</StackPanel>");
var comboBox = await stackPanel.GetElement<ComboBox>("/ComboBox");
var clearButton = await comboBox.GetElement<Button>("PART_ClearButton");

int? selectedIndex = await comboBox.GetSelectedIndex();
object? text = await comboBox.GetText();

Assert.True(selectedIndex >= 0);
Assert.NotNull(text);

await clearButton.LeftClick();

await Wait.For(async () =>
{
text = await comboBox.GetText();
Assert.Null(text);
selectedIndex = await comboBox.GetSelectedIndex();
Assert.False(selectedIndex >= 0);
});

recorder.Success();
}
}
}
33 changes: 31 additions & 2 deletions MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Controls;
using XamlTest;
Expand All @@ -19,7 +20,7 @@ public DatePickerTests(ITestOutputHelper output)
public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize()
{
await using var recorder = new TestRecorder(App);

var stackPanel = await LoadXaml<StackPanel>(@"
<StackPanel>
<DatePicker materialDesign:HintAssist.HelperTextFontSize=""20""/>
Expand All @@ -33,5 +34,33 @@ public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize()
Assert.Equal(20, fontSize);
recorder.Success();
}

[Fact]
[Description("Issue 2495")]
public async Task OnDatePicker_WithClearButton_ClearsSelectedDate()
{
await using var recorder = new TestRecorder(App);

var stackPanel = await LoadXaml<StackPanel>($@"
<StackPanel>
<DatePicker SelectedDate=""{DateTime.Today:d}"" materialDesign:TextFieldAssist.HasClearButton=""True""/>
</StackPanel>");
var datePicker = await stackPanel.GetElement<DatePicker>("/DatePicker");
var clearButton = await datePicker.GetElement<Button>("PART_ClearButton");

DateTime? selectedDate = await datePicker.GetSelectedDate();

Assert.NotNull(selectedDate);

await clearButton.LeftClick();

await Wait.For(async () =>
{
selectedDate = await datePicker.GetSelectedDate();
Assert.Null(selectedDate);
});

recorder.Success();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using XamlTest;
using Xunit;
using Xunit.Abstractions;
using Controls = System.Windows.Controls;

namespace MaterialDesignThemes.UITests.WPF.PasswordBoxes
{
Expand Down Expand Up @@ -57,5 +56,35 @@ public async Task OnPasswordBoxHelperTextFontSize_ChangesHelperTextFontSize()
Assert.Equal(20, fontSize);
recorder.Success();
}

[Fact]
[Description("Issue 2495")]
public async Task OnPasswordBox_WithClearButton_ClearsPassword()
{
await using var recorder = new TestRecorder(App);

var grid = await LoadXaml<Grid>(@"
<Grid Margin=""30"">
<PasswordBox materialDesign:TextFieldAssist.HasClearButton=""True"" />
</Grid>");
var passwordBox = await grid.GetElement<PasswordBox>("/PasswordBox");
var clearButton = await passwordBox.GetElement<Button>("PART_ClearButton");

await passwordBox.SendKeyboardInput($"Test");

string? password = await passwordBox.GetPassword();

Assert.NotNull(password);

await clearButton.LeftClick();

await Wait.For(async () =>
{
password = await passwordBox.GetPassword();
Assert.Null(password);
});

recorder.Success();
}
}
}
35 changes: 33 additions & 2 deletions MaterialDesignThemes.UITests/WPF/TextBoxes/TextBoxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public TextBoxTests(ITestOutputHelper output)

[Fact]
[Description("Issue 1883")]
public async Task OnClearButtonShown_ControlHeighDoesNotChange()
public async Task OnClearButtonShown_ControlHeightDoesNotChange()
{
await using var recorder = new TestRecorder(App);

Expand Down Expand Up @@ -56,7 +56,7 @@ public async Task OnClearButtonShown_ControlHeighDoesNotChange()

[Fact]
[Description("Issue 1883")]
public async Task OnClearButtonWithHintShown_ControlHeighDoesNotChange()
public async Task OnClearButtonWithHintShown_ControlHeightDoesNotChange()
{
await using var recorder = new TestRecorder(App);

Expand Down Expand Up @@ -125,6 +125,37 @@ public async Task OnTextCleared_MultilineTextBox()
recorder.Success();
}

[Fact]
[Description("Issue 2495")]
public async Task OnTextBox_WithClearButton_ClearsText()
{
await using var recorder = new TestRecorder(App);

var grid = await LoadXaml<Grid>(@"
<Grid Margin=""30"">
<TextBox VerticalAlignment=""Top""
Text=""Some Text""
materialDesign:TextFieldAssist.HasClearButton=""True"">
</TextBox>
</Grid>");
var textBox = await grid.GetElement<TextBox>("/TextBox");
var clearButton = await textBox.GetElement<Button>("PART_ClearButton");

string? text = await textBox.GetText();

Assert.NotNull(text);

await clearButton.LeftClick();

await Wait.For(async () =>
{
text = await textBox.GetText();
Assert.Null(text);
});

recorder.Success();
}

[Fact]
[Description("Issue 2002")]
public async Task OnTextBoxDisabled_FloatingHintBackgroundIsOpaque()
Expand Down
64 changes: 64 additions & 0 deletions MaterialDesignThemes.Wpf/Internal/ClearText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace MaterialDesignThemes.Wpf.Internal
{
public static class ClearText
{
public static readonly RoutedCommand ClearCommand = new();

public static bool GetHandlesClearCommand(DependencyObject obj)
=> (bool)obj.GetValue(HandlesClearCommandProperty);

public static void SetHandlesClearCommand(DependencyObject obj, bool value)
=> obj.SetValue(HandlesClearCommandProperty, value);

public static readonly DependencyProperty HandlesClearCommandProperty =
DependencyProperty.RegisterAttached("HandlesClearCommand", typeof(bool), typeof(ClearText), new PropertyMetadata(false, OnHandlesClearCommandChanged));

private static void OnHandlesClearCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
if ((bool)e.NewValue)
{
element.CommandBindings.Add(new CommandBinding(ClearCommand, OnClearCommand));
}
else
{
for (int i = element.CommandBindings.Count - 1; i >= 0; i--)
{
if (element.CommandBindings[i].Command == ClearCommand)
{
element.CommandBindings.RemoveAt(i);
}
}
}
}

static void OnClearCommand(object sender, ExecutedRoutedEventArgs e)
{
switch (e.Source)
{
case DatePicker datePicker:
datePicker.SetCurrentValue(DatePicker.SelectedDateProperty, null);
break;
case TextBox textBox:
textBox.SetCurrentValue(TextBox.TextProperty, null);
break;
case ComboBox comboBox:
comboBox.SetCurrentValue(ComboBox.TextProperty, null);
comboBox.SetCurrentValue(Selector.SelectedItemProperty, null);
break;
case PasswordBox passwordBox:
passwordBox.Password = null;
break;
}
e.Handled = true;
}
}

}
}
46 changes: 5 additions & 41 deletions MaterialDesignThemes.Wpf/TextFieldAssist.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public static void SetPrefixText(DependencyObject element, string? value)
/// Controls the visbility of the clear button.
/// </summary>
public static readonly DependencyProperty HasClearButtonProperty = DependencyProperty.RegisterAttached(
"HasClearButton", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(false, HasClearButtonChanged));
"HasClearButton", typeof(bool), typeof(TextFieldAssist), new PropertyMetadata(false));

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

private static void RemoveSpellingSuggestions(ContextMenu menu)
{
foreach (FrameworkElement item in (from item in menu.Items.OfType<FrameworkElement>()
where ReferenceEquals(item.Tag, typeof(Spelling))
select item).ToList())
foreach (FrameworkElement item in
(from item in menu.Items.OfType<FrameworkElement>()
where ReferenceEquals(item.Tag, typeof(Spelling))
select item).ToList())
{
menu.Items.Remove(item);
}
}

private static void HasClearButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = d as Control; //could be a text box or password box
if (box == null)
{
return;
}

if (box.IsLoaded)
SetClearHandler(box);
else
box.Loaded += (sender, args) =>
SetClearHandler(box);
}

private static void SetClearHandler(Control box)
{
var bValue = GetHasClearButton(box);
var clearButton = box.Template.FindName("PART_ClearButton", box) as Button;
if (clearButton != null)
{
RoutedEventHandler handler = (sender, args) =>
{
(box as TextBox)?.SetCurrentValue(TextBox.TextProperty, null);
(box as ComboBox)?.SetCurrentValue(ComboBox.TextProperty, null);
if (box is PasswordBox passwordBox)
{
passwordBox.Password = null;
}
};
if (bValue)
clearButton.Click += handler;
else
clearButton.Click -= handler;
}
}

/// <summary>
/// Applies the text box view margin.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf">
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
xmlns:internal="clr-namespace:MaterialDesignThemes.Wpf.Internal">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml" />
Expand Down Expand Up @@ -474,7 +475,8 @@
VerticalAlignment="Center"
HorizontalAlignment="Right"
Padding="0"
Focusable="False">
Focusable="False"
Command="{x:Static internal:ClearText.ClearCommand}">
<Button.Margin>
<MultiBinding Converter="{StaticResource ComboBoxClearButtonMarginConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Padding" />
Expand Down Expand Up @@ -868,6 +870,7 @@
<Setter Property="wpf:ColorZoneAssist.Mode" Value="Standard" />
<Setter Property="wpf:HintAssist.Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="Template" Value="{StaticResource MaterialDesignFloatingHintComboBoxTemplate}" />
<Setter Property="internal:ClearText.HandlesClearCommand" Value="True" />
<Style.Triggers>
<Trigger Property="IsEditable" Value="True">
<Setter Property="IsTabStop" Value="False" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
xmlns:internal="clr-namespace:MaterialDesignThemes.Wpf.Internal"
xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Calendar.xaml" />
Expand Down Expand Up @@ -185,7 +186,8 @@
Height="Auto"
Padding="2 0 0 0"
Focusable="False"
Style="{DynamicResource MaterialDesignToolButton}">
Style="{DynamicResource MaterialDesignToolButton}"
Command="{x:Static internal:ClearText.ClearCommand}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ClearButtonVisibilityConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:TextFieldAssist.HasClearButton)" />
Expand Down Expand Up @@ -439,6 +441,7 @@
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource MaterialDesignValidationErrorTemplate}" />
<Setter Property="wpf:TextFieldAssist.UnderlineBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="wpf:HintAssist.Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="internal:ClearText.HandlesClearCommand" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DatePicker}">
Expand All @@ -457,7 +460,7 @@
Grid.Column="0"
Grid.Row="0"
Focusable="{TemplateBinding Focusable}"
Style="{DynamicResource MaterialDesignDatePickerTextBox}"
Style="{StaticResource MaterialDesignDatePickerTextBox}"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Expand Down
Loading