Adding Popup Border Animation around TextBox in C# wpf - c#

I am currently developing a WPF application. When the user submits the form I have a method which checks to ensure that the fields are filled in and what I want to do is, if they are not, display a red to white gradient going around the box as a fade in animation.
Is this possible?

Use validation in your binding, and then assign a validation error template to the TextBox. Here's one that does a red rectangle:
<ControlTemplate x:Key="errorTemplate">
<Canvas Width="{Binding Path=AdornedElement.ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}"
Height="{Binding Path=AdornedElement.ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}">
<Border BorderBrush="Red" BorderThickness="1" >
<AdornedElementPlaceholder/>
</Border>
</Canvas>
</ControlTemplate>
Add this to the binding: Validation.ErrorTemplate="{StaticResource errorTemplate}

You can use a style and use data trigger to achieve this. This way, when your textbox is empty, you will see a red border and light red background. See the sample code below:
<Style x:Key="RequiredField" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">
<Setter Property="TextBox.BorderBrush" Value="{StaticResource MySolidBrush}" />
<Setter Property="TextBox.Background" Value="{StaticResource MyInnerBrush}"/>
<Setter Property="TextBox.ToolTip" Value="This Field is Mandatory"/>
</DataTrigger>
</Style.Triggers>
</Style>

Related

Border thickness using style in WPF

I am making a little project, in which I am using data validation. Now I am trying to make styling for TextBox, when the input is not correct. I want to make a red border around the TextBox and a ToolTip with error message, which I return from C# code.
I started with making the red border. I wrote this in XAML:
<Window.Resources>
<Style x:Key="ErrorTemplate" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"></Setter>
<Setter Property="BorderThickness" Value="5"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
But if I write it like this, BorderThickness property is changing inner border thickness of the TextBox. With the code above, I get this:
Is there a way, to avoid this, and get something like this?
And in a way, that I can add that ToolTip, using the same Style.
EDIT:
Code of TextBox:
<TextBox
Grid.Row="1"
Grid.Column="3"
Margin="10px"
FontSize="14pt"
VerticalAlignment="Center"
Padding="5px"
Text="{Binding Path=Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource ErrorTemplate}">
<TextBox.DataContext>
<local:Data/>
</TextBox.DataContext>
</TextBox>
The blue border that you see is the border of the TextBox itself, which is light blue by default in its keyboard focused state. The border is 5 dips thick, because you explicitly set it in your provided ErrorTemplate style. The red line around it is the default error template of the TextBox.
In order to meet your requirements, create a style like below.
Set a general Margin of 5 dips to account for the border that is displayed on error, it could otherwise be cut off outside the container since it is just an overlay over the original TextBox and it is not resized.
Set an optional default tool tip for the non-error state.
Set an ErrorTemplate, which is a special template dedicated to the error state, where AdornedElementPlaceholder represents the original TextBox in the template.
Set a trigger that changes the tool tip text to the validation error in error state.
<Style x:Key="MyTextBoxValidationStyle" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="ToolTip" Value="There is no error."/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Border BorderBrush="Red" BorderThickness="5">
<AdornedElementPlaceholder/>
</Border>
</StackPanel>
</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>
Apply the style to your TextBox using the Style property.
<TextBox Style="{StaticResource MyTextBoxValidationStyle}" ...>
Here is a sample screenshot of the result.

Change Button's image appearance when enabled

Currently I have buttons in WPF but I want them to appear lighter when they are disabled and full colored when they are enabled. Is there an option to make the image lighter automatically, or I need to edit them in inkscape and import as a completely different image and change the image Source in the ViewModel.
The code currently being used by one of the buttons:
<Button Visibility="{Binding SettingsButtonIsEnabled}" Command="{Binding OnSaveProject}" VerticalAlignment="Top" ToolTip="{DynamicResource SaveProject}">
<Image RenderOptions.BitmapScalingMode="HighQuality" Source="/Axon.Oscillographic.Viewer;component/Icons/save.png"/>
</Button>
One possible solution would be to reduce the image Opacity, when button is disabled. This could be done via Style with a DataTrigger:
<Image RenderOptions.BitmapScalingMode="HighQuality" Source="/Axon.Oscillographic.Viewer;component/Icons/save.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}"
Value="False">
<Setter Property="Opacity" Value="0.75"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>

How to bind ToolTip content in a custom textblock styles property setter

