Tooltip for Textbox with maxlength property - c#

I want to make a tooltip for my TextBoxes in XAML, and i want do do this with styling in Xaml.
The Tooltip should display something like Enter up to x characters, with x equal to the MaxLength property of the textbox. I only want to display the tooltip if the MaxLength is set.
What i have now is something like:
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource valueLargerThanZero}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Margin="0" Text="{Binding MaxLength, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TextBox}}, StringFormat='Enter up to {0} characters'}"/>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
The condition is working, so if there is a MaxLength set the tooltip is displayed. Only the binding inside the tooltip is not working.
Other stuff that i've tried is:
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource valueLargerThanZero}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content="{Binding MaxLength, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TextBox}}}" ContentStringFormat="{}Enter up to {0} characters"/>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
This gives the same problem as the option shown above, but i cant style the tooltip properly.
Another thing i've tried is:
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource valueLargerThanZero}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip" Value="{Binding MaxLength, RelativeSource={RelativeSource Self}, StringFormat='Enter up to {0} characters'}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
But this way the string formatting won't work.
Something i've tried before and that has worked was:
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MaxLength, Converter={StaticResource valueLargerThanZero}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Margin="0" Text="{Binding MaxLength ,StringFormat='Enter up to {0} characters'}"/>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
But this way the DataContext for the TextBox, and my other bindings dont work anymore.
Does anyone have a solution that (favorably) only includes XAML?
EDIT
While using a converter for MaxLength to the text Enter up to MaxLength characters it worked.
I used:
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource valueLargerThanZero}}" Value="True">
<Setter Property="ToolTip" Value="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource textBoxToolTipConverter}}"/>
</DataTrigger>
</Style.Triggers>
</Style>

MultiDataTrigger, as name implies, is meant to be used with many conditions. You actually never used more than one. You can leverage of couple options:
Create style for textBox and within set default ToolTip to "Enter up to.." and in style trigger check whether MaxLength equals either null or 0, if so set ToolTip empty.
Bind ToolTip property to MaxLength and in converter assign value based on whether MaxLength is set
EDIT
<TextBox Text="Text" MaxLength="55">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=MaxLength,StringFormat='Enter up to {0} characters'}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=MaxLength}" Value="0">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
This will display maxLength since ToolTip needs to have StringFormat set in diffrent way. I refer you to this post. All in all you end up being forced to create converter.

You can try this for the first code example.
Update added full style code for clarification.
<Style x:Key="ToolTipTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource StandardTextBox}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MaxLength, RelativeSource={RelativeSource Self}, Converter={StaticResource valueLargerThanZero}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Margin="0" Text="{Binding DataContext.MaxLength, RelativeSource={RelativeSource Mode=Self}, StringFormat=Enter up to {0} characters}"/>
</Setter.Value>
</Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>

Related

Enabling TextBox with trigger

I want a Textbox to be disabled, except the user hovers with the mouse over it. But this does not seam to work, the Textbox is still not editable if I hover over it. (Yes I know that this is unusual.)
What I tried is:
<Border>
<TextBox Text="Hover to edit me!" IsEnabled="False" >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Border}, Path=IsMouseOver}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
<!-- this does not work either -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
<!-- neither did this -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEnabled" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
I wonder if it is possible at all using a trigger?
This is because of the Dependency Property Value Precedence in WPF.
You need to set the IsEnabled property to false in the Style, so the trigger can override the value.
The local value has always a higher priority, so setting IsEnabled="False" directly in the text box will override any style or style trigger value.
Here is a working example:
<Border>
<TextBox Text="Text">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsMouseOver,
RelativeSource={RelativeSource FindAncestor, AncestorType=Border}}"
Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>

Finding the Content of a ContentPresenter and setting styles

I couldn't find an appropriate topic for my problem. I have the following code.
<ContentPresenter x:Name="ContentPresenter"
VerticalAlignment="Center"
Width="Auto"/>
<TextBox x:Name="TextBlockFront" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=TextBlockFront, Path=IsFocused}" Value="true"/>
<Condition Binding="{Binding ElementName=ContentPresenter, Path=ContentProperty}" Value="ComboBox"/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Hidden"/>
<Setter Property="Text" Value="{Binding ElementName=ContentPresenter,Path=SelectedItem}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
I want to get the clarification about the second condition of my MultiTrigger. I am relative new to WPF and I would appreciate any help in sorting out my doubt.
Expected behaviour:
when the TextBox is focused AND the ContentPresenter is ComboBox then I want to set the above properties. If the ContentPresenter is TextBox then I will set the Text property instead of SelectedItem

Text.IsEmpty Opposite

