Wpf TextBox style with tooltip - wpf

I want my text boxes to show a specific text as tool tip depending on the validation status. To do this I currently use the following code and it works:
<TextBox Name="textBox1">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="False">
<Setter Property="ToolTip" Value="{Binding XPath=HelpText/Description, Mode=OneTime}" />
</Trigger>
<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>
</TextBox.Style>
</TextBox>
When there is no error, a description text read from an xml-file is displayed and when there is an error the validation error string is displayed.
The problem is that now I have to copy this style and apply it to every textbox (and change the XPath, it is unique for each text box), what I would want to do is put the style in Window.Resources so that I can just reference it for every text box like this:
<TextBox Name="textBox2" Style="{StaticResource TextBoxStyle}" />
But the XPath will be different for each textbox so I cannot just place my style in Window.Resources. How can I make an instance specific XPath available in the style? Is there a way to do this without creating a new text box and derive from TextBox? I was wondering if one could use an attached property in this case but I don't know how they work.
Thanks in advance.

I haven't completely tested this, but you could use an attached property
Something like:
class ToolTipHelper {
public static readonly DependencyProperty ToolTipProperty =
DependencyProperty.RegisterAttached("ToolTip", typeof(string) ...
Then you modify your trigger:
<Trigger Property="Validation.HasError" Value="False">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(ToolTipHelper.ToolTip}, Mode=OneTime}" />
</Trigger>
And you use it like
<TextBox ToolTipHelper.ToolTip="{Binding XPath=HelpText/Description, Mode=OneTime}" />

You could use the Tag Property and change your style to
<Style TargetType="{x:Type TextBox}">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" />
<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>
Then you bind the desired XPath to the Tag property of each specific TextBox
<TextBox Tag="{Binding XPath=HelpText/Description, Mode=OneTime}" />

Related

binding a checkbox value with a textblock text on xaml

I want to bind a checkbox values ischecked=true or ischecked=false with a textblock text "Activé" or "Désactivé" only on Xaml and without using a code behind is that possible?
Check out datatriggers.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.datatrigger?view=netframework-4.7.2
https://www.wpf-tutorial.com/styles/trigger-datatrigger-event-trigger/
And here is an example that should work for you.
<CheckBox Name="MyCheckBox" Content="IsActive"/>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Activé"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyCheckBox, Path=IsChecked}" Value="False">
<Setter Property="Text" Value="Désactivé"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

Bind DataGridCell style directly to its contents

I have a StatusCell style for DataGridCell that I would like to use in several place in my application. I would like to externalize the Style tag so that I can reuse it easily without having to duplicate the code in my XAML everywhere.
Every other source I've found has required me to bind the trigger off the property from my ViewModel. But across the application, the column might be bound to MyStatusProperty or SubObject.MyStatusProperty, etc, so I want to do this to allow me to have one style that will apply to all of these without having to specify where it's binding from.
I am able to do this with a TextBlock with the following style. This lets me bind the TextBlock to whatever I want and the style binding doesn't matter where it's coming from.
<Style x:Key="StatusLabel" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="Completed">
<Setter Property="Foreground" Value="Green"/>
</Trigger>
</Style.Triggers>
</Style>
and when I create a textblock that I want to use this styling, all I have to do is
<TextBlock Style="{StaticResource StatusLabel}" Text="{Binding Whatever}" />
But with a DataGridCell it doesn't let me do this
<Style x:Key="StatusCell" TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="Content" Value="Completed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
<DataGridTextColumn Header="Status" Binding="{Binding MyStatusProperty}"
CellStyle="{StaticResource StatusCell}" />
I also tried setting up the trigger like this:
<DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource Self}}" Value="Reviewed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</DataTrigger>
But neither of these work. I have also tried swapping out "Content" in the last example for "Binding" and "Text"
Is there another property I can bind to in the DataGridCell that will let bind the style trigger to the contents of the cell without knowing the binding path?
As usual, I found a workaround shortly after asking. Since it's working with TextBlocks, I just have to use TemplateColumns instead of TextColumns, although I'd still prefer to be able to use TextColumns since they'd use 6 less lines of XAML.
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyProperty}" Style="{StaticResource StatusCellTextBlock}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Using the style:
<Style x:Key="StatusCellTextBlock" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="Completed">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>

TextBox trigger to clear Text using a style

