Default ErrorTemplate displays alongside with a custom ErrorTemplate - c#

I have a custom Validation.ErrorTemplate and for some reason, WPF displays both my custom error template and the default one. They both show the same error as expected, however I don't want to display the default ErrorTemplate.
My code:
<Style TargetType="TextBox" x:Key="MyTextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderBrush="red" BorderThickness="1" Background="#11FF0000" IsHitTestVisible="False" x:Name="errorBorder"/>
<AdornedElementPlaceholder x:Name="placeholder" />
<Popup AllowsTransparency="True" HorizontalAlignment="Right" HorizontalOffset="0" VerticalOffset="0" PopupAnimation="Fade" Placement="Right"
PlacementTarget="{Binding ElementName=errorBorder}" IsOpen="{Binding ElementName=placeholder, Path=AdornedElement.IsFocused, Mode=OneWay}">
<StackPanel Orientation="Horizontal">
<Polygon VerticalAlignment="Center" Points="0,4,4,4" Fill="red" Stretch="Fill" Stroke="red"
StrokeThickness="2" />
<Border Background="red" CornerRadius="0" Padding="4">
<TextBlock HorizontalAlignment="Center" Foreground="white" FontWeight="Bold" Margin="2,0,0,0"
Text="{Binding ElementName=placeholder, Path=AdornedElement.ToolTip, Mode=OneWay}" />
</Border>
</StackPanel>
</Popup>
</Grid>
</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}" />
</Trigger>
</Style.Triggers>
</Style>
And this
<Style TargetType="TextBox" BasedOn="{StaticResource MyTextBox}"/>
I'd like to know if anyone knows why WPF displays both my error template and the default one.
EDIT
http://i58.tinypic.com/a14k6q.png - the picture with both errors showing

It is because you defined ErrorTemplate and also defiend a Validation.HasError trigger in your style, you could use one of them. If you want to use the ErrorTemplate you need to remove the trigger, and change the Text binding to "Path=AdornedElement.Validation.Errors).CurrentItem.ErrorContent" then you will just see the result from ErrorTemplate:
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border/>
<AdornedElementPlaceholder x:Name="placeholder" />
<Popup>
<StackPanel>
<Polygon/>
<Border>
<TextBlock Text="{Binding ElementName=placeholder,
Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent,
Mode=OneWay}" />
</Border>
</StackPanel>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>

Related

Button with round edge grid and set disable button color to green

I want to have a button with round edge grid and set disable button color to gray, green otherwise. Attached snippet of code. I don't know how to change the default. If I disable my button, I get the output as the image on the left, however, I want it to look like the image on the right!
<Button x:Name="button" Height="30" Width="100" Margin="825,0,0,0" Background="Transparent"
BorderThickness="0" BorderBrush="Transparent">
<Button.Content>
<Border CornerRadius="12.5" Height="25" Width="95" Margin="0" BorderBrush="Gray"
BorderThickness="4,4,4,4" Background="Gray">
<TextBlock Text="Back" HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="12,0,13,0" Foreground="White"/>
</Border>
</Button.Content>
</Button>
Content is only one part of Button. The rest is determined by Template.
If multiple buttons should have custom apperance, the Template should be set in buttons Style. For one button it can be done inplace:
<Button x:Name="button" Content="Back" Height="30" Width="100" Margin="825,0,0,0" Background="Transparent"
BorderThickness="0" BorderBrush="Transparent">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border CornerRadius="12.5" Height="25" Width="95" Margin="0" BorderBrush="Gray"
BorderThickness="4,4,4,4" Background="Gray">
<TextBlock Text="{TemplateBinding Content}"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="12,0,13,0" Foreground="White"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
if you are going to reuse the template, declare it as Resource
<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="RoundBtn">
<Border Name="roundBorder" CornerRadius="12.5" Height="25" Width="95" Margin="0" BorderBrush="Gray"
BorderThickness="4,4,4,4" Background="Gray">
<TextBlock Text="{TemplateBinding Content}"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="12,0,13,0" Foreground="White"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="roundBorder" Property="Background" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
and set button Template using resource:
<Button x:Name="button" Content="Back" Height="30" Width="100"
Template="{StaticResource RoundBtn}"
Margin="825,0,0,0" Background="Transparent"
BorderThickness="0" BorderBrush="Transparent"/>
This should work for you. I've used an outer grid with a blue background so that I could be sure it was all working, but just remove that and move the style to whichever resources you need to. Explanation as comments within the xaml:
<Grid Background="Blue">
<Grid.Resources>
<!-- Override the default button style, otherwise you get a grey
rectangle behind the ellipse when disabled -->
<Style TargetType="Button" x:Key="MyButtonStyle">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button x:Name="button" Height="30" Width="100"
BorderThickness="0" BorderBrush="Transparent" IsEnabled="false"
Style="{StaticResource MyButtonStyle}">
<Button.Content>
<Border CornerRadius="12.5" Height="25" Width="95">
<Border.Style>
<Style TargetType="Border">
<!-- Button is gray by default, i.e. when enabled -->
<Setter Property="Background" Value="Gray"/>
<Style.Triggers>
<!-- If the button becomes disabled then it becomes green -->
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="Back" HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="White"/>
</Border>
</Button.Content>
</Button>
</Grid>