i have this CheckBox
<CheckBox VerticalAlignment="Center" Name="CheckItem"
IsChecked="{Binding ElementName=Faccette, Path=Text.isEmpty, Mode=OneWay}"
/>
Faccette is a TextBlock, i need the opposite of the Text.isEmpty.
Is it possible without a Converter?
You may use a Style with DataTriggers:
<CheckBox>
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Faccette, Path=Text}" Value="">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Faccette, Path=Text}" Value="{x:Null}">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>

How to add parameters to an existing style

I have the following button in my XAML:
<Button Content="ADD"
Style="{DynamicResource MaterialDesignFlatButton}" />
I would like to keep the style and create a validation rule for it, like if some textbox is empty it should disable the button and set a tooltip on it.
So I tried this:
<Button Content="ADD"
Command="{Binding AddDateCommand}"
VerticalAlignment="Top"
Margin="8 0 0 0">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatButton}">
<triggers to check the rules and set the attributes/>
</Style>
</Button.Style>
</Button>
But I'm getting the following error:
Can only base on a Style with target type that is base type
'IFrameworkInputElement'
You will get an error : Style can be set only once in your present code.
You have to use TargetType with BasedOn styles.
So, define your style should be conceptually like this :
<Style x:Key="MaterialDesignFlatButton" TargetType="Button">
<Setter Property="Background" Value="Red"/>
</Style>
<Style x:Key="NewStyle" BasedOn="{StaticResource MaterialDesignFlatButton}"
TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=tb, Path=Text.Length, Mode=OneWay}" Value="10"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
<Setter Property="BorderBrush" Value="Cornsilk"/>
<Setter Property="BorderThickness" Value="15"/>
<Setter Property="Foreground" Value="MediumPurple"/>
<Setter Property="FontSize" Value="15"/>
</Style>
use IsEnabled property of the button if you have one TextBox:
<Button Content="ADD"
Command="{Binding AddDateCommand}"
VerticalAlignment="Top"
Margin="8 0 0 0"
IsEnabled="{Binding ElementName=yourTxtBox, Path=Text.Length, Mode=OneWay}" />
But for two TextBoxes you should use:
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=firstTxtBox, Path=Text.Length, Mode=OneWay}" Value="0"/>
<Condition Binding="{Binding ElementName=lastTxtBox, Path=Text.Length, Mode=OneWay}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>

How to use the same DataTrigger for multiple DataTemplate?

I want to set a DataTrigger for multiple DataTemplate but I am not sure if there is way to do that?
I have view structured like below
<ItemsControl ItemsSource="{Binding Collection}"
AlternationCount="2"
ItemTemplateSelector="{StaticResource RowSelector}" >
<local:HeaderTemplateSelector x:Key="RowSelector"
NormalTemplate="{StaticResource NormalTemplate}"
ExpanderTemplate1="{StaticResource ExpanderTemplate1}"
ExpanderTemplate2="{StaticResource ExpanderTemplate2}"
ExpanderTemplate3="{StaticResource ExpanderTemplate3}" />
<DataTemplate x:Key="NormalTemplate" ...>
<stackpanel x:Name="main"
<DataTemplate x:Key="ExpanderTemplate1" ...>
<Grid x:Name="main" ..>
<DataTemplate x:Key="ExpanderTemplate2" ...>
<Expander x:Name="main"
<DataTemplate x:Key="ExpanderTemplate3" ...>
<Textblock x:Name="main" ...>
I want this data trigger which provides alternative background for rows, the trigger defined below
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="Blue" TargetName="main"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="black" TargetName="main"/>
</Trigger>
</DataTemplate.Triggers>
I can add this to the end of each DataTemplate and it works fine but it mean that I have to duplicate the some code 4 times, is there any way to avoid that? (I know I can use datagrid but it is overkill for this simple structure)
You can try to set the trigger with the style:
<Style TargetType="Panel" x:Key="AlternatelyPainted">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
(code thankfully stolen from this answer) and use it in your templates:
<DataTemplate x:Key="NormalTemplate" ...>
<StackPanel Style="{StaticResource AlternatelyPainted}">
...
</StackPanel>
</DataTemplate>
etc.
Edit: if you want the style to be universal, not Panel-specific, you can try the following trick:
<Style x:Key="AlternatelyPainted">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Panel.Background" Value="Red"/>
<Setter Property="Control.Background" Value="Red"/>
<Setter Property="TextBlock.Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Panel.Background" Value="Blue"/>
<Setter Property="Control.Background" Value="Blue"/>
<Setter Property="TextBlock.Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
(removed the TargetType and added explicit types to the property names). Note that TextBlock doesn't inherit its Background, so we need an additional line for it.

Categories