i have a base style and a style in wpf.
the base style is:
<Style x:Key="BaseTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#DDFFDD" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="behaviors:OCCInteraction.Triggers" Value="{StaticResource ResourceKey=validationTrigger}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="#FFDDDD"/>
<Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
And the the specific style is:
<Style x:Key="EditableTextBox" TargetType="{x:Type TextBox}" BasedOn="{StaticResource ResourceKey=BaseTextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Red" BorderThickness="1" Padding="0" Margin="0">
<AdornedElementPlaceholder Margin="0"/>
</Border>
<TextBlock Text="test" />
<Image Style="{StaticResource ResourceKey=WarningImage}"/>
<TextBlock Text="{Binding Path=(Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Mode=Self,AncestorLevel=2}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now i want to add the Validation Error Text to a textblock next to the image. But the Same Binding Path doesn't work. I've tried diferent bindings, but I can't figure out how to access the same binding like on the base style.
Thanks for help :)
Have you tried it without the AncestorLevel? You should be the same object.
You cannot use Mode=Self and AncestorLevel properties. Just use Mode=Self.
Ancestor level is used when you try to reach parent of that control in visual tree.
Related
I have a lot of labels on my wpf App like this.
<Label Style="{StaticResource styleLabelTitle}">
<TextBlock TextTrimming="CharacterEllipsis" Text="{localization:Translate geolocation_controls}">
</TextBlock>
</Label>
I want to add a tooltip to show complete name when ellipsis is working. So i add the tooltip in the label style.
<Style x:Key="styleLabelTitle" TargetType="Label" x:Shared="False">
<Setter Property="Foreground" Value="{StaticResource brushTextsForeground}"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="FontFamily" Value="Consolas"></Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
</Style>
The problem is that I think when tooltip is appearing is changing the textblock parent. So the text is only appearing in the Tooltip and is removed from the original label.
Any ideas?
Thanks in advance.
I finally came to a solution by doing a new style.
<Style x:Key="styleLabelText" TargetType="{x:Type Label}"
x:Shared="False">
<Setter Property="Foreground" Value="{StaticResource brushTextsForeground}"></Setter>
<Setter Property="FontWeight" Value="Normal"></Setter>
<Setter Property="FontFamily" Value="Consolas"></Setter>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"
Text="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter> </Style>
You only have an instance of the textblock, then when you assign it to the tooltip the content is lost as you said.
You should set the value of the tooltip with the translated text out of the style:
<Label Style="{StaticResource styleLabelTitle}" ToolTip="{localization:Translate geolocation_controls}">
<TextBlock ...>
</Label>
I have a treeview which has been populated using a dataset. Now I need to add different icons to the treeview nodes. How do I add them using xaml?
You need to add a custom template for the TreeViewItem like this:
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Tag="{Binding}" />
<Image Source="{Binding Converter={StaticResource CustomImagePathConvertor}}" />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
You can change Image Path and use Template to implement it:
<ToggleButton x:Name="Expander" ClickMode="Press">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid Height="16" Width="16">
<Image x:Name="imgBkg" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="imgBkg" Value="/XXXX;component/YourImage.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
The EditCommandButton style defined below is showing odd behavior; it only shows for the last instance defined (next to the Avatar label below).
I tried making the style x:Shared="true" and using DynamicResource but with the same result.
I do eventually want the button to invisible until request, which is why you see a trigger for that. But until I figure this out I just have the default visibility at visible.
Can someone explain this behavior and offer a fix?
Cheers,
Berryl
Design View
Runtime View
Button Style
<Style x:Key="EditCommandButtonStyle" TargetType="{x:Type Button}" >
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="a" Foreground="Navy" FontWeight="Bold" FontFamily="Wingdings 3" FontSize="24" Width="30"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="theBorder" Background="Transparent" CornerRadius="4">
<ContentPresenter x:Name="theContent" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="theContent" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="theContent" Property="Visibility" Value="Visible"/>
<Setter TargetName="theBorder" Property="Background" Value="Orange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding SelectedSatelliteVm.EditCommand}"/>
</Style>
Usage
<DataTemplate x:Key="PersonInfoEditingControlTemplate">
<Grid>
...
<Label Grid.Column="0" Grid.Row="0" ... />
<Button Style="{StaticResource EditCommandButtonStyle}" Grid.Column="1" Grid.Row="0"/>
<TextBlock .../>
<Label Grid.Column="0" Grid.Row="1" ... />
<Button Style="{StaticResource EditCommandButtonStyle}" Grid.Column="1" Grid.Row="1"/>
<TextBlock .../>
<TextBlock Grid.Column="0" Grid.Row="2" .../>
<Button Style="{StaticResource EditCommandButtonStyle}" Grid.Column="1" Grid.Row="2"/>
<Image Grid.Column="2" Grid.Row="2" ... />
</Grid>
</DataTemplate>
EDIT for Rachel
Look Ma, no triggers! Only the last button shows the arrow though...
<Style x:Key="TestCommandButtonStyle" TargetType="{x:Type Button}" >
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="a" Foreground="Navy" FontWeight="Bold" FontFamily="Wingdings 3" FontSize="24" Width="30"/>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding SelectedSatelliteVm.EditCommand}"/>
</Style>
the FIX
<Style x:Key="EditCommandButtonStyle" TargetType="{x:Type Button}" >
<Setter Property="Content" Value="a" />
<Setter Property="Foreground" Value="Navy" />
<Setter Property="FontFamily" Value="Wingdings 3" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Command" Value="{Binding SelectedSatelliteVm.EditCommand}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="theBorder" CornerRadius="4">
<ContentPresenter x:Name="theContent" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="theContent" Property="Visibility" Value="Hidden"/>
<Setter TargetName="theBorder" Property="Background" Value="Transparent"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="theContent" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="theContent" Property="Visibility" Value="Visible"/>
<Setter TargetName="theBorder" Property="Background" Value="Orange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style as originally written actually had two problems, both fixed by the style above. (by fixed I mean it works as intended, not that it is the 'right' way to do it!):
DP Precedence - this is what Rachel's answer is addressing although her answer. But the only effective fix I found is to set visibility using two triggers for IsMouseOver as shown.
Only the last item in the Grid was showing any content! - this one makes no sense to me, but moving the properties previously in the TextBlock content to Setters on the style fixes that.
Dependencey properties defined in the <Tag> have a higher precedence than items defined in triggers, so will always overwrite them.
For example, in this case the button's Visibility will always be Collapsed regardless of what the trigger is, because the property is defined in the <Button> tag
<Button Visibility="Collapsed">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Property="{Binding SomeValue}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
To make this trigger work, move the Visibility property to a Style Setter, which has a lower precedence than a Style Trigger, so triggered values will overwrite it
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Property="{Binding SomeValue}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
See MSDN's Dependency Property Precedence List for more information
Edit
As for your second problem about the Content only showing up once, I suspect it is because you have the Content bound in your style, and not the ContentTemplate.
WPF is creating a single instance of the TextBlock arrow, and a control can only have one parent, so after the style is applied, so only one Button can contain the arrow TextBlock
The fix is to use the ContentTemplate property instead of the Content property.
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="a" Foreground="Navy" FontWeight="Bold" FontFamily="Wingdings 3" FontSize="24" Width="30"/>
</DataTemplate>
</Setter.Value>
</Setter>
Honestly, I'm surprised it didn't give you an error about Specified element is already the logical child of another element. That is usually what happens in these kind of cases (see my post here for more details)
How do I do this? I want to get rid of that annoying red border that shows on each invalid datagrid cell.
You can just add this line to your DataGrid:
<DataGrid Validation.ErrorTemplate="{x:Null}" />
I had this same issue but in my case I wasn't using IDataError or INotifyDataErrorInfo. I'm using a custom ValidationRule and styles to handle my validation logic and presentation. I was already using the DataGrid RowStyle to display a custom style for the row that has errors so I thought it would be easy to do something similar with the DataGridCell.
Things to note:
You can't just define a style and set the DataGrid.CellStyle. Instead you have to use the ElementStyle and/or the EditingElementStyle.
The style TargetType has to match the DataGridColumn type that the cell is using. So for a DataGridComboBoxColumn the TargetType should be ComboBox
DataGrid Column XAML:
<DataGridComboBoxColumn Header="Column1"
ItemsSource="{Binding Path=Column1Path}"
DisplayMemberPath="Column1DisplayPath"
ElementStyle="{StaticResource DGComboColValidationStyle}"
EditingElementStyle="{StaticResource DGComboColValidationStyle}">
<DataGridComboBoxColumn.SelectedItemBinding>
<Binding Path="Column1Path" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<Validation:CustomValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridComboBoxColumn.SelectedItemBinding>
</DataGridComboBoxColumn>
Style Definitions
<Style x:Key="DGComboColValidationStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderThickness="1"
BorderBrush="{Binding ElementName=adorner1, Path=DataContext[0].ErrorContent.ValidationType, Converter={StaticResource ValidationTypeColorConverter}}"
CornerRadius="3">
</Border>
<AdornedElementPlaceholder Name="adorner1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DGTextColValidationStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderThickness="1"
BorderBrush="{Binding ElementName=adorner2, Path=DataContext[0].ErrorContent.ValidationType, Converter={StaticResource ValidationTypeColorConverter}}"
CornerRadius="3">
</Border>
<AdornedElementPlaceholder Name="adorner2"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DGRowValidationStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="ValidationErrorTemplate">
<Setter.Value>
<ControlTemplate x:Name="DGRowValidationTemplate">
<Grid>
<Ellipse Width="12" Height="12" Stroke="Black" StrokeThickness="0.5"
Fill="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=(Validation.Errors)[0].ErrorContent.ValidationType, Converter={StaticResource ValidationTypeColorConverter}}"/>
<TextBlock FontWeight="Bold" Padding="4,0,0,0"
Margin="0" VerticalAlignment="Top" Foreground="White" Text="!"
ToolTip="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=(Validation.Errors)[0].ErrorContent.ValidationMessage}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent.ValidationType, Converter={StaticResource ValidationTypeColorConverter}}"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="false">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="DGCellStyle" TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="false">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=(Validation.Errors)[0].ErrorContent.ValidationMessage}"/>
</Trigger>
</Style.Triggers>
</Style>
Reference:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/6d2d6513-7bca-4359-a12b-46da3c380b0a/wpf-4-datagrid-editingelementstyle-and-validationerrortemplate-adorner-layer?forum=wpf
Set the ValidatesOnDataErrors and ValidatesOnExpcetions to False for your binding of your cell.
In case you want your validations, then you have to override the Validation Template for your control. Please refer to my answer here -
Validation Error Style in WPF, similar to Silverlight
I hated the red border because it was put in the adorner and adorners sit on top of the window, meaning that if your element is partially/fully hidden by another element (like it is in a grid) the full adorner still shows :(
That didn't mean I didn't want some customization though, so I can still highlight my items in pink, or change their foreground and have the DataGridCell correctly clip everything off. You can do this by using a style trigger.
Hope this helps!
<DataGrid.Resources>
<Style TargetType="{x:Type TextBlock}" x:Key="TextBlockErrorStyle">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<!-- Just the adorned element means NO RED BORDER -->
<AdornedElementPlaceholder Name="controlWithError" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="Pink"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
...
<DataGridTemplateColumn Header="Description" MinWidth="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
Style="{StaticResource ResourceKey=TextBlockErrorStyle}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
You can check this as well
How to: Implement Validation with the DataGrid Control
I have a combobox that i need to edit its error template to show a red border when there is a validation error.
I am using the following style
<Style TargetType="{x:Type ComboBox}" >
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="3">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
The border never shows up when validation errors occur. Any tips what is going wrong?
The Style you posted works. You should check your binding, did you add ValidatesOnDataErrors=True and ValidatesOnExceptions=True to the binding of SelectedValue?
enter code heretry without the dock panel, that is uneuseful since it wraps jus one element. However, sicnecerely I don't wnow if it makes sense to wrap a textbox with a border, since it has already a border! You should try to change directly the colour of its border. You could try to use again the panel but then put the border around the panel ie:
Border BorderBrush="Red" BorderThickness="3"
DockPanel
AdornedElement
This makes more sense because the wrap panel has not its own border.
Use This.
<Style x:Key="textBoxStyle" TargetType="{x:Type telerik:RadMaskedTextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Control.BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
I don't like any of the responses here. Simply put, how do you change the border color for the error template for a ComboBox using Blend or not? It shouldn't be acceptable to draw another border around the existing border of the ComboBox. I've figured out how to creat a ControlTemplate in Blend but not a Validation Template.
I've come close with trying to make it appear like I've changed the actual border color, but that's not what I actually want. Suggestions? To complicate it a bit, I'd like to display a red asterisk outside of the right border of the control.
The following code is a close attempt, but it is actually drawing a border inside the ComboBox and if you look close, you can see that the border is 2 pixels wide when combined with the ComboBox border:
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder>
<Border BorderBrush="Blue" BorderThickness="1" CornerRadius="2" />
</AdornedElementPlaceholder>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
I searched around some more and came up with a solution based on another article here: WPF - How to apply style to AdornedElementPlaceholder's AdornedElement?
<!-- This works -->
<ComboBox Name="comboBox1" Style="{StaticResource NewComboBoxStyle}" Validation.ErrorTemplate="{StaticResource comboBoxValidationTemplate}" />
<SolidColorBrush x:Key="MainBorderBrush">#FF91B3FF</SolidColorBrush>
<Style x:Key="NewComboBoxStyle" TargetType="{x:Type ComboBox}" BasedOn="{StaticResource myErrorTemplate}">
<Setter Property="BorderBrush" Value="{DynamicResource MainBorderBrush}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
<!-- Sets ToolTip when Validation.HasError is True. -->
<Style TargetType="Control" x:Key="myErrorTemplate">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="comboBoxValidationTemplate">
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder/>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
</ControlTemplate>