Issue with arorner when doing validation on a TextBox control - wpf

I am validating a TextBox control for a particular range. If user enter a value out of range, an Adorner control as shown in the image above is shown.
My problem is the control is cut beyond the window size. How do I place the red adorner above the window even though the window size is smaller as above.
Below is the code I have used for a ControlTemplate and Style:
<ControlTemplate x:Key="validationTemplate">
<DockPanel LastChildFill="True">
<TextBlock Name="ErrorText" DockPanel.Dock="Bottom" Foreground="White"
FontSize="12" Padding="7" FontFamily="Trebuchet MS"
Margin="5,5,0,0"
TextWrapping="Wrap"
Text="{Binding [0].ErrorContent}" >
<TextBlock.Background>
<SolidColorBrush Color="Red" Opacity="0.95"></SolidColorBrush>
</TextBlock.Background>
</TextBlock>
<AdornedElementPlaceholder Name="ErrorTextBox" />
</DockPanel>
</ControlTemplate>
<Style x:Key="ValidationStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Red" ShadowDepth="5" Direction="135" Opacity="0.5"></DropShadowEffect>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Please help.

Well, I am not aware that Adorners can be rendered outside of the window, so for your question, you can't place Adorner outside of window.
However, popup is usually used, when you need behaviour like that. Just wrap everything into Popup element, and see if it helps.

Related

WPF apply triggers to listview items to change background

I am trying to create a sort of left menu for navigation inside the desktopapplication. My idea is to use Buttons as Listview items which should behave in this way: when i hover with the mouseover them theri background should change color (becomes darkCyan) and when i click one its background color should change persistently (to darkCyan) until i click on another button of the list. The problem is that i am using a the DataTemplate property to specify how the buttons should look like and I am tryin to apply the triggers to change the background color on the ControlTemplate of the ListView. The result is that sometimes the background color changes but the command related to the button is not fired other times the contrary. I think that I am doing the things in the wrong element of the tree view, but I don't have enough knowledge of the tree view so I am not understanding what I am doing wrong. Here is the code of the XAML in which i define the styles for the Buttons and the ListView
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:TripViewModel}">
<views:TripView />
</DataTemplate>
<Style TargetType="{x:Type Button}">
<Setter Property="Height"
Value="50" />
<Setter Property="Background"
Value="#555D6F" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Grid Background="Transparent">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="DarkCyan" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And here is the code in which I create the ListView
<ListView Name="MenuButtons"
ItemsSource="{Binding PageViewModels}"
Background="Transparent"
IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Anyone can help?
Thanks
I have solved the issue by using a ListBox instead of a ListView and setting the ItemContainer to be a button in the following way
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Margin="0" />
</ItemContainerTemplate>
</ListBox.ItemTemplate>

Update style on element when ValidationError has occured, with an overriden template

I've overriden the Template of TextBox to give it a rounded border:
<Style TargetType="TextBox">
<Setter Property="b:TextBoxBehaviours.SelectAllTextOnFocus" Value="True" />
<Setter Property="Width" Value="75" />
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3" BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What I'm trying to achieve is that when the TextBox gets a validation error, the Border should change colour to a different one (e.g. Red). I've tried putting a DataTrigger in the Border style, but that doesn't seem to work.
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3" BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBox}, Path=Validaton.HasError}" Value="True">
<Setter Property="BorderBrush" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
I've tried changing the Mode on the binding to see if that works, but doesn't seem to have any affect. I've also changed the DataBinding path to bind to another property (i.e. the Text value), to see if that triggers it, so I think it's more my understanding of DataTriggers in ControlTemplates isn't quite right?
Am I approaching this incorrectly, or just slightly off?.
Use the ControlTemplate.Triggers section to change properties of elements inside the template dynamically. Use Setter.TargetName to specifiy the named element to target:
<ControlTemplate>
<Border x:Name="brdTxtBx" BorderThickness="2" CornerRadius="3"
BorderBrush="{x:Static SystemColors.ActiveBorderBrush}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="brdTxtBx" Property="BorderBrush" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
The usual way to achieve your requirements is to use the Validation.ErrorTemplate Attached Property. From the linked page on MSDN:
If the user enters an invalid value, you may want to provide some feedback about the error on the application user interface (UI). One way to provide such feedback is to set the Validation.ErrorTemplate attached property to a custom ControlTemplate.
<TextBox Name="textBox1" Width="50" FontSize="15"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textBoxInError}"
Grid.Row="1" Grid.Column="1" Margin="2">
...
Again, from the linked page:
The following example shows the custom ControlTemplate validationTemplate that creates a red exclamation mark to notify the user of a validation error. Control templates are used to redefine the appearance of a control:
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
Please see the linked page for more information.