Show popup based on two properties (IsFocused, IsVisible)

I have popup error message box, it opens once the textbox is focused.
The issue when opening dialog window, if you click on the close button (on close I hide the window) the error message stays visible. I tried to add trigger to check the visibility but it didn't work. any idea?
XAML:
<ControlTemplate x:Key="errorToolTipTemplate">
<Grid>
<Border BorderBrush="#EF5B7C" BorderThickness="1" Background="#EF5B7C" Opacity="0.4" IsHitTestVisible="False" x:Name="errorBorder" />
<AdornedElementPlaceholder x:Name="placeholder" />
<Popup x:Name="popup" AllowsTransparency="True"
PopupAnimation="Fade"
Placement="Right"
VerticalOffset="-3"
PlacementTarget="{Binding ElementName=errorBorder}"
IsOpen="{Binding ElementName=placeholder, Path=AdornedElement.IsFocused, Mode=OneWay}">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=errorBorder, Path=IsVisible, Mode=OneWay}" Value="False">
<Setter Property="IsOpen" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<DockPanel>
<Polygon VerticalAlignment="Center"
Points="0,5 5,0 5,10"
Fill="#EF5B7C"
Stretch="Fill" />
<Border Background="White" BorderBrush="#EF5B7C" BorderThickness="1" CornerRadius="4" Padding="2">
<DockPanel>
<Image Height="20" Width="20" VerticalAlignment="Center" Source="Images/C04_32.png" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{StaticResource errorBrush}"
FontWeight="Bold" Margin="2,0,0,0"
Text="{Binding ElementName=placeholder, Path=AdornedElement.ToolTip, Mode=OneWay}" />
</DockPanel>
</Border>
</DockPanel>
</Popup>
</Grid>
</ControlTemplate>
<!--Textbox style-->
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource errorToolTipTemplate}" />
</Style>
Fixed this by adding triggers for the ControlTemplate.
I have add trigger with SourceTarget as the adorner, and TargetName as the popup, and changed the IsOpen property according to the other control visibility.
<ControlTemplate x:Key="errorToolTipTemplate">
<Grid>
<Border BorderBrush="#EF5B7C" BorderThickness="1" Background="#EF5B7C" Opacity="0.4" IsHitTestVisible="False" x:Name="errorBorder" />
<AdornedElementPlaceholder x:Name="placeholder" />
<Popup x:Name="popup" AllowsTransparency="True"
PopupAnimation="Fade"
Placement="Right"
VerticalOffset="-3"
PlacementTarget="{Binding ElementName=errorBorder}"
IsOpen="{Binding ElementName=placeholder, Path=AdornedElement.IsFocused, Mode=OneWay}">
<DockPanel>
<Polygon VerticalAlignment="Center"
Points="0,5 5,0 5,10"
Fill="#EF5B7C"
Stretch="Fill" />
<Border Background="White" BorderBrush="#EF5B7C" BorderThickness="1" CornerRadius="4" Padding="2">
<DockPanel>
<Image Height="20" Width="20" VerticalAlignment="Center" Source="Images/C04_32.png" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{StaticResource errorBrush}"
FontWeight="Bold" Margin="2,0,0,0"
Text="{Binding ElementName=placeholder, Path=AdornedElement.ToolTip, Mode=OneWay}" />
</DockPanel>
</Border>
</DockPanel>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="placeholder" Property="IsVisible" Value="False">
<Setter TargetName="popup" Property="IsOpen" Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

Name header for context menu

