Skip to content

Commit 8c0bcfd

Browse files
Fix hover issue on DatePicker and TimePicker (#3548)
* Add UI tests for the issue * Add dedicated AP to communicate IsMouseOver between elements * Fix issue using new AP and mapping the hosts IsMouseOver down to the nested TextBox * Update UI tests to use predefined MDIX constants * Move AP to dedicated type in the internal namespace
1 parent dd9c6f9 commit 8c0bcfd

File tree

6 files changed

+92
-5
lines changed

6 files changed

+92
-5
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace MaterialDesignThemes.Wpf.Internal;
2+
3+
public static class InternalTextFieldAssist
4+
{
5+
/// <summary>
6+
/// Used by text field "wrappers" (i.e. controls hosting a text field and decorating on top of it) to signal to the text field that the mouse is over it,
7+
/// when in fact it is over a sibling (i.e. something in the wrapper) which is visually placed on top of the text field.
8+
/// </summary>
9+
public static readonly DependencyProperty IsMouseOverProperty = DependencyProperty.RegisterAttached(
10+
"IsMouseOver", typeof(bool), typeof(InternalTextFieldAssist), new PropertyMetadata(default(bool)));
11+
public static void SetIsMouseOver(DependencyObject element, bool value) => element.SetValue(IsMouseOverProperty, value);
12+
public static bool GetIsMouseOver(DependencyObject element) => (bool)element.GetValue(IsMouseOverProperty);
13+
}

src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DatePicker.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
wpf:TextFieldAssist.TextFieldCornerRadius="{TemplateBinding wpf:TextFieldAssist.TextFieldCornerRadius}"
8787
wpf:TextFieldAssist.UnderlineBrush="{TemplateBinding wpf:TextFieldAssist.UnderlineBrush}"
8888
wpf:TextFieldAssist.UnderlineCornerRadius="{TemplateBinding wpf:TextFieldAssist.UnderlineCornerRadius}"
89+
internal:InternalTextFieldAssist.IsMouseOver="{TemplateBinding IsMouseOver}"
8990
BorderBrush="{TemplateBinding BorderBrush}"
9091
Focusable="{TemplateBinding Focusable}"
9192
Style="{DynamicResource NestedTextBoxStyle}">

src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,13 @@
444444
</MultiTrigger>
445445

446446
<!-- IsMouseOver -->
447+
<Trigger Property="IsMouseOver" Value="True">
448+
<!-- If the mouse is over the text field itself, we explicitly override the AP so the subsequent triggers will take effect -->
449+
<Setter Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
450+
</Trigger>
447451
<MultiTrigger>
448452
<MultiTrigger.Conditions>
449-
<Condition Property="IsMouseOver" Value="True" />
453+
<Condition Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
450454
<Condition Property="wpf:TextFieldAssist.HasFilledTextField" Value="False" />
451455
<Condition Property="wpf:TextFieldAssist.HasOutlinedTextField" Value="False" />
452456
<Condition Property="wpf:TextFieldAssist.NewSpecHighlightingEnabled" Value="False" />
@@ -455,7 +459,7 @@
455459
</MultiTrigger>
456460
<MultiTrigger>
457461
<MultiTrigger.Conditions>
458-
<Condition Property="IsMouseOver" Value="True" />
462+
<Condition Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
459463
<Condition Property="wpf:TextFieldAssist.HasFilledTextField" Value="False" />
460464
<Condition Property="wpf:TextFieldAssist.HasOutlinedTextField" Value="False" />
461465
<Condition Property="wpf:TextFieldAssist.NewSpecHighlightingEnabled" Value="True" />
@@ -465,15 +469,15 @@
465469
</MultiTrigger>
466470
<MultiTrigger>
467471
<MultiTrigger.Conditions>
468-
<Condition Property="IsMouseOver" Value="True" />
472+
<Condition Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
469473
<Condition Property="wpf:TextFieldAssist.HasFilledTextField" Value="True" />
470474
</MultiTrigger.Conditions>
471475
<Setter TargetName="OuterBorder" Property="Background" Value="{DynamicResource MaterialDesign.Brush.TextBox.HoverBackground}" />
472476
<Setter TargetName="OuterBorder" Property="BorderBrush" Value="{DynamicResource MaterialDesign.Brush.TextBox.HoverBorder}" />
473477
</MultiTrigger>
474478
<MultiTrigger>
475479
<MultiTrigger.Conditions>
476-
<Condition Property="IsMouseOver" Value="True" />
480+
<Condition Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
477481
<Condition Property="Validation.HasError" Value="False" />
478482
<Condition Property="wpf:ValidationAssist.HasError" Value="False" />
479483
<Condition Property="wpf:TextFieldAssist.HasOutlinedTextField" Value="True" />
@@ -490,7 +494,7 @@
490494
</MultiTrigger>
491495
<MultiTrigger>
492496
<MultiTrigger.Conditions>
493-
<Condition Property="IsMouseOver" Value="True" />
497+
<Condition Property="internal:InternalTextFieldAssist.IsMouseOver" Value="True" />
494498
<Condition Property="wpf:TextFieldAssist.HasOutlinedTextField" Value="True" />
495499
<Condition Property="wpf:HintAssist.IsFloating" Value="True" />
496500
<Condition SourceName="Hint" Property="IsHintInFloatingPosition" Value="True" />

src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TimePicker.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
wpf:TextFieldAssist.TextFieldCornerRadius="{TemplateBinding wpf:TextFieldAssist.TextFieldCornerRadius}"
9999
wpf:TextFieldAssist.UnderlineBrush="{TemplateBinding wpf:TextFieldAssist.UnderlineBrush}"
100100
wpf:TextFieldAssist.UnderlineCornerRadius="{TemplateBinding wpf:TextFieldAssist.UnderlineCornerRadius}"
101+
internal:InternalTextFieldAssist.IsMouseOver="{TemplateBinding IsMouseOver}"
101102
BorderBrush="{TemplateBinding BorderBrush}"
102103
Focusable="{TemplateBinding Focusable}"
103104
Style="{DynamicResource NestedTextBoxStyle}">

tests/MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,40 @@ public async Task DatePicker_WithCustomHintBackgroundSet_ShouldApplyHintBackgrou
302302

303303
recorder.Success();
304304
}
305+
306+
[Fact]
307+
[Description("Issue 3547")]
308+
public async Task DatePicker_ShouldApplyIsMouseOverTriggers_WhenHoveringCalendarButton()
309+
{
310+
await using var recorder = new TestRecorder(App);
311+
312+
// Arrange
313+
Thickness expectedThickness = Constants.DefaultOutlinedBorderActiveThickness;
314+
var stackPanel = await LoadXaml<StackPanel>("""
315+
<StackPanel>
316+
<DatePicker
317+
Style="{StaticResource MaterialDesignOutlinedDatePicker}" />
318+
</StackPanel>
319+
""");
320+
var datePicker = await stackPanel.GetElement<DatePicker>("/DatePicker");
321+
var datePickerTextBox = await datePicker.GetElement<DatePickerTextBox>("/DatePickerTextBox");
322+
var datePickerTextBoxBorder = await datePickerTextBox.GetElement<Border>("OuterBorder");
323+
var datePickerTimeButton = await datePicker.GetElement<Button>("PART_Button");
324+
325+
// Act
326+
await datePickerTextBoxBorder.MoveCursorTo();
327+
await Task.Delay(50);
328+
var datePickerTextBoxHoverThickness = await datePickerTextBoxBorder.GetBorderThickness();
329+
await datePickerTimeButton.MoveCursorTo();
330+
await Task.Delay(50);
331+
var datePickerCalendarButtonHoverThickness = await datePickerTextBoxBorder.GetBorderThickness();
332+
333+
// Assert
334+
Assert.Equal(expectedThickness, datePickerTextBoxHoverThickness);
335+
Assert.Equal(expectedThickness, datePickerCalendarButtonHoverThickness);
336+
337+
recorder.Success();
338+
}
305339
}
306340