How to Clear Triggered Control Template

I am trying to achieve the "hint text" functionality for a TextBox in WPF. I can set the default text fine, but the problem comes when I want the control to return its appearance to a normal TextBox. Here is the trigger I have so far:
Trigger A
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox>
<TextBox.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="UniformToFill">
<VisualBrush.Visual>
<Label Content="{TemplateBinding TextBox.Tag}" Background="White"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
This sets the Background to a VisualBrush when the Text property is empty. What I need to do is clear this ControlTemplate when the user selects the TextBox to input text.
Here is what I tried:
Trigger B
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
These two don't work together. I tested each by changing the Background colors. If I comment out either one, they will each work. If both are uncommented, Trigger A works and B is never seen. How can I remove/overwrite Trigger A?
I know that the functionality of these templates is supposed to clear when the trigger condition is no longer met, but for example, Trigger A's setting will not go away when I enter text into the TextBox like it should. Like the Text property is still String.Empty or something.
So what am I missing?
EDIT:
Here is the whole style (there's not much more to it):
<UserControl.Resources>
<Style TargetType="TextBox" x:Key="FormsTextBox">
<Setter Property="Width" Value="45"/>
<Setter Property="Margin" Value="3 2 3 2"/>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBox>
<TextBox.Background>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="UniformToFill">
<VisualBrush.Visual>
<Label Content="{TemplateBinding TextBox.Tag}" Background="White" Width="45"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I cannot see whole template but this looks a bit overcomplicated. I assume you're trying to achieve watermark text. For hint use box standard ToolTip property, which by default will display your text in a popup when hover your TextBox but this behaviour can be disabled and ToolTip property reused. You can either create reusable Style - which I prefer - for TextBox, something like this:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<Style TargetType="{x:Type TextBox}" x:Key="WatermarkTextBoxStyle">
<Setter Property="ToolTipService.IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
BorderThickness="1"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
<Grid>
<TextBlock Margin="5,0,0,0"
Text="{TemplateBinding ToolTip}"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}"
Opacity="0.5"/>
<ScrollViewer Name="PART_ContentHost"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<TextBox ToolTip="watermark text" Style="{StaticResource WatermarkTextBoxStyle}"/>
</Window>
or, if it's a one-off thing, then do you can do something like this without any Style or Template:
<Grid Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<TextBlock Margin="5,0,0,0"
Text="watermark text"
Opacity="0.5"
Visibility="{Binding ElementName=myTextBox, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="myTextBox" Background="Transparent" />
</Grid>

IsMouseOver triggers background color change only temporarily

