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>
Related
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.
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.
<dg:DataGrid.Resources>
<ViewModel:SmartMessenger x:Key="Noitemsfound">
</ViewModel:SmartMessenger>
</dg:DataGrid.Resources>
<dg:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>`enter code here`
<TextBlock Text="{Binding Source={StaticResource Noitemsfound }, Path=pNorecords,Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</dg:DataGrid.RowDetailsTemplate>
Here i am trying to bind a textblock that will display a message when there are no items in the datagrid . I am using VS 2008 Express edition.
The problem is am not able to bind the property Noitemsfound with a code behind class SmartMessenger ... Whats missing here???
if you just want to display a message in your datagrid when there are no items - you can do this with a style. Put this style in your App.xaml Resources or at least in your datagrid Resources.
<Style x:Key="{x:Type ItemsControl}" TargetType="{x:Type ItemsControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="no items"
FontFamily="{StaticResource FontFamily}"
FontSize="{StaticResource FontSize}"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Items, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="no items"
FontFamily="{StaticResource FontFamily}"
FontSize="{StaticResource FontSize}"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="{x:Type DataGrid}" TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type ItemsControl}}">
</Style>
Check output window. WPF dumps binding error/exception occurred to output windows. There you gets the chance to verify if it is binding to right source, if path is correct etc.
You can also try adding dummy converter in the binding expression. Converter provides you chance to debug binding expression
I would think this is possible, but the obvious way isn't working.
Currently, I'm doing this:
<ContentControl
Content="{Binding HurfView.EditedPart}">
<ContentControl.Resources>
<Style
TargetType="ContentControl"
x:Key="emptytemplate">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}"
Value="{x:Null}">
<Setter
Property="ContentControl.Template">
<Setter.Value>
<ControlTemplate>
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBlock>EMPTY!</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Resources>
</ContentControl>
I'm not getting any binding errors and this compiles. However, it doesn't produce the expected result. I've also tried the obvious:
<DataTemplate DataType="{x:Null}"><TextBlock>Hurf</TextBlock></DataTemplate>
This won't compile. And attempting to set the content twice fails as well:
<ContentControl
Content="{Binding HurfView.EditedPart}">
<TextBlock>DEFAULT DISPLAY</TextBlock>
</ContentControl>
Can I do this without writing a custom template selector?
Simple, you have to bind the content property in the style. Styles won't overwrite a value on a control if there's a binding present, even if the value evaluates to Null. Try this.
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{Binding HurfView.EditedPart}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}" Value="{x:Null}">
<Setter Property="ContentControl.Template">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock>EMPTY!</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Since I stumbled upon this question and had the same problem today, I wanted to contribute another way how I solved the problem. Since I did not like to add another style trigger I used the property TargetNullValue which seems to be a bit more readable than the accepted solution (which works nevertheless):
<ContentControl>
<ContentControl.Content>
<Binding Path="ContentViewModel">
<Binding.TargetNullValue>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock>EMPTY!</TextBlock>
</Grid>
</Binding.TargetNullValue>
</Binding>
</ContentControl.Content>
</ContentControl>
You could return DBNull.Value as the FallbackValue of the Binding for the Content of the ContentControl, and create a DataTemplate for DBNull :
<DataTemplate DataType="{x:Type system:DBNull}">
<!-- The default template -->
</DataTemplate>
...
<ContentControl Content="{Binding HurfView.EditedPart, FallbackValue={x:Static system:DBNull.Value}}" />
I'm trying to apply a style to an adorned element, but I don't know the correct syntax. Here is what I've tried:
<!-- ValidationRule Based Validitaion Control Template -->
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder Style="textStyleTextBox"/>
</DockPanel>
</ControlTemplate>
The only problem is that the following line doesn't work:
<AdornedElementPlaceholder Style="textStyleTextBox"/>
Any help would be greatly appreciated.
Thanks,
-Charles
Need to put where the resource is coming from.
<TextBox Style="{StaticResource textStyleTextBox}"/>
Then define the style in a resource such as the user control resources:
<UserControl.Resources>
<Style TargetType="TextBox" x:Key="textStyleTextBox">
<Setter Property="Background" Value="Blue"/>
</Style>
</UserControl.Resources>
However I dont believe you want to set the style of the adornedelement within the placeholder. It's just a placeholder for any control with that template. You should set the style of the adornedelement in the element itself like the example I provided above. If you want to style the control based upon it's validation then something like this:
<Window.Resources>
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Yellow" Width="55" FontSize="18">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel x:Name="mainPanel">
<TextBlock>Age:</TextBlock>
<TextBox x:Name="txtAge"
Validation.ErrorTemplate="{DynamicResource validationTemplate}"
Style="{StaticResource textBoxInError}">
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<ExceptionValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox>
</StackPanel>