First, let me say I've been working with WPF for about a week. I want to style a TextBox so that when it is disable, it is cleared. This article explained how to do it, however I'm confused on how to set the generic style as a resource so that every TextBox can bind to a different property without repeating the style for each TextBox.
<Window.Resources>
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{What do I really put here?}" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
....
<TextBox Style="{StaticResource style1}" Text="{Binding SomeProperty}"/>
Thanks!
You won't be able to use the Text property like that. Setting the Text property explicitly on any TextBox that has that style will override the Text setter in the trigger (like you noticed).
If you only need the TextBox to be cleared and not the property it is binding to, then a workaround is to use an attached property (or Tag) for the text which you bind Text to in the Style.
Example..
<Style TargetType="{x:Type TextBox}" x:Key="style1">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self},
Path=Tag}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Then a TextBox can use this Style like
<TextBox Style="{StaticResource style1}" Tag="{Binding SomeProperty}" />

Using Picture or Icon instead of DataGridCheckBoxColumn in WPF

I want to change the CheckBox which is inside of a DataGridColumn into an image when it is checked and another when it is unchecked, How can i do ?
Ps: My DataGridCheckBoxColumn is defined like this:
<DataGridCheckBoxColumn Header="Priority" Binding="{Binding PRIORITY, Converter={StaticResource converter}}"/>
Converter is converting bytes to boolean.
Use the ElementStyle and EditingElementStyle properties to create and set a different Template for the CheckBox which fits that.
e.g.
<DataGridCheckBoxColumn Binding="{Binding IsActive}">
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Image MaxWidth="32" MaxHeight="32">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Images/Error.ico" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}}" Value="True">
<Setter Property="Source" Value="Images/Default.ico" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
This makes the column display an image based on IsChecked, the URIs are just hardcoded and the CheckBox is disabled because editing in the ElementStyle does not change any properties on the bound object. Its only purpose is to display the approriate image.
(The EditingElementStyle is not set here so if the user clicks the cell again to edit it a normal CheckBox appears which can be checked or unchecked.)

Use a property trigger to change a property that already has a binding

How can I use a property trigger in a style (or another method) to change a property (ToolTip for example) that already has its value defined by a binding?
I have a simple button like so:
<Button Name="Button1" Style="{StaticResource ButtonStyle}"
ToolTip="{Binding Name}" >My Button</Button>
It has a binding on the tooltip to show the Name property of the class set as DataContext.
My problem is I want to show the Name when the button is enabled, but something else when it is disabled. I thought I could get around my problem with a style and a trigger like so:
<Style TargetType="Button" x:Key="ButtonStyle">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ToolTip" Value="Disabled" />
</Trigger>
</Style.Triggers>
</Style>
But this doesn't work. If I removed the tooltip binding from the button then I do get the correct tooltip when the button is disabled. But it seems I can't have both a binding and a trigger on the same property.
I could get around it by adding another trigger like so:
<Trigger Property="IsEnabled" Value="true">
<Setter Property="ToolTip" Value="{Binding Name}" />
</Trigger>
But I would like to use the style for 4 or 5 buttons that will all have different bindings for the enabled ToolTip, but the same (fixed) tooltip when they are disabled.
Any suggestions?
The easiest solution to your problem is to create a button style that all of your buttons can use (_DisabledButtonToolTipStyle in the example below) and then define a unique style for each button that applies the enabled tool tip value. If the individual buttons always have a different tool tip value then I'd recommended simply embedding the style like below; however, if you wanted to reuse the enabled tool tip style you could easily define it in your resources and give it a key.
<Window.Resources>
<Style x:Key="_DisabledButtonToolTipStyle" TargetType="Button">
<Setter Property="ToolTipService.ShowOnDisabled" Value="True" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="ToolTip" Value="Disabled" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Name="Button1" Content="My Button">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource _DisabledButtonToolTipStyle}">
<Setter Property="ToolTip" Value="{Binding Name}" />
</Style>
</Button.Style>
</Button>
</Grid>
Your problem has nothing to do with binding, the problem is that properties you set directly on the element override the properties set in a style, for example:
Given the style
<Style TargetType="Button">
<Setter Property="Background" Value="Blue"/>
</Style>
And those two buttons:
<Button Content="1st"/>
<Button Content="2nd" Background="Red"/>
The first button will get its background from the style but the second overrides the style background.
You can get what you want with triggers but not using styles, you can use a DataTemplate like this (the TextBox and CheckBox are there for testing):
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBox Name="edt" Text="Tooltip text"/>
<Button Name="btn" Content="x"
ToolTip="{Binding ElementName=edt, Path=Text}"
ToolTipService.ShowOnDisabled="True"/>
<CheckBox Content="Enabled"
IsChecked="{Binding ElementName=btn, Path=IsEnabled}"/>
</StackPanel>
<DataTemplate.Triggers>
<Trigger SourceName="btn" Property="IsEnabled" Value="False">
<Setter TargetName="btn" Property="ToolTip" Value="Disabled"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>

Resources