first my validation template
<ControlTemplate x:Key="ValidationTemplate" >
<Grid>
<AdornedElementPlaceholder Name="MyAdornedElement" />
<Path x:Name="path" Margin="-2,-2,0,0" Data="M 0,10 L 10,0 L 0,0 Z" Fill="{StaticResource BrushError}" StrokeThickness="2" Stroke="White"
Visibility="{Binding ElementName=MyAdornedElement,Path=AdornedElement.Visibility}"
ToolTip="{Binding ElementName=MyAdornedElement,Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
</Grid>
</ControlTemplate>
and my textbox style
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}"/>
<Setter Property="UndoLimit" Value="0"/>
<Style.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderThickness" Value="{StaticResource IsFocusBorderThickness}"/>
<Setter Property="BorderBrush" Value="{StaticResource IsFocusBorderBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource IsDisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors).CurrentItem.ErrorContent, RelativeSource={x:Static RelativeSource.Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
and now the mysterius behavior
i have a tabcontrol with 2 tabpages, each of the pages contains textboxes. if i open my view and move from first to second tabpage a back and then push the validate button - all is fine, my validation template is shown for all textboxes on both tabpages.
BUT when i just open the view and dont navigate to tabpage 2 - then push the validate button - just the textboxes on tabpage 1 have the validation template shown. even more when i hit the button again on the validation template its not show on tabpage 2.
Any hints what i'm missing?
EDIT:
if i use snoop and walk on the snoop treeview to my textbox on tabpage 2, then the validation Template Adorner is visible as far as i click on the TextBox in the Snoop Treeview...
if i add the following to my textbox style it works.
<Trigger Property="IsVisible" Value="false">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Trigger>
<Trigger Property="IsVisible" Value="true">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}"/>
</Trigger>
Related
I want to achieve similar effect, which can be seen in Notepad++: dividing TabContol into two TabControls. Obviously both will have selected tab on their own, but still only one of them will be active.
For those who doesn't know Notepad++, this is how it looks like:
For that I'll need to introduce "Active" property on TabControl (not Focused, because when one of TabControls loses focus, its selected tab still remains active). However, I have no idea how to craft trigger on TabItem's ControlTemplate, which will allow me to distinguish selected and selected+active tab.
This is how my current TabItem template look:
<Style x:Key="BaseRootTabItem" TargetType="TabItem">
<Style.Setters>
<Setter Property="Background" Value="{StaticResource NormalTabBackgroundBrush}" />
<Setter Property="TextBlock.Foreground" Value="{StaticResource NormalTabForegroundBrush}" />
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource HoverTabBackgroundBrush}" />
<Setter Property="TextBlock.Foreground" Value="{StaticResource HoverTabForegroundBrush}" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="DocumentTabItem" TargetType="TabItem" BasedOn="{StaticResource BaseRootTabItem}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border x:Name="TabBorder" BorderThickness="1,1,1,0" BorderBrush="Transparent"
Background="{TemplateBinding Background}" TextBlock.Foreground="{TemplateBinding Foreground}">
<ContentPresenter x:Name="ContentSite" HorizontalAlignment="Center" VerticalAlignment="Center" ContentSource="Header" Margin="6,2" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource SelectedDocumentTabBackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource SelectedDocumentTabForegroundBrush}" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
I need something like:
<Trigger Property="(Owning TabControlEx's Active property)" Value="True">
<Trigger.Setters>
...
</Trigger.Setters>
</Trigger>
Or maybe there's some other solution?
Since Active property doesn't belong to TabItem, Trigger won't work. Use DataTrigger with binding to parent:
<DataTrigger Binding="{Binding Active, RelativeSource={RelativeSource AncestorType=TabControlEx}}"
Value="True">
</DataTrigger>
As the title suggests, I'm looking for a trigger property that triggers when the left mouse button is clicked over the button. Problem being my button has a rectangle as it's control template and I'd like to change the fill/stroke when the button/rectangle is clicked.
The only trigger property I can find that works is "IsMouseOver"
Anything like MouseDown or IsPressed doesn't work.
My xaml right now:
<Button x:Name="my_Button" Click="my_Button_Click" Margin="268,91,-266,-94">
<Button.Template>
<ControlTemplate>
<Rectangle HorizontalAlignment="Left" Height="20" Stroke="Black" VerticalAlignment="Top" Width="141" Margin="105,10,0,0" StrokeThickness="2">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="Blue" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="Red"/>
<Setter Property="Stroke" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</ControlTemplate>
</Button.Template>
</Button>
Where the Trigger Property "IsMouseOver" is I would like the property to be MouseDown, and then proceeding to set the fill and stroke of the rectangle to different colors.
xaml I've tried that hasn't worked:
<Trigger Property="MouseDown" Value="True">
<Setter Property="Fill" Value="Red"/>
<Setter Property="Stroke" Value="Black"/>
</Trigger>
Edit: I would like to clarify that IsMouseOver works perfectly with the trigger property, but I need it to be when the mouse is clicked on the button rather than hovered over.
See the DataTrigger below :
<Button x:Name="my_Button" Click="my_Button_Click" Margin="268,91,-266,-94">
<Button.Template>
<ControlTemplate>
<Rectangle HorizontalAlignment="Left" Height="20" Stroke="Black" VerticalAlignment="Top" Width="141" Margin="105,10,0,0" StrokeThickness="2">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="Blue" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True">
<Setter Property="Fill" Value="Red"/>
<Setter Property="Stroke" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</ControlTemplate>
</Button.Template>
</Button>
WPF Slider tick label:
I'm looking for some way to place labels along with the Slider Ticks. I dont think there is a straight way to do this. Not sure why Microsoft has not provided this basic feature.
How can I achieve this in WPF maybe using a dependency property.
The below slider code sample shows time interval, 1, 3, 6, 12, 24. I want these numbers to appear above/below the ticks. if I place a label binding to the slider element as shown in the code snippet, the values appear end of the slider all packed together as comma separated values.
Any other way to have the labels appear along the ticks. I want this in WPF way, maybe overriding a dependency property, not in the code behind.
<Slider Name="ServiceTime"
Minimum="1"
Maximum="24"
Value="{Binding TimeInterval}"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True"
Ticks ="{Binding TimeIntervalList}"
MinWidth="450"/>
<Label Content="{Binding ElementName=ServiceTime, Path=Value}"
VerticalAlignment="Center" Width="100" />
Right click on your slider -> Edit Template -> Edit a copy and customize the Thumb (creating another template for the thumb itself and adding an additional Label for example).
EDIT:
This for instance shows the current slider value in the label, beneath the slider itself.
The normal Canvas for the thumb is moved into a stack panel. The label is placed beneath the original paths of the thumb and binded to the slider.value of the "parent" template.
Although it´s showing only the actual slider value (as a double) this might give you the direction to get your own solution...
<Style x:Key="CustomThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="Focusable" Value="false"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Height" Value="22"/>
<Setter Property="Width" Value="11"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<StackPanel>
<Canvas SnapsToDevicePixels="true">
<Canvas.RenderTransform>
<TranslateTransform X="5.5" Y="11"/>
</Canvas.RenderTransform>
<Path x:Name="Background" Data="{StaticResource SliderThumbOuterBorderGeometry}" Fill="{StaticResource HorizontalSliderThumbNormalBackground}"/>
<Path x:Name="InnerBorder" Data="{StaticResource SliderThumbMiddleBorderGeometry}" Stroke="White"/>
<Path x:Name="OuterBorder" Data="{StaticResource SliderThumbOuterBorderGeometry}" Stroke="#FF929292"/>
<Label Margin="-5.5,12,0,-12" Background="Brown" HorizontalAlignment="Center"
Content="{Binding (Slider.Value),RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Slider}}}"></Label>
</Canvas>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="Background" Value="{StaticResource HorizontalSliderThumbHoverBackground}"/>
<Setter Property="Stroke" TargetName="OuterBorder" Value="{StaticResource HorizontalSliderThumbHoverBorder}"/>
</Trigger>
<Trigger Property="Foreground" Value="Blue">
<Setter Property="Fill" TargetName="Background" Value="{StaticResource HorizontalSliderThumbHoverBackground}"/>
<Setter Property="Stroke" TargetName="OuterBorder" Value="{StaticResource HorizontalSliderThumbHoverBorder}"/>
</Trigger>
<Trigger Property="IsDragging" Value="true">
<Setter Property="Fill" TargetName="Background" Value="{StaticResource HorizontalSliderThumbPressedBackground}"/>
<Setter Property="Stroke" TargetName="OuterBorder" Value="{StaticResource HorizontalSliderThumbPressedBorder}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="Background" Value="#FFF4F4F4"/>
<Setter Property="Stroke" TargetName="InnerBorder" Value="{x:Null}"/>
<Setter Property="Data" TargetName="OuterBorder" Value="{StaticResource SliderThumbDisabledGeometry}"/>
<Setter Property="Stroke" TargetName="OuterBorder" Value="#FFAEB1AF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Problem domain: In my WPF application, I change background of lot of UI controls like Button or ListItems dynamically based on data they contain. The background is changed either when the control is loaded or based on use action/data received.
Problem statement: If the background is too dark (Green/Blue) I want to set the foreground to white else black.
Constraints: I have a big application and performance is a major concern. That's why I am hesitant to use converters and am looking for some xaml/style trigger based solutions as this is just a condition based issue.
Solutions tried: To keep it simple, I am explaining what I tried for a simple wpf button:
<UserControl.Resources>
<Style x:Key="NoChromeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding Background}"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="Chrome"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true"
HorizontalAlignment="Stretch">
<TextBlock
Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
Background="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource MyTextBlockStyle}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Black"/>
</Trigger>
<Trigger Property="Background" Value="White">
<Setter Property="Foreground" Value="Aqua"/>
</Trigger>
<Trigger Property="Background" Value="Transparent">
<Setter Property="Foreground" Value="BlueViolet"/>
</Trigger>
<Trigger Property="Background" Value="Green">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="Background" Value="Yellow">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
<Trigger Property="Background" Value="Red">
<Setter Property="Foreground" Value="Yellow"/>
</Trigger>
<Trigger Property="Background" Value="Black">
<Setter Property="Foreground" Value="DarkSeaGreen"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style x:key="MyTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontStyle" Value="Italic"/>
<Style.Triggers>
<Trigger Property="Background" Value="White">
<Setter Property="Foreground" Value="Aqua"/>
</Trigger>
<Trigger Property="Background" Value="Transparent">
<Setter Property="Foreground" Value="BlueViolet"/>
</Trigger>
<Trigger Property="Background" Value="Green">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="Background" Value="Yellow">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
<Trigger Property="Background" Value="Red">
<Setter Property="Foreground" Value="Yellow"/>
</Trigger>
<Trigger Property="Background" Value="Black">
<Setter Property="Foreground" Value="DarkSeaGreen"/>
</Trigger>
</Style>
</UserControl.Resources>
When button is created in the XAML:
<Button Content="{Binding Name}" Style="{StaticResource NoChromeButton}"/>
Also, I would like to point out a couple of things in the above style:
If I would have used ContentPresenter instead of TextBlock inside the Grid named Chrome, background property was not set on the ContentPresenter and when I snooped (http://snoopwpf.codeplex.com/) the UI, I found that the ContentPresenter has TextBlock whose Background was always set to Default and hence no styletriggers were applied to the TextBlock. Also, this TextBlock's background valuesource is Default.
On the other hand, when I use TextBlock directly inside the Grid named Chrome, I can set its background explicitly to Grid's Background which is set to Button's Background. Snooping reveals that now TextBlock's Background ValueSource is ParentTemplate.
Button picks up MyTextBlockStyle while displaying its content.
Style triggers for Button or TextBlock were never triggered unless I did mouse over the button which changes the button's background to Black and propagates this value down to TextBlock background changing the TextBlock's foreground color to DarkSeaGreen.
Also, changing the button's background in snoop utility while application is running, triggers the Style Triggers.
Questions:
Why none of the Style triggers work for Background property whereas they work for IsMouseOver property?
What I am doing wrong?
Any solution for this?
I found the solution to my problem.
TextBlock does not derive from Control. Any text shown on UI by any control internally uses TextBlock to represent the textual content. If TextBlock style is set using the following in ResourceDictionary:
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontStyle" Value="Normal" />
</Style>
Any control that represents text will have this style (since no key is assigned to this style which implies that all TextBlock will get it by default) unless the control's template override the TextBlock's default style which can be done as follows:
<Button Grid.Column="1" Style="{StaticResource NoChromeButton}">
<TextBlock Style="{x:Null}" Text="abc" FontFamily="Segoe UI Symbol"/>
</Button>
This simple setting has resolved most of the issues we have with dynamic foreground color changing.
I just want to give an initial style to my radiobutton group, before one of the radiobuttons is checked or unchecked:
<Style x:Key="RadioButtonStyle" TargetType="{x:Type RadioButton}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter Property="Foreground" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In this example, all my radiobuttons are unfortunately green, whereas I would like them to be red until the user clicks on one of them (at this point, the clicked button turns blue, and the others turn green).
How can i do that?
You just have to set the isChecked property for each RadioButton. Like this
<RadioButton GroupName="players" Grid.Column="2" Grid.Row="1" Name="hvP1" IsChecked="True"/>
<RadioButton GroupName="players" Grid.Column="2" Grid.Row="2" Name="hvP2" IsChecked="False"/>
...