I'm a beginner with this and trying to understand how WPF and XAML work. The following snippet is (a trivial modification) from Nathans Unleashed 4.0 book. I inserted it into an Ok button:
<Button.Style>
<Style TargetType=”{x:Type Button}”>
<Style.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Background” Value=”Yellow”/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
When I run this in XAML crunsher and move the mouse over the Ok button, the button does change it's background color to yellow (fine), but immediately resets the color to it's original value, even if the mouse stays over the button -- why is this? I'd expect it to stay yellow until the mouse is moved away from the button. Is this a problem with XAML crunsher, or is my expectation wrong?
Edit (responding to a comment): here is the complete window, which is taken from Nathan's book, too:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
Background="OrangeRed">
<StackPanel>
<Label FontWeight="Bold" FontSize="20" Foreground="White">
WPF Unleashed (Version 3.0)
</Label>
<Label>© 2006 SAMS Publishing</Label>
<Label>Installed Chapters:</Label>
<ListBox>
<ListBoxItem>Chapter 1</ListBoxItem>
<ListBoxItem>Chapter 2</ListBoxItem>
</ListBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button MinWidth="75" Margin="10">Help</Button>
<Button MinWidth="75" Margin="10">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
OK
</Button>
</StackPanel>
<StatusBar>You have successfully registered this product.</StatusBar>
</StackPanel>
</Window>
Unfortunatly the fancy hover over animation is built-in to the Button, you will have to override the ControlTemplate to stop this from happening.
<Button MinWidth="75" Margin="10" FocusVisualStyle="{x:Null}" Content="OK">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="border" BorderThickness="1" Padding="4,2" BorderBrush="DarkGray" CornerRadius="3" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Name="content"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I copied your code into a new Visual Studio 2010 project and ran it successfully (.NET 4.0).
I believe the problem is a bug with XAML Cruncher.

WPF Combobox Validation.ErrorTemplate error

I have a combobox that i need to edit its error template to show a red border when there is a validation error.
I am using the following style
<Style TargetType="{x:Type ComboBox}" >
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="3">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
The border never shows up when validation errors occur. Any tips what is going wrong?
The Style you posted works. You should check your binding, did you add ValidatesOnDataErrors=True and ValidatesOnExceptions=True to the binding of SelectedValue?
enter code heretry without the dock panel, that is uneuseful since it wraps jus one element. However, sicnecerely I don't wnow if it makes sense to wrap a textbox with a border, since it has already a border! You should try to change directly the colour of its border. You could try to use again the panel but then put the border around the panel ie:
Border BorderBrush="Red" BorderThickness="3"
DockPanel
AdornedElement
This makes more sense because the wrap panel has not its own border.
Use This.
<Style x:Key="textBoxStyle" TargetType="{x:Type telerik:RadMaskedTextBox}">
<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="Control.BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
I don't like any of the responses here. Simply put, how do you change the border color for the error template for a ComboBox using Blend or not? It shouldn't be acceptable to draw another border around the existing border of the ComboBox. I've figured out how to creat a ControlTemplate in Blend but not a Validation Template.
I've come close with trying to make it appear like I've changed the actual border color, but that's not what I actually want. Suggestions? To complicate it a bit, I'd like to display a red asterisk outside of the right border of the control.
The following code is a close attempt, but it is actually drawing a border inside the ComboBox and if you look close, you can see that the border is 2 pixels wide when combined with the ComboBox border:
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder>
<Border BorderBrush="Blue" BorderThickness="1" CornerRadius="2" />
</AdornedElementPlaceholder>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
I searched around some more and came up with a solution based on another article here: WPF - How to apply style to AdornedElementPlaceholder's AdornedElement?
<!-- This works -->
<ComboBox Name="comboBox1" Style="{StaticResource NewComboBoxStyle}" Validation.ErrorTemplate="{StaticResource comboBoxValidationTemplate}" />
<SolidColorBrush x:Key="MainBorderBrush">#FF91B3FF</SolidColorBrush>
<Style x:Key="NewComboBoxStyle" TargetType="{x:Type ComboBox}" BasedOn="{StaticResource myErrorTemplate}">
<Setter Property="BorderBrush" Value="{DynamicResource MainBorderBrush}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
<!-- Sets ToolTip when Validation.HasError is True. -->
<Style TargetType="Control" x:Key="myErrorTemplate">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="comboBoxValidationTemplate">
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder/>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
</ControlTemplate>

Resources