Hey everyone!
For the life of me I can not get my Validation.ErrorTemplate showing for my custom Textbox control. I tried adding an AdornerDecorator
out of desperation because I read that helps sometimes. If anyone can help point me in the right direction, that would be amazing.
I uploaded a sample project if someone would rather see an example of this failing. You will see that when you click the button, the second Textbox is using the standard error template.
http://www.vbninja.com/CustomTextBox.zip
Thanks
<ControlTemplate x:Key="validationTemplate2">
<DockPanel DataContext="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)/ErrorContent}">
<Ellipse x:Name="Ellipse" DockPanel.Dock="Right" Margin="2,0,2,0" Width="14" Height="14" VerticalAlignment="Center"
Stroke="#40000000" StrokeThickness="2"
Fill="Red">
<Ellipse.ToolTip>
<Border MaxWidth="350">
<ContentControl FontSize="14" Content="{Binding}"/>
</Border>
</Ellipse.ToolTip>
</Ellipse>
<Border BorderBrush="#40FFAF00" BorderThickness="2" IsHitTestVisible="False">
<Border.Background>
<SolidColorBrush Color="Green" Opacity="0.2"/>
</Border.Background>
<AdornedElementPlaceholder Margin="-2" Name="adorner"/>
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type local:TextBoxControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TextBoxControl}">
<Grid>
<TextBox FontFamily="Arial" FontSize="26" Foreground="Green"
Name="textBoxControl"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualHeight}"
Validation.ErrorTemplate="{StaticResource validationTemplate2}"
Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background, FallbackValue=White}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
</TextBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I programmatically add my bindings in the MainWindow
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Binding binding = new Binding("Value") { Source = alertDescription, ValidatesOnDataErrors = true, ValidatesOnExceptions = true, Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
binding.ValidationRules.Add(new EmptyField());
alertDescription.SetBinding(TextBox.TextProperty, binding);
}
The controls are validated through a method that I found from searching on here
public static class Validator
{
public static bool IsValid(DependencyObject parent)
{
// Validate all the bindings on the parent
bool valid = true;
LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
while (localValues.MoveNext())
{
LocalValueEntry entry = localValues.Current;
if (BindingOperations.IsDataBound(parent, entry.Property))
{
Binding binding = BindingOperations.GetBinding(parent, entry.Property);
foreach (ValidationRule rule in binding.ValidationRules)
{
ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
if (!result.IsValid)
{
BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
valid = false;
}
else
{
BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
System.Windows.Controls.Validation.ClearInvalid(expression);
}
}
}
}
// Validate all the bindings on the children
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (!IsValid(child)) { valid = false; }
}
return valid;
}
}