Skip to content

Commit 1e00779

Browse files
committed
Merge branch 'ValidationErrorTemplate' of https://github.com/l1pton17/MaterialDesignInXamlToolkit into l1pton17-ValidationErrorTemplate
2 parents d828115 + 0a2c6da commit 1e00779

9 files changed

+316
-14
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using System.Windows;
8+
using System.Windows.Data;
9+
10+
namespace MaterialDesignThemes.Wpf.Converters
11+
{
12+
public class BooleanToVisibilityConverter : IValueConverter
13+
{
14+
public Visibility TrueValue { get; set; }
15+
public Visibility FalseValue { get; set; }
16+
17+
public object Convert(object value, Type targetType,
18+
object parameter, CultureInfo culture)
19+
{
20+
if (value is Boolean)
21+
return (Boolean)value ? TrueValue : FalseValue;
22+
23+
if (parameter is Boolean)
24+
return (Boolean)parameter ? TrueValue : FalseValue;
25+
26+
return null;
27+
}
28+
29+
public object ConvertBack(object value, Type targetType,
30+
object parameter, CultureInfo culture)
31+
{
32+
if (Equals(value, TrueValue))
33+
return true;
34+
35+
if (Equals(value, FalseValue))
36+
return false;
37+
38+
if (Equals(parameter, TrueValue))
39+
return true;
40+
41+
if (Equals(parameter, FalseValue))
42+
return false;
43+
44+
return null;
45+
}
46+
}
47+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Controls.Primitives;
9+
using System.Windows.Input;
10+
11+
namespace MaterialDesignThemes.Wpf
12+
{
13+
/// <summary>
14+
/// MahApp's validation popup copy
15+
/// </summary>
16+
public sealed class CustomValidationPopup : Popup
17+
{
18+
private Window _hostWindow;
19+
20+
public CustomValidationPopup()
21+
{
22+
this.Loaded += this.CustomValidationPopup_Loaded;
23+
this.Opened += this.CustomValidationPopup_Opened;
24+
}
25+
26+
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
27+
{
28+
this.IsOpen = false;
29+
}
30+
31+
private void CustomValidationPopup_Loaded(object sender, RoutedEventArgs e)
32+
{
33+
var target = this.PlacementTarget as FrameworkElement;
34+
if (target == null)
35+
{
36+
return;
37+
}
38+
39+
this._hostWindow = Window.GetWindow(target);
40+
if (this._hostWindow == null)
41+
{
42+
return;
43+
}
44+
45+
this._hostWindow.LocationChanged -= this.hostWindow_SizeOrLocationChanged;
46+
this._hostWindow.LocationChanged += this.hostWindow_SizeOrLocationChanged;
47+
this._hostWindow.SizeChanged -= this.hostWindow_SizeOrLocationChanged;
48+
this._hostWindow.SizeChanged += this.hostWindow_SizeOrLocationChanged;
49+
target.SizeChanged -= this.hostWindow_SizeOrLocationChanged;
50+
target.SizeChanged += this.hostWindow_SizeOrLocationChanged;
51+
this._hostWindow.StateChanged -= this.hostWindow_StateChanged;
52+
this._hostWindow.StateChanged += this.hostWindow_StateChanged;
53+
this._hostWindow.Activated -= this.hostWindow_Activated;
54+
this._hostWindow.Activated += this.hostWindow_Activated;
55+
this._hostWindow.Deactivated -= this.hostWindow_Deactivated;
56+
this._hostWindow.Deactivated += this.hostWindow_Deactivated;
57+
58+
this.Unloaded -= this.CustomValidationPopup_Unloaded;
59+
this.Unloaded += this.CustomValidationPopup_Unloaded;
60+
}
61+
62+
private void CustomValidationPopup_Opened(object sender, EventArgs e)
63+
{
64+
//this.SetTopmostState(true);
65+
}
66+
67+
private void hostWindow_Activated(object sender, EventArgs e)
68+
{
69+
//this.SetTopmostState(true);
70+
}
71+
72+
private void hostWindow_Deactivated(object sender, EventArgs e)
73+
{
74+
//this.SetTopmostState(false);
75+
}
76+
77+
private void CustomValidationPopup_Unloaded(object sender, RoutedEventArgs e)
78+
{
79+
var target = this.PlacementTarget as FrameworkElement;
80+
if (target != null)
81+
{
82+
target.SizeChanged -= this.hostWindow_SizeOrLocationChanged;
83+
}
84+
if (this._hostWindow != null)
85+
{
86+
this._hostWindow.LocationChanged -= this.hostWindow_SizeOrLocationChanged;
87+
this._hostWindow.SizeChanged -= this.hostWindow_SizeOrLocationChanged;
88+
this._hostWindow.StateChanged -= this.hostWindow_StateChanged;
89+
this._hostWindow.Activated -= this.hostWindow_Activated;
90+
this._hostWindow.Deactivated -= this.hostWindow_Deactivated;
91+
}
92+
this.Unloaded -= this.CustomValidationPopup_Unloaded;
93+
this.Opened -= this.CustomValidationPopup_Opened;
94+
this._hostWindow = null;
95+
}
96+
97+
private void hostWindow_StateChanged(object sender, EventArgs e)
98+
{
99+
if (this._hostWindow != null && this._hostWindow.WindowState != WindowState.Minimized)
100+
{
101+
var target = this.PlacementTarget as FrameworkElement;
102+
var holder = target != null ? target.DataContext as AdornedElementPlaceholder : null;
103+
if (holder != null && holder.AdornedElement != null)
104+
{
105+
var errorTemplate = holder.AdornedElement.GetValue(Validation.ErrorTemplateProperty);
106+
holder.AdornedElement.SetValue(Validation.ErrorTemplateProperty, null);
107+
holder.AdornedElement.SetValue(Validation.ErrorTemplateProperty, errorTemplate);
108+
}
109+
}
110+
}
111+
112+
private void hostWindow_SizeOrLocationChanged(object sender, EventArgs e)
113+
{
114+
var offset = this.HorizontalOffset;
115+
// "bump" the offset to cause the popup to reposition itself on its own
116+
this.HorizontalOffset = offset + 1;
117+
this.HorizontalOffset = offset;
118+
}
119+
}
120+
}

MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,18 @@
192192
<SubType>Designer</SubType>
193193
<Generator>MSBuild:Compile</Generator>
194194
</Page>
195+
<Page Include="Themes\MaterialDesignTheme.ValidationErrorTemplate.xaml">
196+
<SubType>Designer</SubType>
197+
<Generator>MSBuild:Compile</Generator>
198+
</Page>
195199
</ItemGroup>
196200
<ItemGroup>
197201
<Compile Include="Card.cs" />
198202
<Compile Include="Clock.cs" />
199203
<Compile Include="ClockChoiceMadeEventArgs.cs" />
200204
<Compile Include="ClockItemButton.cs" />
201205
<Compile Include="ColorZone.cs" />
206+
<Compile Include="Converters\BooleanToVisibilityConverter.cs" />
202207
<Compile Include="Converters\BrushToRadialGradientBrushConverter.cs" />
203208
<Compile Include="Converters\CalendarDateCoalesceConverter.cs" />
204209
<Compile Include="Converters\CircularProgressBar\ArcEndPointConverter.cs" />
@@ -215,6 +220,7 @@
215220
<Compile Include="Converters\NotZeroToVisibilityConverter.cs" />
216221
<Compile Include="Converters\SizeToRectConverter.cs" />
217222
<Compile Include="CustomPopupPlacementCallbackHelper.cs" />
223+
<Compile Include="CustomValidationPopup.cs" />
218224
<Compile Include="DataGridAssist.cs" />
219225
<Compile Include="DateTimeEx.cs" />
220226
<Compile Include="ListSortDirectionIndicator.cs" />
@@ -248,6 +254,7 @@
248254
<Compile Include="ToolTipAssist.cs" />
249255
<Compile Include="RippleAssist.cs" />
250256
<Compile Include="Ripple.cs" />
257+
<Compile Include="ValidationAssist.cs" />
251258
<EmbeddedResource Include="Properties\Resources.resx">
252259
<Generator>ResXFileCodeGenerator</Generator>
253260
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -282,6 +289,7 @@
282289
<Name>MaterialDesignColors.Wpf</Name>
283290
</ProjectReference>
284291
</ItemGroup>
292+
<ItemGroup />
285293
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
286294
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
287295
Other similar extension points exist, see Microsoft.Common.targets.

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Dark.xaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
<!-- plan is to add all of the light/dark specific brushes, quite granular at first. can then consolodate any repition later on if necessary -->
66

7+
<Color x:Key="ValidationErrorColor">#f44336</Color>
8+
<SolidColorBrush x:Key="ValidationErrorBrush" Color="{StaticResource ValidationErrorColor}"/>
9+
710
<SolidColorBrush x:Key="MaterialDesignBackground" Color="#FF000000"/>
811
<SolidColorBrush x:Key="MaterialDesignPaper" Color="#FF37474f"/>
912
<SolidColorBrush x:Key="MaterialDesignBody" Color="#DDFFFFFF"/>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf">
44

55
<!-- use this resource dictionary to set up the most common themese for standard controls -->
6-
6+
77
<ResourceDictionary.MergedDictionaries>
8+
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.ValidationErrorTemplate.xaml" />
89
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml" />
910
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.button.xaml" />
1011
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.calendar.xaml" />

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Light.xaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
3-
3+
44
<!-- plan is to add all of the light/dark specific brushes, quite granular at first. can then consolodate any repition later on if necessary -->
5+
<Color x:Key="ValidationErrorColor">#f44336</Color>
6+
<SolidColorBrush x:Key="ValidationErrorBrush" Color="{StaticResource ValidationErrorColor}"/>
57

68
<SolidColorBrush x:Key="MaterialDesignBackground" Color="#FFFFFFFF"/>
79
<SolidColorBrush x:Key="MaterialDesignPaper" Color="#FFfafafa"/>

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TextBox.xaml

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
<converters:TextFieldHintVisibilityConverter x:Key="TextFieldHintVisibilityConverter" />
77

8+
9+
810
<Style x:Key="MaterialDesignTextBox" TargetType="{x:Type TextBox}">
911
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
1012
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignTextBoxBorder}" />
@@ -19,16 +21,7 @@
1921
<Setter Property="AllowDrop" Value="true"/>
2022
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
2123
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
22-
<Setter Property="Validation.ErrorTemplate">
23-
<Setter.Value>
24-
<ControlTemplate>
25-
<StackPanel>
26-
<AdornedElementPlaceholder />
27-
<TextBlock FontSize="10" Foreground="#f44336" Text="{Binding Path=[0].ErrorContent}" />
28-
</StackPanel>
29-
</ControlTemplate>
30-
</Setter.Value>
31-
</Setter>
24+
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource MaterialDesignValidationErrorTemplate}"/>
3225
<Setter Property="Template">
3326
<Setter.Value>
3427
<ControlTemplate TargetType="{x:Type TextBox}">
@@ -58,7 +51,7 @@
5851
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}"/>
5952
</Trigger>
6053
<Trigger Property="Validation.HasError" Value="true">
61-
<Setter Property="BorderBrush" Value="#f44336"/>
54+
<Setter Property="BorderBrush" Value="{DynamicResource ValidationErrorBrush}"/>
6255
</Trigger>
6356
</ControlTemplate.Triggers>
6457
</ControlTemplate>
@@ -164,7 +157,7 @@
164157
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}"/>
165158
</Trigger>
166159
<Trigger Property="Validation.HasError" Value="true">
167-
<Setter Property="BorderBrush" Value="#f44336"/>
160+
<Setter Property="BorderBrush" Value="{DynamicResource ValidationErrorBrush}"/>
168161
</Trigger>
169162
</ControlTemplate.Triggers>
170163
</ControlTemplate>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
4+
xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters">
5+
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" TrueValue="Visible" FalseValue="Collapsed"/>
6+
7+
<ControlTemplate x:Key="MaterialDesignValidationErrorTemplate">
8+
<ControlTemplate.Resources>
9+
<DataTemplate DataType="{x:Type ValidationError}">
10+
<TextBlock Foreground="{DynamicResource ValidationErrorBrush}"
11+
FontSize="10"
12+
MaxWidth="250"
13+
Margin="2"
14+
TextWrapping="Wrap"
15+
Text="{Binding ErrorContent}"
16+
UseLayoutRounding="false" />
17+
</DataTemplate>
18+
</ControlTemplate.Resources>
19+
<StackPanel>
20+
<AdornedElementPlaceholder Name="Placeholder" />
21+
<Border Name="DefaultErrorViewerWrapper"
22+
Visibility="Collapsed">
23+
<Border Name="DefaultErrorViewer"
24+
Background="{DynamicResource MaterialDesignPaper}">
25+
<ContentPresenter Content="{Binding CurrentItem}" />
26+
</Border>
27+
</Border>
28+
29+
<wpf:CustomValidationPopup x:Name="ValidationPopup"
30+
IsOpen="False"
31+
Placement="Bottom" PlacementTarget="{Binding ElementName=Placeholder}"
32+
AllowsTransparency="True">
33+
<Border Background="{DynamicResource MaterialDesignPaper}">
34+
<ContentPresenter Content="{Binding CurrentItem}" />
35+
</Border>
36+
</wpf:CustomValidationPopup>
37+
</StackPanel>
38+
<ControlTemplate.Triggers>
39+
<MultiDataTrigger>
40+
<MultiDataTrigger.Conditions>
41+
<Condition Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.ShowOnFocus)}" Value="True"/>
42+
<Condition Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.UsePopup)}" Value="True"/>
43+
</MultiDataTrigger.Conditions>
44+
<MultiDataTrigger.Setters>
45+
<Setter TargetName="ValidationPopup" Property="IsOpen"
46+
Value="{Binding ElementName=Placeholder, Path=AdornedElement.IsKeyboardFocusWithin, Mode=OneWay}"/>
47+
</MultiDataTrigger.Setters>
48+
</MultiDataTrigger>
49+
50+
<MultiDataTrigger>
51+
<MultiDataTrigger.Conditions>
52+
<Condition Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.ShowOnFocus)}" Value="True"/>
53+
<Condition Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.UsePopup)}" Value="False"/>
54+
</MultiDataTrigger.Conditions>
55+
<MultiDataTrigger.Setters>
56+
<Setter TargetName="DefaultErrorViewer" Property="Visibility"
57+
Value="{Binding ElementName=Placeholder, Path=AdornedElement.IsKeyboardFocusWithin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
58+
</MultiDataTrigger.Setters>
59+
</MultiDataTrigger>
60+
61+
<DataTrigger Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.UsePopup)}" Value="True">
62+
<Setter TargetName="ValidationPopup" Property="IsOpen" Value="True"/>
63+
</DataTrigger>
64+
65+
<DataTrigger Binding="{Binding ElementName=Placeholder, Path=AdornedElement.(wpf:ValidationAssist.UsePopup)}" Value="False">
66+
<Setter TargetName="DefaultErrorViewerWrapper" Property="Visibility" Value="Visible"/>
67+
</DataTrigger>
68+
</ControlTemplate.Triggers>
69+
</ControlTemplate>
70+
</ResourceDictionary>

0 commit comments

Comments
 (0)