pass Validation error to UI element in WPF? - c#

I am using IDataErrorInfo to validate my data in a form in WPF. I have the validation implemented in my presenter.
The actual validation is happening, but the XAML that's supposed to update the UI and set the style isn't happening.
Here it is:
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
The problem is that my binding to Validation.Errors contains no data. How do I get this data from the Presenter class and pass it to this XAML so as to update the UI elements?
EDIT:
Textbox:
<TextBox Style="{StaticResource textBoxInError}" Name="txtAge" Height="23" Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Width="150">
<TextBox.Text>
<Binding Path="StrAge" Mode="TwoWay"
ValidatesOnDataErrors="True"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
The validation occurs, but the style to be applied when data is invalid is not happening.

Have you watched the output window as your form is being bound? a significant number of validation issues can be found by reviewing the output as the binding occurs.
One quick note as well:
use
Path=(Validation.Errors).CurrentItem.ErrorContent
rather than
Path=(Validation.Errors)[0].ErrorContent
It will save you some further binding excecption when a valid value is provided to the control

I noticed that your Style is not completely finished.
The Style needs a control template that defines a "Validation.ErrorTemplate" for it to work when a validation error occurs. Try making the following changes to see how it goes.
Paul Stovell has a very good article on WPF validation here that will cover most things you need. I have also written an article here to simplify validation that you might also like.
BEFORE
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
AFTER
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>

Related

how to base a style on another style and refer to its elements?

i have an ExtendedTreeView control which extendes TreeView. My extendedTreeView has a property called Highlight so unlike a normal TreeView, i want the extendedTreeView to highlight items based on this Highlight property rather than IsSelected. So I have a style defined for a TreeView like below.
<Style x:Key="TreeViewStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Border Name="Bd"
Background="Transparent"
//other stuff
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And then i define another style for the extendedTreeView which is based on the TreeViewStyle. The problem is that when i try to set "Bd" which is the border, it can't recognize it and has no idea what im referring to.
<Style x:Key="TreeViewStyle2" TargetType="{x:Type controls:ExtendedTreeView}" BasedOn="{StaticResource TreeViewStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ControlTemplate.Triggers>
<Trigger Property="controls:ExtendedTreeView.Highlight" Value="true">
<Setter TargetName="Bd" Property="Background" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
IS there anyway of fixing this? or an alternative way of doing it? thanks

Disable style of one text box among the 10 in WPF application

I have use 10 TextBox in my application and in that same application i have defined the style in App.xaml It applies for all the text boxes in my application . how to disable the style applying for a single TextBox.
Can any one help me out in this.
I used the below code to set the style
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Background="White"
x:Name="Bd" BorderBrush="#FF50729f" CornerRadius="3"
BorderThickness="1"
>
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" TargetName="Bd" Value="#FFe0dfe3"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="#FF9da3ab"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="#FFd22c2c" BorderThickness="1"
Background="#FFfce8e8" CornerRadius="3" >
<AdornedElementPlaceholder></AdornedElementPlaceholder>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#FFe0dfe3"/>
<Setter Property="BorderBrush" Value="#FF9da3ab"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
if you just want the default style then
<TextBox Style="{x:Null}"/>
otherwise Cédric Bignon's answer with target type defined (like below) will do the job.
No property will be inherited by your application TextBoxStyle
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<!-- your setters-->
</Style>
</TextBox.Style>
</TextBox>
if you want to slightly change the default application style
then use the following tecqnique where in your style you can
redefine properties you want to be different from the default
or define additional ones
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<!-- your setters-->
</Style>
</TextBox.Style>
</TextBox>
Just use an empty style:
<TextBox>
<TextBox.Style>
<Style>
</Style>
</TextBox.Style>
</TextBox>

Attached Behavior in Trigger Causes Build Error

I have a Style with a control template and I am having trouble getting it to compile. I am trying to trigger an attached behavior. If I put it in the control template triggers it works fine...but if I put it in the textbox triggers I get a build error that says:
Cannot find the static member 'SelectAllProperty' on the type
'TextBoxBehavior'
Here is my code:
<Style x:Key="RenamingTextBox" TargetType="{x:Type TextBox}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBlock x:Name="block" Visibility="Visible" Text="{TemplateBinding Text}" Margin="0"/>
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.Setters>
<Setter TargetName="box" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<!-- This next line gives an error even though it is the same format as the one below -->
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger.Setters>
</Trigger>
</TextBox.Triggers>
</TextBox>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsRenaming}" Value="true">
<DataTrigger.Setters>
<Setter TargetName="block" Property="TextBox.Visibility" Value="Collapsed" />
<Setter TargetName="box" Property="TextBox.Visibility" Value="Visible" />
<!-- Uncommenting below works fine -->
<!--<Setter TargetName="box" Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>-->
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Any ideas as to why one gives a build error and the other doesn't?
Nevermind, I needed to put the triggers for the textbox in a style instead:
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Change control property from ControlTemplate Trigger

I have overly simplified my code here. Needless to say, the binding works but the Triggering doesn't.
<Style TargetType="{x:Type c:SplineConnection}" BasedOn="{StaticResource {x:Type c:BezierSpline}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:SplineConnection}">
<Canvas>
<c:SplinePoint Point="{Binding Path=StartPoint, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Background="{Binding Path=StartBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Canvas>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="StartBrush" Value="White" />
<Setter Property="EndBrush" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The trigger works (I tested it doing a manual hookup), but it doesn't change the StartBrush property for my SplineConnection control, thus triggering the binding of the SplinePoint.
How do I make the template trigger setter change the control's (binding) property?

Change a button's content in a style?

I'm trying to do something similar to this:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content"
Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Content">
<Setter.Value>
<CheckBox Content="Mouse is over" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Window>
However, I get a run-time XamlParseException with a message of:
Cannot add content of type
'System.Windows.Controls.CheckBox' to
an object of type 'System.Object'.
Error at object
'System.Windows.Controls.CheckBox
I'm actually trying to draw different icons for the button's content depending on external conditions. So I'm actually trying to use a DataTrigger, but the example above simplifies the problem. Any ideas?
The actual error is occurring because Visuals can not be directly set as a Setter value.
You can get the behavior you are looking for though, by setting the ContentTemplate using a DataTemplate, or by creating your content as a resource, either specific to the button or located elsewhere.
<Button>
<Button.Resources>
<CheckBox x:Key="Local_MouseOverContent" Content="Mouse is over" />
</Button.Resources>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content"
Value="{StaticResource Local_MouseOverContent}" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="Button">
<CheckBox Content="Mouse is over" />
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
REMARK! Exactly your example works in .NET Framework 4 without any Changes !!!!
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content"
Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Content">
<Setter.Value>
<CheckBox Content="Mouse is over" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
If you are making a generic style to be used by buttons all around your app, you will get visual tree conflicts using the approach where the image is a resource. So the template is your only choice in that case.
WARNING: This may not be the best or correct way to do it. Make sure you read the other answers on this page as well.
Pretty sure you'd want to use a control template in this sort of situation. Something like:
<style>
<Setter Property="Content">
<Setter.Value>
<ControlTemplate>
<Image Img="something.jpg" />
</ControlTemplate>
</Setter.Value>
</Setter>
</style>
And add a control template in the trigger for the on-hover.
Here's a good link

Categories