RelativeSource binding works from Style but not ControlTemplate - c#

An entry in my Application.Resources ResourceDictionary is a control template that, slimmed down, looks similar to the following:
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border>
<Border.BorderBrush>
<SolidColorBrush Color="{Binding Path=BorderColor, RelativeSource={RelativeSource AncestorType=UserControl}" />
</Border.BorderBrush>
</Border>
</ControlTemplate>
Each UserControl has its own property BorderColor which this pulls from. In this example, the binding fails to find the property.
Cannot find source for binding with reference 'RelativeSource
FindAncestor, AncestorType='System.Windows.Controls.UserControl',
AncestorLevel='1''.
However, it works in another entry in the dictionary:
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="BorderBrush" Value="{Binding Path=BorderColor, RelativeSource={RelativeSource AncestorType=UserControl}"/>
</Style>
How can I fix the binding in the first example? Preferably I would like to not need additional properties on the instance of each control in the user control.

Two suggestions:
If the ControlTemplate is part of a Style you could set the BorderBrush property of the ToggleButton to the SolidColorBrush with the binding and use a TemplateBinding in the template:
<Style x:Key="myStyle" TargetType="ToggleButton">
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush Color="{Binding Path=BorderColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border BorderBrush="{TemplateBinding Background}" BorderThickness="10">
<TextBlock>....</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you want to define a standalone ControlTemplate for some reason a workaround would be to bind to a Brush property instead of a Color property:
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border BorderBrush="{Binding Path=BorderBrushProperty, RelativeSource={RelativeSource AncestorType=UserControl}}" BorderThickness="10">
<TextBlock>....</TextBlock>
</Border>
</ControlTemplate>

It works if you use Background instead of BorderColor. Is BorderColor your own property?
<Window.Resources>
<ControlTemplate x:Key="template" TargetType="{x:Type ToggleButton}">
<Border>
<Border.BorderBrush>
<SolidColorBrush Color="{Binding Path=Background,RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Border.BorderBrush>
</Border>
</ControlTemplate>
</Window.Resources>
<UserControl Background="Aqua">
<ToggleButton Template="{StaticResource template}"></ToggleButton>
</UserControl>

Related

Custom control and additional style in generic.xaml for it