307341
public class FutureDateValidationRule : ValidationRule

tests/MaterialDesignThemes.UITests/WPF/TimePickers/TimePickerTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,40 @@ public async Task TimePicker_WithCustomHintBackgroundSet_ShouldApplyHintBackgrou
609609

610610
recorder.Success();
611611
}
612+
613+
[Fact]
614+
[Description("Issue 3547")]
615+
public async Task TimePicker_ShouldApplyIsMouseOverTriggers_WhenHoveringTimeButton()
616+
{
617+
await using var recorder = new TestRecorder(App);
618+
619+
// Arrange
620+
Thickness expectedThickness = Constants.DefaultOutlinedBorderActiveThickness;
621+
var stackPanel = await LoadXaml<StackPanel>("""
622+
<StackPanel>
623+
<materialDesign:TimePicker
624+
Style="{StaticResource MaterialDesignOutlinedTimePicker}" />
625+
</StackPanel>
626+
""");
627+
var timePicker = await stackPanel.GetElement<TimePicker>("/TimePicker");
628+
var timePickerTextBox = await timePicker.GetElement<TimePickerTextBox>("/TimePickerTextBox");
629+
var timePickerTextBoxBorder = await timePickerTextBox.GetElement<Border>("OuterBorder");
630+
var timePickerTimeButton = await timePicker.GetElement<Button>("PART_Button");
631+
632+
// Act
633+
await timePickerTextBoxBorder.MoveCursorTo();
634+
await Task.Delay(50);
635+
var timePickerTextBoxHoverThickness = await timePickerTextBoxBorder.GetBorderThickness();
636+
await timePickerTimeButton.MoveCursorTo();
637+
await Task.Delay(50);
638+
var timePickerTimeButtonHoverThickness = await timePickerTextBoxBorder.GetBorderThickness();
639+
640+
// Assert
641+
Assert.Equal(expectedThickness, timePickerTextBoxHoverThickness);
642+
Assert.Equal(expectedThickness, timePickerTimeButtonHoverThickness);
643+
644+
recorder.Success();
645+
}
612646
}
613647

614648
public class OnlyTenOClockValidationRule : ValidationRule

0 commit comments

Comments
 (0)