Hide border when TextBox is empty - wpf

I'm trying to have a textbox appear at the end of my game with the winner. I did this using a trigger on the text property that would set the visibility to collapsed when it was empty. Next, I tried adding a border to this textbox. When my textbox is collapsed however (so when the game is still going), the textbox is invisible as before, but the border is already showing as a small black box on the screen.
Does anyone know how I can hide the border until the textbox it's containing isn't empty?
Thanks in advance.
<Border BorderBrush="Black" BorderThickness="2" Canvas.ZIndex="2" Canvas.Left="160" Canvas.Top="225" Background="White">
<TextBlock FontFamily="Helvetica" FontSize="20" FontWeight="Bold"
Text="{Binding WinnerPopup.Value}" Foreground="{Binding WinnerPopup.Value, Converter={StaticResource ownerConverter}}" Padding="15">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>

You can use the following trick to also collapse the border when the TextBlock gets collapsed:
<Border Canvas.Left="160"
Canvas.Top="225"
Background="White"
BorderBrush="Black"
BorderThickness="2"
Canvas.ZIndex="2"
Visibility="{Binding Visibility,
ElementName=myTextBlock}">
<TextBlock x:Name="myTextBlock"
FontFamily="Helvetica"
FontSize="20"
FontWeight="Bold"
Foreground="{Binding WinnerPopup.Value,
Converter={StaticResource ownerConverter}}"
Padding="15"
Text="{Binding WinnerPopup.Value}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>

Related

Custom TextBox not updating property when change

I have a custom TextBox control where the defaul value is the Tag string. When the TextBox is empty it shows a TextBlock on top with the Tag string with other color and centered. The control works on almost every scenario but I am having issues with one specifically. I have a TexBox that need to update a property when I type so I thought that adding the UpdateSourceTrigger=PropertyChanged will update the peroperty, but it doesn't work when I use the custom style, it works only on default so I thought that the issue is on the custom Style, but I am too new on xaml to figure out. Could someone please enlight me on what I am doing wrong?
<TextBox Grid.Column="2" Margin="5" Style="{StaticResource DefaultTextBox}" Tag="Sesión" Text="{Binding Sesion, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
<!--Directorio TextBox-->
<Style x:Key="DefaultTextBox" TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="#ffffff" BorderBrush="#ffacacac" BorderThickness="1" CornerRadius="0">
<Grid>
<TextBox x:Name="TextBoxPersonalizado" Background="Transparent" BorderThickness="0" VerticalContentAlignment="Center" Text="{TemplateBinding Text}"/>
<TextBlock Foreground="#828282" HorizontalAlignment="Center" IsHitTestVisible="False" Text="{TemplateBinding Tag}" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=TextBoxPersonalizado}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Hidden"/>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TemplateBinding is for one direction only.
That beeing said, you need to use RelativeSource inside your template.
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
Then you can use your customized TextBox like this.
<TextBox
x:Name="InputTextBox"
Grid.Column="0"
Width="100"
Margin="5"
Style="{StaticResource DefaultTextBox}"
Tag="Sesión" />
<TextBox Grid.Column="1" Text="{Binding ElementName=InputTextBox, Path=Text}" />

How can I get a strikethrough decoration applied to a label via a trigger?

There are a zillion examples of how to do this to a TextBlock, but I need it on a label. I thought the default template for a label included a TextBlock, so I tried this:
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Included}" Value="False">
<Setter Property="TextBlock.TextDecorations" Value="Strikethrough" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Label Grid.Column="0" BorderThickness="0,1,0,0" BorderBrush="White"
Background="Transparent" Padding="5,0,5,0"
Content="{Binding Path=BillingDefinitionId}"
IsEnabled="{Binding Path=Included}" />
</Grid>
The effect I am going for is when Included is false, the label should be disabled and have strikethrough text. But this is not working for the strikethrough bit.
If I explicity declare the TextBlock, then it seems to work:
<Grid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Included}" Value="False">
<Setter Property="TextDecorations" Value="Strikethrough" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Label Grid.Column="0" BorderThickness="0,1,0,0" BorderBrush="White"
Background="Transparent" Padding="5,0,5,0"
HorizontalContentAlignment="Right" VerticalContentAlignment="Center"
IsEnabled="{Binding Path=Included}">
<TextBlock Text="{Binding Path=BillingDefinitionId}" />
</Label>
I suppose that's fairly simple, not sure why it didn't occur to me prior to posting, but it seems a bit overkill if the default template is a TextBlock… Regardless, it works.

ErrorTemplate for PasswordBox with AttachedProperty

