Reputation: 483
Validation error will not show if I transfer Binding through Dependency Property in custom control.
DETAIL
I have a viewmodel which always have a validation error on one property
class ViewModel : IDataErrorInfo
{
public string Value { get; set; }
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get { return "Error"; }
}
}
and a TextBox
on view
<TextBox
Text="{Binding Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
so it will be surrounded by a red border.
Then I created a custom control named WrappedTextBox
which contains a Text
Dependency Property
class WrappedTextBox : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(WrappedTextBox));
}
and template
<Style TargetType="local:WrappedTextBox">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:WrappedTextBox">
<Grid>
<AdornerDecorator>
<TextBox
Text="{Binding Text,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True,
RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
put it on view
<local:WrappedTextBox
Text="{Binding Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
as the picture above shows, the second control has no red border on it.
if I don't remove Validation.ErrorTemplate
of WrappedTextBox
, it will be
How do I show error template on TextBox
inside WrappedTextBox
?
Upvotes: 0
Views: 1450
Reputation: 818
As far as I know your problem is that IDataErrorInfo
needs to be implemented on the class you are binding to, in your ControlTemplate
you are binding to the Text
-Property of your WrappedTextBox
, therefore your WrappedTextBox
itself has to implement IDataErrorInfo
to make validation work on your TextBox
.
Read also this article what you also can do instead of creating a new control.
For what you want to achieve 2 options came to my mind (Note: This options are created under the assumption that you are doing more stuff so the options of the previously mentioned article does not apply to you)
Option 1: Derive direct from TextBox
Code behind:
class WrappedTextBox : TextBox
{
}
Style:
<Style TargetType="local:WrappedTextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<AdornerDecorator>
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
</AdornerDecorator>
<-- ControlTemplate.Triggers etc. -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage: (same as before)
<local:WrappedTextBox
Text="{Binding Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
Option 2: Pass the Binding
itself
Code behind:
[TemplatePart(Name = "PART_TEXTBOX", Type = typeof(TextBox))]
class WrappedTextBox : Control
{
private TextBox _partTextBox;
private BindingBase _textBinding;
public BindingBase TextBinding
{
get => _textBinding;
set
{
if (_textBinding != value)
{
_textBinding = value;
ApplyTextBinding();
}
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_partTextBox = base.GetTemplateChild("PART_TEXTBOX") as TextBox;
ApplyTextBinding();
}
private void ApplyTextBinding()
{
if (_partTextBox != null)
BindingOperations.SetBinding(_partTextBox, TextBox.TextProperty, _textBinding);
}
}
Style:
<Style TargetType="local:WrappedTextBox">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:WrappedTextBox">
<Grid>
<AdornerDecorator>
<TextBox x:Name="PART_TEXTBOX" />
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<local:WrappedTextBox
TextBinding="{Binding Value,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
Upvotes: 1