My WPF application has a UserControl in it called AlarmItem. Here's the XAML:
<UserControl x:Class="CarSystem.CustomControls.AlarmItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cs="clr-namespace:CarSystem.CustomControls"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100"
DataContext="{Binding Path=Alarm, RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<Style TargetType="{x:Type cs:AlarmItem}">
<Setter Property="IsFlashing" Value="False" />
<Setter Property="FlashColor" Value="{DynamicResource NotPendingAlarmBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExpired}" Value="True">
<Setter Property="FlashColor" Value="{DynamicResource ExpiredAlarmBorder}" />
<Setter Property="IsFlashing" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsPending}" Value="True">
<Setter Property="IsFlashing" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsWhiteListAlarm}" Value="True">
<Setter Property="FlashColor" Value="{DynamicResource WhiteListAlarmBorder}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="AlarmItemBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExpired}" Value="True">
<Setter Property="BorderThickness" Value="4" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsPending}" Value="True">
<Setter Property="BorderThickness" Value="4" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Border HorizontalAlignment="Center"
Margin="5"
Height="100"
Name="Border"
Style="{StaticResource AlarmItemBorderStyle}"
VerticalAlignment="Center"
Width="100">
<Border.Resources>
<Storyboard x:Key="FlashingStoryboard" AutoReverse="False" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:01"
Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<DiscreteColorKeyFrame KeyTime="00:00:00" Value="Black" />
<DiscreteColorKeyFrame KeyTime="00:00:00.5" Value="{Binding Path=FlashColor, RelativeSource={RelativeSource AncestorType={x:Type cs:AlarmItem}}}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FlashStates">
<VisualState x:Name="FlashingOn"
Storyboard="{StaticResource ResourceKey=FlashingStoryboard}" />
<VisualState x:Name="FlashingOff" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
Name="AlarmImage"
Source="{Binding Path=Image, RelativeSource={RelativeSource AncestorType={x:Type cs:AlarmItem}}}"
Stretch="Fill" />
<cs:ResponseTimer Expired="Timer_Expired"
Grid.Row="1"
HideIfExpired="True"
IsTabStop="False"
MinHeight="10"
x:Name="TheTimer"
TimeoutPeriod="00:02:30"
VerticalAlignment="Bottom" />
</Grid>
</Border>
</UserControl>
The code behind for this control has the following code in it that runs when the IsFlashing property changes:
private void OnIsFlashingChanged( object sender, bool flashNow ) {
if ( flashNow ) {
if ( !VisualStateManager.GoToElementState( Border, "FlashingOn", true ) ) {
Log.Debug( "AlarmItem.xaml.cs: Visual State Manager transition failed." );
}
} else {
if ( !VisualStateManager.GoToElementState( Border, "FlashingOff", true ) ) {
Log.Debug( "AlarmItem.xaml.cs: Visual State Manager transition failed." );
}
}
}
private static void OnIsFlashingInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
AlarmItem alarmItem = (AlarmItem) d;
alarmItem.OnIsFlashingChanged( d, (bool) e.NewValue );
}
The line that sets the visual state for the Border to FlashingOn throws the following InvalidOperationException when it executes:
System.InvalidOperationException: 'BorderBrush' property does not point to a DependencyObject in path '(0).(1)'.
What have I done wrong? I recently added the Style and the triggers that set the IsFlashing property; in the past, I did it in the code-behind and this error did not occur then.
After much research, trial & error, and heartache, the reason I'm getting the error is because the Border class's BorderBrush property is a Brush, not a SolidColorBrush. Brush is an abstract type that SolidColorBrush and all the various gradient brush classes descend from.
Related
I am currently working on Visual Studio extension displaying a hierarchy of items via a ListView. The WPF ListView contains items styled to appear as if they were part of a tree. They are also styled to reduce their padding, border thickness and margin to 0.
These items can be collapsed or expanded to hide their assigned children. The collapse and expand behaviours basically consist of adding/removing the items off the ListView and changing the state of the Expander element.The scrollbar appears as needed if the amount of items is too big to be contained in the window.
Wanted selection behaviour
I do get the wanted behaviour most of the time. However, from times to times, all the items change their view to appear like this:
Random behaviour occurring randomly
The problem repeats consistently when the scrollbar appears or disappears but also occurs without any change in the state of the scrollbar. I tried on VS2015Enterprise and VS2017 Enterprise/Professional and the problem only occurs on Windows 8.1/10. It works fine on Windows 7.
Would you have any ideas as to what might cause this added padding/margin/border?
(I already watched solution of different base style and adding the default style for the used elements did not help).
EDIT: Here is the style used for the items / treeview:
<!-- Tree style -->
<Style x:Key="{x:Type custom:IssuesTreeViewInternal}" TargetType="{x:Type custom:IssuesTreeViewInternal}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type custom:IssuesTreeViewInternal}">
<ScrollViewer x:Name="_tv_scrollviewer_" Focusable="False" CanContentScroll="True">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ExpandCollapseToggleStyle style -->
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Width="15" Height="13" Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Collapsed">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Expanded">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked" />
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="Collapsed" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,2" Data="M 4.5 0.5 L 8.5 4.5 L 4.5 8.5 Z" Stroke="{DynamicResource {x:Static vsUI:EnvironmentColors.StartPageTextDateBrushKey}}" />
<Path x:Name="Expanded" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,0,1,2" Data="M 9.5 0.5 L 9.5 5.5 L 4.5 5.5 Z" Visibility="Hidden" Stroke="{DynamicResource {x:Static vsUI:EnvironmentColors.StartPageTextDateBrushKey}}" Fill="{DynamicResource {x:Static vsUI:EnvironmentColors.StartPageTextDateBrushKey}}" />
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Stroke" TargetName="Collapsed" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.ScrollBarArrowGlyphPressedBrushKey}}" />
<Setter Property="Stroke" TargetName="Expanded" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.ScrollBarArrowGlyphPressedBrushKey}}" />
<Setter Property="Fill" TargetName="Expanded" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.ScrollBarArrowGlyphPressedBrushKey}}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Fill" TargetName="Collapsed" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.StartPageTextDateBrushKey}}" />
<Setter Property="Fill" TargetName="Expanded" Value="Transparent" />
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TreeViewItemFocusVisual style -->
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="0" Stroke="{DynamicResource {x:Static vsUI:EnvironmentColors.SystemHighlightBrushKey}}" StrokeThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- IssuesTreeViewItem style -->
<Style x:Key="{x:Type custom:IssuesTreeViewItem}" TargetType="{x:Type custom:IssuesTreeViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="1" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.ToolboxContentTextBrushKey}}" />
<Setter Property="Focusable" Value="False" /> <!-- No need to focus on it. We can have focus on the parent ListViewItem instead. -->
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0" /> <!-- 0 border thickness makes sure clicking on the IssuesTreeViewItem is registered everywhere, even in the border area. -->
<Setter Property="ToolTip" Value="{Binding Path=Content.Tooltip}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type custom:IssuesTreeViewItem}">
<ControlTemplate.Resources>
<converters:LeftMarginMultiplierConverter x:Key="LeftMarginMultiplier" DefaultLength="19" />
</ControlTemplate.Resources>
<Border Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<Grid Margin="{Binding Converter={StaticResource LeftMarginMultiplier}, RelativeSource={RelativeSource TemplatedParent}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
ClickMode="Press"
VerticalAlignment="Top"
Margin="0, 2, 0, 0"
Command="{Binding Path=ToggleButtonCommand, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
<ContentPresenter x:Name="PART_Header"
Grid.Column="1"
Content="{Binding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasChildren" Value="False">
<Setter TargetName="Expander"
Property="Visibility"
Value="Hidden" />
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=IsKeyboardFocusWithin}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter TargetName="Border"
Property="Background"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PassiveHighlightBrush}" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PassiveHighlightBrush}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected}"
Value="False" />
<Condition Binding="{Binding Path=Content.IsSuppressed, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource TextViewItemForegroundColorConverter}}" />
</MultiDataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.SystemHighlightBrushKey}}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.SystemHighlightBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsUI:EnvironmentColors.SystemHighlightTextBrushKey}}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
We use the TreeView Style this way in the XAML bit of the VS Extension:
<local:IssuesTreeViewInternal
x:Name="issuesTreeInternal"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
SelectionMode="Extended"
DataContext="{Binding IssuesTreeViewInternalViewModel}"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=IssuesTreeItems}"
SelectedItem="{Binding SelectedItem}"
behaviours:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedNames}"
x:FieldModifier="internal">
<local:IssuesTreeViewInternal.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}" />
</Style>
</local:IssuesTreeViewInternal.ItemContainerStyle>
</local:IssuesTreeViewInternal>
The IssueTreeViewInternal is a class inheriting from ListView.
I had the exact same issue. Some spacing was added in Windows 10
this SO answer fixed it :
https://stackoverflow.com/a/15618991/4117068
This is what I did
<ListView ...>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
I'm trying to replicate the checkboxes seen in this image:
css-custom-checkboxes
I've been using the xaml from this example as a starting point:
web.archive.org - WPF CheckBox style (inspired by android)
My requirements:
Solid background.
No border.
Checkmark area and checkmark resizes with control.
Checkmark is centered in the checkbox area.
My Code:
<Style TargetType="{x:Type local:DMStyle2CheckBox}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Height" Value="32" />
<Setter Property="Width" Value="138" />
<Setter Property="FocusVisualStyle" Value="{DynamicResource MyFocusVisualStyte}" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<BulletDecorator>
<BulletDecorator.Bullet>
<Grid Background="{x:Null}" Height="{TemplateBinding Height}" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height, UpdateSourceTrigger=PropertyChanged}"
MinHeight="30" MinWidth="30" ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="4*" />
<RowDefinition Height="6*" />
<RowDefinition Height="1*" />
<RowDefinition Height="4*" />
</Grid.RowDefinitions>
<!-- Checkmark Box -->
<Border Name="InnerBorder" Grid.Column="1" Grid.ColumnSpan="5" Grid.Row="2" Grid.RowSpan="5" BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}"/>
<!-- Checkmark -->
<Path x:Name="CheckMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,
1.52588e-005L 9.97498,1.22334 Z" Fill="{Binding CheckMarkColor, RelativeSource={RelativeSource Mode=TemplatedParent}}" Opacity="0"
Stretch="Uniform" Grid.Column="2" Grid.ColumnSpan="4" Grid.Row="3" Grid.RowSpan="3" />
<Path Name="InderminateMark" Grid.Column="3" Grid.Row="4" Data="M0,4 L1,5 5,1 4,0" Opacity="0"
Stretch="Fill" StrokeThickness="0" Fill="#808080" />
</Grid>
</BulletDecorator.Bullet>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="CheckMark" Duration="0:0:0.2" To="1" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked" >
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="CheckMark" Duration="0:0:0.2" To="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InderminateMark" Duration="0:0:0.2" To="1" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Margin="4,0,4,0" VerticalAlignment="Center" HorizontalAlignment="Left" RecognizesAccessKey="True" />
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="InnerBorder" Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CheckMark" Property="Fill" Value="{StaticResource Color.Black}" />
<Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource Color.Black}" />
<Setter TargetName="InderminateMark" Property="Fill" Value="{StaticResource Color.Black}" />
<Setter TargetName="InnerBorder" Property="BorderBrush" Value="{StaticResource Color.Black}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
For the most part this code does what I want. The checkmark is the biggest issues for me. It's not centered or resizable and it overflows the checkmark area.
I've tried playing with the grid columns and rows that hold the checkmark but its becoming increasingly frustrating. Ideally I would like to get rid of the grid columns and rows but haven't had much luck with that either.
I've looked at every SO question about wpf checkbox styles I could find and have searched for any other online resources but most of them don't come close to what I want. I even attempted to modify the default template with Expression Blend but that didn't work out either.
I ended up figuring it out based on this template I found: Circle-checkbox-style-in-WPF
Style Template
<Style TargetType="{x:Type local:FlatCheckBox}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Height" Value="32" />
<Setter Property="Width" Value="138" />
<Setter Property="FocusVisualStyle" Value="{DynamicResource MyFocusVisualStyte}" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}"
BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}"
BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
CornerRadius="0" Width="20" Height="20" VerticalAlignment="Center">
<Grid>
<Path x:Name="CheckMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,
6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z" Fill="{Binding CheckMarkColor, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Margin="3" Opacity="0" Stretch="Fill" />
</Grid>
</Border>
<ContentPresenter Grid.Column="1" x:Name="content" Margin="5,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="CheckMark" Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Class for custom dependency properties
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Control_Styles.Styles
{
public partial class FlatCheckBox : CheckBox
{
public static readonly DependencyProperty CheckMarkColorProperty =
DependencyProperty.Register("CheckMarkColor", typeof(Brush), typeof(FlatCheckBox));
public Brush CheckMarkColor
{
get { return (Brush)GetValue(CheckMarkColorProperty); }
set { SetValue(CheckMarkColorProperty, value); }
}
}
}
you can set a style
<Style TargetType="CheckBox" x:Key="Flat_CheckBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="MinWidth" Value="60"/>
<Setter Property="UIElement.SnapsToDevicePixels" Value="True"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="TextElement.Foreground" Value="Black"/>
<Setter Property="FrameworkElement.FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="White" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightSkyBlue" />
</Trigger>
</Style.Triggers>
</Style>
I have a Style for my TreeViewItems which at the moment is applied to all because he has no Key.
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid x:Name="gChildren">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle x:Name="HorLin" Grid.Row="0" Height="1" Stroke="Black" SnapsToDevicePixels="True" VerticalAlignment="Bottom" />
<Rectangle x:Name="VerLinUp" Grid.Row="1" Width="1" Height="20" Stroke="Black" SnapsToDevicePixels="True" />
<Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="3" Padding="6" Grid.Row="2" Margin="2,0">
<ContentPresenter Name="PART_Header" ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<Rectangle x:Name="VerLinDown" Grid.Row="3" Width="1" Height="10" Stroke="Black" SnapsToDevicePixels="True" />
<ItemsPresenter x:Name="itemPresenter" Grid.Row="4" HorizontalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Items.Count, Converter={StaticResource IsGreaterThanConv}, ConverterParameter=0}" Value="false">
<Setter TargetName="VerLinDown" Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource HasParentMoreChildren}}" Value="false">
<Setter TargetName="HorLin" Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsParentTreeViewItem}}" Value="false">
<Setter TargetName="VerLinUp" Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsParentTreeViewItem}}" Value="true">
<Setter TargetName="VerLinUp" Property="Height" Value="10" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsFirstOrLastItem}}" Value="2">
<Setter TargetName="HorLin" Property="Width" Value="{Binding ElementName=gChildren, Path=ActualWidth, Converter={StaticResource ArithmeticConverter}, ConverterParameter=/2}" />
<Setter TargetName="HorLin" Property="HorizontalAlignment" Value="Right" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsFirstOrLastItem}}" Value="1">
<Setter TargetName="HorLin" Property="Width" Value="{Binding ElementName=gChildren, Path=ActualWidth, Converter={StaticResource ArithmeticConverter}, ConverterParameter=/2}" />
<Setter TargetName="HorLin" Property="HorizontalAlignment" Value="Left" />
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Bd" Property="Panel.Background" Value="{StaticResource SelectedItemAreaBrush}" />
<Setter TargetName="Bd" Property="Border.BorderBrush" Value="{StaticResource SelectedItemBorderBrush}" />
<Setter TargetName="Bd" Property="TextElement.Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel x:Name="spChildren" HorizontalAlignment="Center" IsItemsHost="True" Margin="4,0,4,6" Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
When I leave it like that everything works like I want it too. But if I give my Style a Name and set the ItemContainerStyle on my TreeView it looks different
<Style x:Key="GoodTVI" TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid x:Name="gChildren">
Adding my TreeViewItem-Style to my TreeView
TVVerlauf.SetBinding(TreeView.ItemContainerStyleProperty, "GoodTVI");
The Rest of the Style stays exactly the same. This is the only difference but the Result looks completley different.
I give my Style a Name
Actually, you don't.
<Style x:Key="GoodTVI" TargetType="TreeViewItem">
...
That's not a name. That's a key. The Style is a resource. Resources have keys, not names. The two are different. For one thing, resource keys are objects rather than strings. And they're looked up differently, in different places.
Here, you're binding the treeview's ItemContainerStyle property to the GoodTVI property of its DataContext (which should be your viewmodel).
TVVerlauf.SetBinding(TreeView.ItemContainerStyleProperty, "GoodTVI");
Since GoodTVI is a resource key, not the name of a property on the viewmodel, you're naturally not getting good results.
Try this on your TreeView in the XAML:
<TreeView
ItemContainerStyle="{StaticResource GoodTVI}"
...other properties...
>
If you need to do this in C#, try this:
TVVerlauf.ItemContainerStyle = (DataTemplate)TVVerlauf.FindResource("GoodTVI");
Try this:
TVVerlauf.SetBinding(TreeView.StyleProperty, "GoodTVI");
I have a UserControl, which contains a Grid which contains a Border:
<UserControl Focusable="True" Name="uctrlScenePanel">
<Grid MouseDown="Grid_MouseDown">
<Border BorderThickness="0" BorderBrush="Black">
</Border>
</Grid>
I now wanted to change the Border-Thickness once the usercontrol "uctrlScenePanel" got Focus, but i can't get it to work and i dont really find useful Eventtrigger-Tutorials which a can understand, because i am very new to WPF.
edit:
d.moncada's answer was very helpful! my xaml now looks like this:
<Border BorderBrush="Black">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=uctrlScenePanel}" Value="true">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=uctrlScenePanel}" Value="false">
<Setter Property="BorderThickness" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
I have still problems,because the focusing of a user control behaves weird, but thats another story...this one is solved for me, thanks for the lesson ;)
You can bind to the UserControl IsFocused property using a DataTrigger.
<UserControl Focusable="True" x:Name="uctrlScenePanel">
<Grid MouseDown="Grid_MouseDown">
<Border BorderBrush="Black">
<Broder.Style>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=uctrlScenePanel}" Value="True">
<Setter Property="BorderThickness" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</UserControl>
I have the following Button and Style in WPF and I need to generalize the Binding in the DataTrigger section because I have near 10 similar buttons in the same Window and each button should be binded to a different property (SelectedPositions, SelectedAgencies, ....). Is it possible to implement?
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
Style="{StaticResource NewButtonStyle}" />
<Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White" />
<Setter Property="Height" Value="22" />
<Setter Property="Width" Value="Auto" />
<Setter Property="FontFamily" Value="OpenSans" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Margin" Value="10,2,10,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="3">
<Grid x:Name="gridButton" Background="#54728e">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="img"
Grid.Column="0"
Width="24"
Height="24"
Source="Img/tick-white.png"
Visibility="Visible" />
<Rectangle x:Name="rect"
Grid.Column="1"
Fill="#54728e"
RadiusX="3"
RadiusY="3" />
<ContentPresenter Grid.Column="1"
Margin="5,0,5,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding SelectedPositions}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="rect" Property="Fill" Value="#8bbcdf" />
<Setter TargetName="img" Property="Visibility" Value="Collapsed" />
<Setter TargetName="gridButton" Property="Background" Value="#8bbcdf" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
could you provide me an example of what you explained?
Sure,
1 - Using Tag
In your Style have your DataTrigger as:
<DataTrigger Binding="{Binding Path=Tag,
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
as for usage:
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
Tag="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
2 - Using Attached Property:
"local:" refers to the xaml namespace alias of your application or if you use different namespaces, the namespace where MyCustomPropertyCollection is declared.
code-behind:
public class MyCustomPropertyCollection {
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.RegisterAttached(
"SomeString",
typeof(string),
typeof(MyCustomPropertyCollection),
new FrameworkPropertyMetadata(string.Empty));
public static void SetSomeString(UIElement element, string value) {
element.SetValue(SomeStringProperty, value);
}
public static string GetSomeString(UIElement element) {
return (string)element.GetValue(SomeStringProperty);
}
}
Style.DataTrigger
<DataTrigger Binding="{Binding Path=(local:MyCustomPropertyCollection.SomeString),
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
usage:
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
local:MyCustomPropertyCollection.SomeString="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
3 - Normal Dependency Property
custom Button class:
public class MyButton : Button {
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.Register(
"SomeString",
typeof(string),
typeof(MyButton),
new FrameworkPropertyMetadata(string.Empty));
public string SomeString {
get {
return (string)GetValue(SomeStringProperty);
}
set {
SetValue(SomeStringProperty, value);
}
}
}
Style in xaml not only needs DataTrigger updated but Style definition too.
so switch
<Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">
to
<Style x:Key="NewButtonStyle" TargetType="{x:Type local:MyButton}">
Style.DataTrigger
<DataTrigger Binding="{Binding Path=SomeString,
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
usage:
<local:MyButton x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
SomeString="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
Tag approach is frowned upon. "Attached Property" is easier to implement but isn't as clear of a indicator of dependencies as a custom class with a normal DP and AP also gets way overused. Take your pick for what you'd prefer.