I know that a passwordbox in wpf could not use the Validation.ErrorTemplate, anyhow i have to show the user, that something is wrong.
My Passwordbox has a binding like this
<PasswordBox Name="Password" local:PasswordHelper.Text="{Binding PasswordProp, Mode=TwoWay}" />
Is it possible to get the same style like the default errortemplate (red border) for this passwordbox, if something is wrong?
This is my ErrorTemplate that I use for the other controls
<Style x:Key="baseControlStyle">
<Setter Property="Control.FontFamily" Value="Verdana" />
<Setter Property="Control.FontSize" Value="12" />
<Setter Property="ToolTipService.ShowOnDisabled" Value="True" />
<Setter Property="Validation.ErrorTemplate" >
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Image x:Name="Bild"
DockPanel.Dock="Right"
Source="../Resources/Nein.ico"
Margin="-5 0 0 0"
MaxHeight="16"
MaxWidth="16"
VerticalAlignment="Center"
ToolTip="{Binding ElementName=myControl, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</Image>
<Border BorderBrush="Red" BorderThickness="1" CornerRadius="2">
<AdornedElementPlaceholder x:Name="myControl" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Control.ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Thanks
One solution would be to put an actual TextBox underneath the PasswordBox and bind the Text property to PasswordProp too and give the TextBox the ErrorTemplate:
<Grid>
<TextBox Template="{x:Null}" Style="{StaticResource baseControlStyle}" Text="{Binding PasswordProp, Mode=TwoWay}" />
<PasswordBox Name="Password" local:PasswordHelper.Text="{Binding PasswordProp, Mode=TwoWay}" />
</Grid>
Since the controls of the ErrorTemplate will be put on an adorner layer, your error template will be displayed on top of the PasswordBox event though the TextBox is underneath the PasswordBox.
Also note that I have set the TextBox controltemplate to null. Since it is not supposed to be visible it doesn't need to be rendered.

WPF Validation Errors: Setting Tooltip with Error Message

Why is there no tooltip text on errors?
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Border ...>
<AdornedElementPlaceholder ...
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Border>
...
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also noticed that
<AdornedElementPlaceholder ...
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
fails but the below suceeds, even with the same binding, why is this so? Doesn't AdornedElementPlaceholder refer to the text box? Even if it doesn't, shouldn't a tooltip appear somewhere?
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
I know I'm late, but let me share a solution I found studying this question: WPF custom validator with tooltip.
In it's simplest form this ErrorTemplate shows only a Tooltip with the ErrorContent for the whole AdornedElement.
<ControlTemplate x:Key="validationTemplate">
<Grid Background="Transparent"
ToolTip="{Binding Path=/ErrorContent}">
<AdornedElementPlaceholder />
</Grid>
</ControlTemplate>
But of course you can decorate it as desired e.g. with a Tooltip for just a marker.
<ControlTemplate x:Key="validationTemplate">
<Grid>
<Ellipse Fill="Red" Opacity="0.8" Width="10" Height="10"
HorizontalAlignment="Right" VerticalAlignment="Top"
ToolTip="{Binding Path=/ErrorContent}" />
<AdornedElementPlaceholder />
</Grid>
</ControlTemplate>
Put this Template in Resources and all you have to do is setting the Validation.ErrorTemplate.
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Even this annoying Trigger is no longer needed.
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
You can't place a tooltip on the AdornedElementPlaceholder, I don't think it's visible at all, it's just reserving space for whoever uses it (in your case a TextBox). Looking at the Visual Tree with Snoop we can see that the TemplatedAdorner ends up in a different place in the VisualTree than the TextBox so there will be now way for us to find the TextBox from the VisualTree. We can find it through AdornedElement, but we still won't be able to set a tooltip.
The only thing visible here in the TemplatedAdorner is the Border. The Border knows its Child - the TemplatedAdorner - which in turn knows its AdornedElement - the TextBox. So we could set the ToolTip for the Border with this. (However, this Binding seems to fail to update the Tooltip for the Border. It works when I look at it with Snoop and after that it displays.)
<Border BorderBrush="Red"
BorderThickness="4"
ToolTip="{Binding RelativeSource={RelativeSource self},
Path=Child.AdornedElement.(Validation.Errors)[0].ErrorContent}">
So, the TextBox has its AttachedProperty Validation where we can find the ErrorContent so it must set its own ToolTip like you did at your last example, otherwise it won't work.
I found a way to implement ToolTip with the returned error message from the validation class that you might create to validate your input.
First: Binding the error message
Adding <Style> for the TextBox with Style.Trigger as followed:
<Style TargetType="{x:Type TextBox}" x:Key="ToolTipError">
<!-- Some style setters -->
<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>
Second: Add the style to TextBox
<TextBox
Style="{StaticResource ToolTipError}"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<TextBox.Text>
<Binding
Path="YourViewModelProperty"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnNotifyDataErrors="True"
ValidatesOnDataErrors="True"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule:DateValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Bonus!
You can change the ugly red border with other thing.
For example you can change it to red exclamation mark:
<Window.Resources>
<ControlTemplate x:Key="validationTemplate">
<StackPanel>
<TextBlock Text="!" FontSize="26" Foreground="Red"/>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
<Window.Resources>

How to make a text box Visibility=Hidden with a trigger

I seem to be having a hard time today. All I want to do is make a TextBox hidden of visible based on a bool value databound to the Window its hosted in.
What I have just won't compile and I don't understand why. Please help.
<TextBlock Grid.Column="2" Text="This order will be sent to accounting for approval"
Foreground="Red" VerticalAlignment="Center" FontWeight="Bold" Padding="5">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AllowedToSubmit}" Value="True">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
You need to set the Style.TargetType in order for it to recognize the Visibility property:
<TextBlock Grid.Column="2" VerticalAlignment="Center" FontWeight="Bold" Foreground="Red" Padding="5" Text="This order will be sent to accounting for approval">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AllowedToSubmit}" Value="True">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Your binding path to AllowedToSubmit probably needs to have ElementName set to the Window's name, as well.
Another option is to bind TextBlock.Visibility directly to the property:
<Window>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
</Window.Resources>
<TextBlock Visibility="{Binding Path=AllowedToSubmit, Converter={StaticResource BoolToVisibility}}" />
</Window>
If you want it to work like in your sample, where true hides the TextBlock, then you can write your own converter to convert opposite of the built-in BooleanToVisibilityConverter.

Resources