How Validation.HasError property is updated for user input validation? - c#

In this tutorial there is a text box that shows a pink background for invalid data. This is the wpf code:
<TextBox Text="{Binding Aid,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,ValidatesOnDataErrors=True}"
Canvas.Left="95" Canvas.Top="60" Width="297">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Pink"/>
...
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" ToolTip="{Binding [0].ErrorContent}"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
But I don't get how Validation.HasError is updated as the user inputs the value?

This sample project takes advantage of IDataErrorInfo interface which makes data validation very simple. This link will describe it in details.

Related

DataTrigger to change TextBlock text on ToggleButton click

I am trying to change the text of a TextBlock when clicking on the ToggleButton the TextBlock is located in. The TextBlock is located within a StackPanel, together with an image, and the StackPanel is located within the DataTemplate of the ToggleButton.
I am trying to accomplish this using a property called EditState within the ViewModel. The EditState property changes to "True" successfully when clicking the ToggleButton, so the issue is not within the ViewModel.
There is no error showing, the text of the TextBlock just won't change. Here is the XAML code I have at the moment:
<ToggleButton x:Name="btnEdit" Grid.ColumnSpan="1" Command="{Binding EditCommand}"
IsEnabled="{Binding CanEdit}" MinWidth="168">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type ToggleButton}">
<StackPanel Orientation="Horizontal">
<Image Source="/Resources/IconEdit.jpg" Height="20">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.2" />
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<TextBlock x:Name="tbkEdit" Text="Enable Editing" Margin="5" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding EditState}" Value="True">
<Setter TargetName="tbkEdit" Property="Text"
Value="Cancel Editing"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
I am looking for a solution consistent with MVVM. This is my first time using WPF and developing a project using MVVM. If someone could correct my mistake or point me in the right direction I would greatly appreciate it.
"a solution consistent with MVVM" is a misconception when you are styling a control. This is only a view element and should not depend on any view model. MVVM is not relevant here.
The TextBlock in the ContentTemplate should show the ToggleButton's Content property by Text="{Binding}", and the Content property should be set by a Style Setter and changed by a Style Trigger on the ToggleButton's IsChecked property.
Also note that DataType="{x:Type ToggleButton}" on the ContentTemplate is pointless and incorrect. DataType is meant for automatic usage of DataTemplate resources, which does not happen here. And it is supposed to match the type of the Content, which is not the type of the control.
<ToggleButton ...>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image ...>
...
</Image>
<TextBlock Text="{Binding}" Margin="5"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Content" Value="Enable Editing"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Cancel Editing"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>

How do I display a List of items inside a tooltip?

I have the following style applied to buttons in my application
<Style x:Key="ButtonPartChooserValidation" TargetType="{x:Type Button}" BasedOn="{StaticResource ControlBaseStyle}">
<Setter Property="Background" >
<Setter.Value>
<Binding Path="(Validation.Errors)" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<converters:ValidationErrorsToBackgroundColorConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding (Validation.Errors).[0].ErrorContent.Value[0], RelativeSource={x:Static RelativeSource.Self}}">
</Setter>
</Trigger>
<Trigger Property="Validation.HasError" Value="False">
<Setter Property="ToolTip" Value="Acceptable value"/>
<Setter Property="Background" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
When there is a validation error the button gets a yellow background and a tooltip displays the first item in the list of errors. The yellow background is what I am trying to achieve, but I would like to display the full list of validation errors and not just the first one. I have tried the following and just gotten an empty list displayed (just the Trigger from above for brevity, names match).
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ListBox ItemsSource="{Binding (Validation.Errors).[0].ErrorContent.Value, RelativeSource={x:Static RelativeSource.Self}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ErrMsg}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Setter.Value>
</Setter>
</Trigger>
I would almost certainly need two ListBox elements to display each of the lists in my object, but as of now I can't even get one to work. What am I setting up wrong?
You could bind to the Validation.Errors attached property of the PlacementTarget of the Tooltip:
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<ItemsControl ItemsSource="{Binding Path=PlacementTarget.(Validation.Errors), RelativeSource={RelativeSource AncestorType=ToolTip}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ToolTip>
</Setter.Value>
</Setter>

Binding not working for error validation

I have style for validation error of a control .I show the error in tooltip also I want to show it in a Textblock next to control.The binding in tooltip works fine but its doesnt work with textblock.I tried hardcoding some text in textblock it works ,but binding is not working.
<Style TargetType="{x:Type Control}" x:Key="S_ErrorTemplate">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Foreground="Red"
FontSize="12pt"
Text="{Binding Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={RelativeSource TemplatedParent}}" />
<Border BorderBrush="Green" BorderThickness="1" >
<AdornedElementPlaceholder Name="myControl"/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<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="Maroon"/>
</Trigger>
</Style.Triggers>
</Style>

Is there any way to change a single attribute of a template in a trigger in WPF?

So I'm trying to get my TabItem to display a different image depending on whether it's selected or not. Right now I've got it working using the following code:
<Window.Resources>
<local:UnselectedImageFilenameConverter x:Key="UnselectedImageFilenameConverter" />
<local:SelectedImageFilenameConverter x:Key="SelectedImageFilenameConverter" />
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Image Source="{TemplateBinding Header, Converter={StaticResource UnselectedImageFilenameConverter}}" Stretch="None" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Image Source="{TemplateBinding Header, Converter={StaticResource SelectedImageFilenameConverter}}" Stretch="None" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
However this seems quite redundant. I'm changing the whole template when all I need is to change the image source. It seems like there should be a more concise way of doing this, but so far no luck. Any ideas?
You could use a control template instead of a style. In it, you can define a trigger which changes the image source according to your whim.
<ControlTemplate TargetType="{x:Type TabItem}" x:Key="TabItemTemplate">
<Image x:Name="TabImage" Source="{Binding Something}"/>
<ControlTemplate.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Setter Property="Source" TargetName="TabImage" Value="{Binding SomethingElse}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
In the above template, if you want the template to be applied to every tab item by default, remove the x:Key attribute.

VisualBrush with Datatrigger not working in WPF

I script-kiddied this code to show helper text in a field before the field is populated by the user (also plan on using some modification of it to show the validation error if one occurs), but the trigger isn't, well, triggering. What's wrong with this code?
xaml:
<TextBox x:Name="firstName" Validation.Error="Text_ValidationError"
Text="{Binding UpdateSourceTrigger=LostFocus, Path=firstName, ValidatesOnDataErrors=true, NotifyOnValidationError=true}" Margin="30,12,50,245">
<TextBox.Style>
<Style TargetType="TextBox" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=firstName}" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="First name" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Don't use a DataTrigger for this, it's not necessary (the binding might be broken, in fact that is the only thing that i can think of that might cause this not to work), use a normal Trigger:
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="First name" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
Tested this, it works. This also has the advantage that the background disappears immediately when the user starts typing rather than when the focus on the control is lost and the source string is updated.

Categories

Resources