The title seems easier than first thought, but I have a context menu that is bound to a list of items. I want to be able to display a title for the context menu to access the list. Currently it just shows up as blank and then allows me to view the list, however I need it to display a title "Add Existing Properties". Also if someone can help me with my image problem that would be much appreciated :). Instead of the first one having the image name I want that to display the name "Add Existing Properties"
<DataTemplate x:Key="AddNodeTemplate">
<Border BorderThickness="1" Background="#F7F7F7">
<Border.BorderBrush>
<DrawingBrush Viewport="8,8,8,8" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#F7F7F7">
<GeometryDrawing.Geometry>
<GeometryGroup>
<RectangleGeometry Rect="0,0,50,50"/>
<RectangleGeometry Rect="50,50,50,50"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.BorderBrush>
<StackPanel>
<Button x:Name="ButtonAdd" Click="ButtonAdd_Click" Height="30" Width="130">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="#F7F7F7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Images/icon_plus.bmp" HorizontalAlignment="Left" Margin="5,0,0,0"/>
<TextBlock Text="Add Property" HorizontalAlignment="Center" Grid.Column="1" VerticalAlignment="Center" Foreground="LightGray" FontStyle="Italic" FontSize="12"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="#F7F7F7"/>
</Style>
</Button.Style>
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Exisiting Properties" ItemsSource="{Binding Path=AvailableProperties}">
<MenuItem.Resources>
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Icon">
<Setter.Value>
<DataTemplate>
<StackPanel>
<Image Width="12" Height="12" Source="{Binding Icon, Converter={StaticResource ImageToSourceConverter}}" Margin="3" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</MenuItem.Resources>
</MenuItem>
<MenuItem Header="Upscale well logs"/>
<MenuItem Header="Upscale well_top attributes"/>
<MenuItem Header="Upscale point attributes"/>
<MenuItem Header="Calculate"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Border>
</DataTemplate>
What is displayed
This style <Style TargetType="MenuItem"> in fact also applies on the top level MenuItem (the one you hardcodedly set its Header to Add Exisiting Properties). You need to use a Trigger to filter that out based on the Role property. All the sub menu items have Role of SubMenuItem, so it should be like this:
<Style TargetType="MenuItem">
<Style.Triggers>
<Trigger Property="Role" Value="SubMenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
<!-- ... -->
</Style>
Edit: About the Icon, you explicitly set it to a DataTemplate which is not rendered, instead it uses the string returned by ToString() (hence the text System.Windows.DataTemplate in place of the icon). You can just remove DataTemplate like this:
<!-- define a non-shared resource of Image -->
<Style TargetType="MenuItem">
<Style.Resources>
<Image x:Key="img" x:Shared="False" Width="12" Height="12" Source="{Binding Icon, Converter={StaticResource ImageToSourceConverter}}"
Margin="3" VerticalAlignment="Center"/>
</Style.Resources>
<!-- ... -->
</Style>
<Setter Property="Icon" Value="{StaticResource img}"/>
Final solution:
<Style TargetType="MenuItem">
<Style.Resources>
<Style TargetType="ContentPresenter">
<Style.Triggers>
<Trigger Property="ContentSource" Value="Icon">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
<Setter Property="Icon" Value="{Binding Icon,
Converter={StaticResource ImageToSourceConverter}}"/>
<!-- ... -->
</Style>
you can either use
`
<MenuItem Header="Add Exisiting Properties" ItemsSource="{Binding Path=AvailableProperties}">
<MenuItem.Resources>
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{TemplateBinding Header}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
or
<MenuItem Header="Add Exisiting Properties" ItemsSource="{Binding Path=AvailableProperties}">
<MenuItem.Resources>
<Style TargetType="MenuItem">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
`

Is there any way to set a tooltip inside an ErrorTemplate?

In our WPF application we have a common control template which we use to display errors in a consistent way
<ResourceDictionary>
<ControlTemplate x:Key="ErrorTemplate">
<Border BorderThickness="1" BorderBrush="Red">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
</ResourceDictionary>
Elsewhere in our application when a control might display an error we set the ErrorTemplate like so
<TextBox Validation.ErrorTemplate="{DynamicResource ErrorTemplate}" />
I now want to display a tool tip in this error template, however setting the tooltip property on the border doesn't help that much as the tooltip only displays when the user mouses over the 1px wide border, not the control itself which is in error.
I know that I can set the tooltip in a style, however this error template is applied to many different controls (combo boxes etc...) and many of these controls also use styles which are independent from my error template - I really want to be able to apply my error template in a generic way to any control.
Is there any way that I can set a tooltip in my ErrorTemplate?
I have a style defined. I have IDataErrorInfo on my object (Customer) which does the validation for the property(LastName) which is databound to a text box for example. Here's my style:
<Style x:Key="ValidationTextBox" TargetType="{x:Type Control}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,2,40,2"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border Background="#B22222" DockPanel.Dock="Right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Foreground="White"/>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center">
<Border BorderBrush="#B22222" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style
<TextBox Style="{StaticResource ValidationTextBox}" Text="{Binding Path=Customer.LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
As I stated in my answer here you can:
<ControlTemplate x:Key="ErrorTemplate">
<Border BorderThickness="1" BorderBrush="Red"
Background="Transparent"
ToolTip="{Binding Path=/ErrorContent}">
<AdornedElementPlaceholder />
</Border>
</ControlTemplate>
I'm sorry I didn't have time yesterday...Would you try below and see if this is what you are after, please?
<Style x:Key="ValidationTextBox2" TargetType="{x:Type Control}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="2">
<DockPanel LastChildFill="True" ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" Background="Transparent">
<TextBlock />
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center">
</AdornedElementPlaceholder>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Trigger a WPF setter in code

I'm trying to trigger a setter property in c# code manually, however I'm not sure whether this is possible?
C#;
// apply this setter to the control
myControl.FindResource("StandardTextBoxStyle") as Style;
// how can I trigger ErrorTemplateSetter from here!?
My style;
<Style TargetType="{x:Type TextBox}" x:Key="StandardTextBoxStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Validation.ErrorTemplate" x:Name="ErrorTemplateSetter">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="true">
<Border Background="OrangeRed" DockPanel.Dock="right" Margin="5,0,0,0"
Width="20" Height="20" CornerRadius="5"
ToolTip="{Binding ElementName=customAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center"
FontWeight="Bold" Foreground="white" />
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Categories

Resources