I am trying to create a textblock that shows a tooltip of the textblock's text when its being trimmed. I have the actual visibility part of this taken care of in a converter. My problem is binding the ToolTip's content to its partent textblock's text. I have been fiddling with different relative paths for awhile now and can never get anything but a blank tooltip. The text shows up fine when I want if I hardcode something in the tooltips content.
<Style x:Key="InfoToolTipBaseTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockBase}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimmedVisibilityConverter}}" Content="{Binding Path=Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBlock}}}"/>
</Setter.Value>
</Setter>
</Style>
Instead of creating a tooltip that's always there, setting its content, and then switching its visibility, just have a style trigger on the TextBlock that sets the ToolTip property to Text when needed.
I've used your existing converter, but you might want to rewrite it to return bool. Then you'd have Value="True" in the DataTrigger.
<Style
x:Key="InfoToolTipBaseTextBlockStyle"
TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource TextBlockBase}"
>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource TrimmedVisibilityConverter}}"
Value="Visible"
>
<Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Self}}" />
</DataTrigger>
</Style.Triggers>
</Style>
I don't know if this is an issue for you, but if Text changes at runtime, the the tooltip won't be updated, because that Binding doesn't know you care about the Text property. The fix for that would be to rewrite the converter as IMultiValueConverter so you can use it with a MultiBinding that would have bindings to Text as well as Self. It wouldn't have to use Text, but it would update the target when Text changes.
A better solution, simpler and WPFish, would be to write a behavior for TextBlock that recycles the guts of your converter, handles change notifications on Text, and updates an attached TextBlockExt.IsTextElided bool property on the TextBlock.
Solved it simply by this.
<Style x:Key="InfoToolTipBaseTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockBase}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimmedVisibilityConverter}}"
Content="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Text}"/>
</Setter.Value>
</Setter>
</Style>

Why is this WPF Button background not changing behind an image?

My squares are either SaddleBrown or WhiteSmoke in color, shifting to DarkTurquoise when selected. This works when there is no image on top of the squares. When I have a (PNG) image on top of the square the original SaddleBrown/WhiteSmoke color shows behind it but when the color is supposed to change to DarkTurquoise nothing happens to the background color of the square.
What could be the problem?
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="Square"
Command="{Binding DataContext.BoardGUI.SquareClickCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid Background="{TemplateBinding Background}">
<Image Source="{Binding Source, Converter={StaticResource NullImageConverter}}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding SquareColor}" Value="Dark">
<Setter TargetName="Square" Property="Background" Value="SaddleBrown"/>
</DataTrigger>
<DataTrigger Binding="{Binding SquareColor}" Value="White">
<Setter TargetName="Square" Property="Background" Value="WhiteSmoke"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="Square" Property="Background" Value="DarkTurquoise"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
As determined in the comments, based on the screenshot and the definition of the triggers, it appears that "SquareColor" is likely getting set or changed after IsSelected is set.

TemplatedParent is null when used inside a ControlTemplate's DataTrigger

Consider this (edited-down) Style, designed for a Button whose Content is a String:
<Style x:Key="Test" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel>
<TextBlock x:Name="text" Text="{TemplateBinding Content}" />
<TextBlock x:Name="demo" Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
<DataTrigger.Value>
<system:String>Test</system:String>
</DataTrigger.Value>
<Setter TargetName="test" Property="Foreground" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The intention in this example is to turn the button text red if it equals the word "Test"1. But it doesn't work, because the trigger's TemplatedParent binding resolves to null instead of to the Button the Style is applied to. However, the TextBlock named "demo" will have its Text set to "System.Windows.Controls.Button: [ButtonText]" as expected, which means TemplatedParent works correctly at that level. Why doesn't it work inside the DataTrigger?
1 I know there are other ways to achieve that, but I'm trying to understand why the binding doesn't work the way I expect it to.
TemplatedParent in your ControlTemplate.Triggers is not what you expect. Inside trigger it actually references Button.TemplatedParent. As such, it will only be non-null if your create that button inside template. You don't create button inside template, so it is null in your case. Now consider this xaml:
<Window.Resources>
<Style x:Key="Test"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel>
<TextBlock x:Name="text"
Text="dummy" />
<TextBlock x:Name="demo"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}">
<DataTrigger.Value>
<system:String>Test</system:String>
</DataTrigger.Value>
<Setter TargetName="text"
Property="Foreground"
Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Test2" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Button Style="{StaticResource Test}"></Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!--<Button Content="Test" Style="{StaticResource Test}"/>-->
<ContentControl Style="{StaticResource Test2}" Content="Test" />
</Grid>
Here I retemplate ContentControl and inside template I use button with your template. If you run this code, you will see "dummy" text in red, because Button.TemplatedParent is now ContentControl, and it has it's Content equals "Test", which confirms what I said above.
Now back to your problem: just change RelativeSource TemplatedParent to RelativeSource Self (no need to change DataTrigger to Trigger) - this one would reference your Button.
I think it might be a similar issue in .NET Core WPF.
My DataTrigger was not firing with {Binding MyProp, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Convert}}, but instead when I changed the RelativeSource to Self the binding started to work. I'm not sure whether it's a hack or a solution, but it worked.
Maybe it's worth mentioning that my template was based on MyView (see below) and I was binding to a DependencyProperty on MyView.
So my final code looked like this:
<ControlTemplate x:Key="Template" TargetType="{x:Type ns:MyView}">
<!-- Some template -->
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding MyProp, RelativeSource={RelativeSource Self}, Converter={StaticResource Convert}}" Value="True">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I'm not quite sure, but I think the trigger equals by referenc, because Content returns an Object. So it will never be true with your string defined within the trigger.

Categories