I have created custom control and a default style for it.
My XAML is simple:
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="10" BorderThickness="1" Background="Transparent" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I connect to this style using DefaultStyleKey:
DefaultStyleKey = typeof(MyControl);
And it works. But now I want to create other style for my control. It's because my control can have some modes defined as enums, like:
public enum ControlMode
{
Mode1,
Mode2
}
Now, when my control is in Mode1 I want it to have its default style. But when it's in Mode2 I want it to have another style, like:
<Style TargetType="{x:Type local:MyControl}" x:Key"styleForMode2>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="1" Background="White" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I achieve that? DefaultStyleKey works only for type name, so the only thing I came up with is to create another class for my control: MyControlWithMode2. But I'm sure there is more proper way. Right?
(this is library not an application, so I cannot use application's resources)
Assuming your control has a Mode property, the default Style could declare Triggers to set different ControlTemplates for different modes:
<Style TargetType="local:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="10" BorderThickness="1"
Background="Transparent" BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Mode" Value="Mode2">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="1" Background="White"
BorderBrush="Black"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

Make use of WindowStyle from resource dictionary and add Buttons to a ControlPresenter in an application

I make use of a resource dictionary with all my styles, icons and more.
Now I'd like to add my own titlebar implementing the title of the solution, my image and a ContentPresenter.
When using the WindowStyle I'd like to add application specific items inside this ContentPresenter, but I don't know how to proceed.
This is my WindowStyle. Inside the Grid you'll find the ContentPresenter I'd like to fill.
<Style TargetType="{x:Type Window}" x:Key="tkDarkWindowStyle">
<Setter Property="AllowsTransparency" Value="True"></Setter>
<Setter Property="Foreground" Value="{StaticResource tkBrandBlueBrush}"></Setter>
<Setter Property="Background" Value="{StaticResource exQuiteDarkBrush}"></Setter>
<Setter Property="WindowStyle" Value="None"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="BorderBrush" Value="{StaticResource exQuiteDarkBrush}"></Setter>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="80" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<DockPanel LastChildFill="True">
<Border Background="{TemplateBinding Background}" DockPanel.Dock="Top"
Height="80" x:Name="titlebar">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<Path DockPanel.Dock="Left" Margin="10" Stretch="Uniform" Fill="{TemplateBinding Foreground}" Data="{Binding Source={StaticResource tkPrimaryLogo}}" VerticalAlignment="Center">
</Path>
<Label Content="{TemplateBinding Title}" Foreground="{TemplateBinding Foreground}" Margin="10" DockPanel.Dock="Left" FontSize="26" VerticalAlignment="Center"/>
</DockPanel>
<ContentPresenter Grid.Column="1"/>
</Grid>
</Border>
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" Padding="4">
<ContentPresenter/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I used the style like this and tried to edit the template and add for example some buttons to the titlebar.
<Window.Resources>
<Style TargetType="{x:Type Window}" x:Key="newWindow" BasedOn="{StaticResource tkDarkWindowStyle}">
<!--here add application specific items? -->
</Style>
</Window.Resources>
How can I use the WindowStyle in my application and add specific items to the space i left at the titlebar?
Since the Window only has a single Content property, it makes no sense to include more than one <ContentPresenter /> element in the ControlTemplate.
You may want to create a custom class that inherits from Window and adds a dependency property called "TitleBarContent" or something. You can then add a ContentControl to the template that binds to this property:
<ContentControl Content="{TemplateBinding TitleBarContent}" />
You can set the value of the dependency property in a style setter as usual:
<Style TargetType="{x:Type local:YourWindowClass}" x:Key="newWindow" BasedOn="{StaticResource tkDarkWindowStyle}">
<Setter Property="TitleBarContent">
<Setter.Value>
<TextBlock>title...</TextBlock>
</Setter.Value>
</Setter>
</Style>

ControlTemplate DataTrigger is not fired in ItemsControl ControlTemplate

I have a NavigationMenuControl with an ObservableCollection<HtNavigationMenuQuickLinkItem>. Everything is working fine, but the Style on my HtMenuIcon Control is not triggered. Where the Visibility is changed correctly. Can someone please give me a hint where I have a mistake? QuickLinkSymbol is a DependencyProperty of an Enum.
I also want to put the Visibility Behavior into the DataTrigger section.
Navigation Menu
<Style TargetType="Navigation:HtNavigationMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Navigation:HtNavigationMenu">
<Grid>
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=QuickLinkItems}"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
QuickLinkItem
<Style TargetType="Navigation:HtNavigationMenuQuickLinkItem">
<Style.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Navigation:HtNavigationMenuQuickLinkItem">
<Controls:MyButton Width="40" Height="40" Margin="10,10,10,0">
<Viewbox Margin="3">
<Controls:HtMenuIcon x:Name="icon" Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsQuicklink, Converter={StaticResource BoolToVis}}"/>
</Viewbox>
</Controls:MyButton >
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding QuickLinkSymbol, RelativeSource={RelativeSource TemplatedParent}}" Value="Home">
<Setter TargetName="icon" Property="Style" Value="{StaticResource Home}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to reference Home as {x:Static EnumNAmeSpace:EnumType.Home}.
Oh, and if QuickLinkSymbol is a DepProp of HtNavigationMenuQuickLinkItem,
just use Trigger instead of DataTrigger.

WPF ListView remove mouseover higligth

I have a WPF Listview containing another UserControl. It works fine, but I cannot remove the mouse blu highlight and selected.
Here the code:
<UserControl.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border
BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}">
<GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type vm:ElementViewModel}" x:Key="ElementTemplate">
<vw:ElementView />
</DataTemplate>
</UserControl.Resources>
<GroupBox Header="{x:Static Translate:Translate.CreateLoop}">
<ListView ItemsSource="{Binding Path=ElementList, UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{StaticResource ElementTemplate}"
Background="{StaticResource EnvLayout}"
BorderBrush="Transparent">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Resources>
<!--Foreground for Selected ListViewItem-->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
<!--Background for Selected ListViewItem-->
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="ItemBackgroundHover" Color="Transparent"/>
</Style.Resources>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</GroupBox>
the datatemplate is used to bind the view with the corresponding viewmodel.
Here the mouse over style I'd like to remove
Try this
Add a style
<UserControl.Resources>
<Style x:Key="MyStyle" TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
Remove your style and instead
ItemContainerStyle="{StaticResource MyStyle}"

Changing WPF Listbox SelectedItem text color and highlight/background Color using C#

