I have successfully created a textbox that displays / collapses an error message depending upon a validation rule set in its model / vm. The code goes like this for the email for ex.:
<StackPanel Grid.Row="3" Grid.Column="1">
<TextBox MaxLength="200" x:Name="mailTextBox"
Style="{StaticResource SectionEditPropertyTextBox}"
Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<ContentPresenter Visibility="{Binding ElementName=mailTextBox, Path=(Validation.HasError), Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=True }"
Content="{Binding ElementName=mailTextBox, Path=(Validation.Errors).CurrentItem}"
HorizontalAlignment="Left">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Label Style="{StaticResource SectionEditErrorLabel}" Content="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
Since I have a bunch of these, I would have liked to put all of this in a control template and relocate this in a common resource file.
My template looks like this:
<ControlTemplate x:Key="FormTextBox" TargetType="{x:Type TextBox}">
<StackPanel Grid.Row="{TemplateBinding Grid.Row}" Grid.Column="{TemplateBinding Grid.Column}">
<TextBox x:Name="validableText" MaxLength="{TemplateBinding MaxLength}"
Style="{StaticResource SectionEditPropertyTextBox}"
Text="{TemplateBinding Text}" />
<ContentPresenter Visibility="{Binding ElementName=validableText, Path=(Validation.HasError), Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=True }"
Content="{Binding ElementName=validableText, Path=(Validation.Errors).CurrentItem}"
HorizontalAlignment="Left">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Label Style="{StaticResource SectionEditErrorLabel}" Content="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</ControlTemplate>
and I link to it like this:
<TextBox Grid.Row="3" Grid.Column="1" MaxLength="200" Template="{StaticResource FormTextBox}"
Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
Unfortunately, it doesn't perform any validation so the binding must be broken somehow...
Please advise...
Thanks.
You won't need the Grid.Row
and Grid.Column
bindings in the Template StackPanel since the StackPanel
won't be the direct child of a Grid
anyway,
TemplateBinding
is always a OneWay
binding so the Text
property for the Templated TextBox
will never get updated. Change it to a regular Binding with RelativeSource
and TwoWay
Change ElementName=validableText
to RelativeSource={RelativeSource TemplatedParent}
in the bindings for ContentPresenter
since we want to perform the validation check on the Templated TextBox
and not the TextBox
inside the Template.
<ControlTemplate x:Key="FormTextBox" TargetType="{x:Type TextBox}">
<StackPanel>
<TextBox x:Name="validableText"
MaxLength="{TemplateBinding MaxLength}"
Style="{StaticResource SectionEditPropertyTextBox}"
Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Text,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<ContentPresenter Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=(Validation.HasError),
Converter={StaticResource BooleanToVisibilityConverter}
ConverterParameter=True}"
Content="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=(Validation.Errors).CurrentItem}"
HorizontalAlignment="Left">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Label Style="{StaticResource SectionEditErrorLabel}" Content="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</ControlTemplate>
On a side note, another alternative that you have here is to create a UserControl
with the original piece of Xaml that you had. You could introduce the Dependency Properties needed for your scenario (Text etc.). It would only require small changes.