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.
来源:https://stackoverflow.com/questions/7100251/wpf-nested-binding-in-a-controltemplate