I am trying to change the highlighted(selected) color and the highlighted text color of a wpf listbox at runtime. I have tried creating a style and applying it as follows:
Style s = new Style(typeof(ListBox));
s.Resources.Add(SystemColors.HighlightBrushKey, Setting.ListSelectedColor);
s.Resources.Add(SystemColors.HighlightTextBrushKey, Setting.ListSelectedTextColor);
lstGames.Style = s;
But this seems to do nothing. Is there any way to achieve this?
EDIT:
Per suggestions, I tried using DynamicResources to achieve this, but so far this has not been successful either. My code for this:
DYNAMICRESOURCES
<UserControl.Resources>
<Color x:Key="ListTextSelectedColor"/>
<Color x:Key="ListSelectedColor"/>
</UserControl.Resources>
LISTBOX
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden"
Name="lstGames" Margin="20" Grid.Row="2" Grid.Column="2"
SelectionChanged="lstGames_SelectionChanged" Grid.RowSpan="2" Grid.ColumnSpan="2"
Background="{x:Null}" BorderBrush="{x:Null}" SelectionMode="Single"
FontSize="18" FontFamily="OCR A Extended">
<Style TargetType="ListBox">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="{DynamicResource ListSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{DynamicResource ListSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="{DynamicResource ListTextSelectedColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="{DynamicResource ListTextSelectedColor}"/>
</Style.Resources>
</Style>
</ListBox>
APPLYING RESOURCES IN C#
this.Resources["ListSelectedColor"] = SETING.ListSelectedColor.Color;
this.Resources["ListTextSelectedColor"] = SETTING.ListSelectedTextColor.Color;
Solution:
<Window x:Class="ListBoxStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ListBoxStyle"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemContainerStyle="{DynamicResource _ListBoxItemStyle}"
Width="200" Height="250"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBoxItem>Hello</ListBoxItem>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</Grid>
</Window>
Thanks to Vinkal and failedprogramming, I got everything working beautifully. I created the following Resources:
<UserControl.Resources>
<SolidColorBrush x:Key="ListTextSelectedColor" x:Shared="False"/>
<SolidColorBrush x:Key="ListSelectedColor" x:Shared="False"/>
<Style x:Key="_ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="{DynamicResource ResourceKey=ListSelectedColor}"/>
<Setter Property="Foreground" Value="{DynamicResource ResourceKey=ListTextSelectedColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And then applied the style to my listbox with:
ItemContainerStyle="{DynamicResource ResourceKey=_ListBoxItemStyle}"
And finally, change the solidcolorbrush resources (therefore changing the setter values) in my C# code by doing the following:
this.Resources["ListSelectedColor"] = EmulatorPage.ListSelectedColor;
this.Resources["ListTextSelectedColor"] = EmulatorPage.ListSelectedTextColor;
Thank you to both of you!
To all the neigh-sayers out there... do not lose hope! it can be done!
I started off with VSS right-click on the listbox and used every "Edit Template" and "Edit Additional Templates" for each thing available until I found the how these things work.
You start off quite simply with a list box, bound to MVVM as normal.
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}">
</ListBox>
In UserControl or Window Resources set up a few things....
ListBoxStyle - This styles the main container of the listbox, you can set the borders, margins, padding etc of the main box here. For my example I'm just getting rid of everything to de-style it.
<UserControl.Resources>
<Style x:Key="ListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</UserControl.Resources>
ItemContainerStyle - This is the bit that people say can't be re-styled - it contains the "windows-selector-blue" bar when an item is selected, but fear not this too can be re-styled (merge this UserControl.Resources section in combination with the above one).
This section is> changing the template of the ItemContainer from whatever it is to a Border, setting a top margin of 3 to pad things out and setting up a style. All we're doing with this style is adding 3px transparent border to the left and right of the item. Then in the Triggers>IsSelected (target of myBorder), changing the border Brush to Red.
<UserControl.Resources>
<Style x:Key="ItemContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="myBorder"
Padding="0" Margin="0 3 0 0"
SnapsToDevicePixels="true"
Style="{DynamicResource borderContent}">
<ContentPresenter />
</Border>
<ControlTemplate.Resources>
<Style x:Key="borderContent" TargetType="Border">
<Setter Property="BorderThickness" Value="3 0 3 0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="myBorder" Property="BorderBrush" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
ListBoxItemDataTemplate - The next step is to make the item container that displays your data. In my example the YourTextBlockStyler has a trigger on the Text>binding and changes the foreground and background colours of the text. Take note that the foreground and background of the Listbox style are set to transparent, so you have to over ride them in your TextBlock style if you want to see anything.
<UserControl.Resources>
<DataTemplate x:Key="ListBoxItemDataTemplate">
<TextBlock Text="{Binding}" Style="{StaticResource YourTextBlockStyler}"/>
</DataTemplate>
</UserControl.Resources>
Back to the listbox - Now we've set up all the styles and templates in the Resources section we can update the listbox with Style="" ItemContainerStyle="" and ItemTemplate=""
<ListBox Width="100"
x:Name="myComboBox" Margin="8"
ItemsSource="{Binding ListBoxListSource}"
SelectedIndex="{Binding ListBox}"
Style="{StaticResource ListBoxStyle}"
ItemContainerStyle="{StaticResource ItemContainerStyle}"
ItemTemplate="{StaticResource ListBoxItemDataTemplate}">
</ListBox>
Then your boring list box will transform magically in to a totally restyled list box with red border selector
From into
All without editing a single System.ResourceBrush =]

Categories