Binding Custom Button Background/ Border to Custom Property - c#

I am fairly new to WPF and the .NET Framework.
I have created a custom button class and have added a dependency property "Colors" which is another class that I created which defines a buttons border/face colors when enabled and disabled. In the style for this button, I am trying to use Relative Source Binding to Bind members of the "Colors" property to different Properties of the button (Border.Background, Border.BorderBrush, etc.).
Here is my button class:
public class FsnButton : Button
{
static FsnButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FsnButton), new FrameworkPropertyMetadata(typeof(FsnButton)));
}
public FsnButton()
{
Colors = FsnColors.GrayBtnColors;
}
public GuiTypes.ButtonColors Colors
{
get { return GetValue(ColorsProperty) as GuiTypes.ButtonColors; }
set { SetValue(ColorsProperty, value); }
}
public static readonly DependencyProperty ColorsProperty =
DependencyProperty.Register("Colors", typeof(GuiTypes.ButtonColors), typeof(FsnButton), null);
}
And here is the template portion of the style
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:FsnButton">
<Border Name="Face" CornerRadius="3"
Background="{ Binding RelativeSource={RelativeSource Self}, Path=Colors.Enabled.Face}"
BorderThickness="1"
BorderBrush="{ Binding RelativeSource={RelativeSource Self}, Path=Colors.Enabled.Border}">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{ Binding RelativeSource={RelativeSource Self}, Path=Colors.Enabled.Border}"></Setter>
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="DarkSlateGray" Direction="320" ShadowDepth="0" BlurRadius="5" Opacity="0.5"></DropShadowEffect>
</Setter.Value>
</Setter>
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="3" Y="3" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
This approach is unsuccessful. When I create an instance of my button class, It doesn't get drawn at all. Am I on the right path? Is there a better way to do what I want?

Your binding paths doesn't resolve. You have to use either TemplateBinding or RelativeSoure TemplatedParent, whenever you are inside a ControlTemplate and want to bind to the templated control itself:
<ControlTemplate TargetType="local:FsnButton">
<!-- TemplatedParent binding source -->
<Border Name="Face"
Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Colors.Enabled.Face}"
BorderBrush="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Colors.Enabled.Border}" />
<!-- TemplateBinding -->
<Border Name="Face"
Background="{TemplateBinding Colors.Enabled.Face}"
BorderBrush="{TemplateBinding Colors.Enabled.Border}" />
</ControlTemplate>

Related

Styling a Derived UserControl causes it to disappear

I have a control PaneBase, which derives from UserControl. There is no XAML, it's just a control. The UserControl type in question is the one from Catel, but I still observe this issue when using System.Windows.Controls.UserControl.
public class PaneBase : UserControl
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title",
typeof(string),
typeof(PaneBase),
new PropertyMetadata(default(string)));
public string Title
{
get => (string) GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
}
I have another control EquationPane which derives from PaneBase and does have XAML:
EquationPane.xaml
<local:PaneBase x:Class="EngineersToolkit.Windows.Views.Panes.EquationPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://schemas.catelproject.com" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EngineersToolkit.Windows.Views.Panes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Equation"
d:DataContext="{d:DesignData EquationPaneViewModel}" d:DesignHeight="450" d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<TextBox Width="150" Height="30"
Text="{Binding Equation, Mode=TwoWay}" />
</Grid>
</local:PaneBase>
EquationPane.xaml.cs
public partial class EquationPane
{
public EquationPane()
{
InitializeComponent();
DataContext = new EquationPaneViewModel();
}
}
All of this works so far, when I include an EquationPane in a window, then it shows up normally:
<Grid>
<panes:EquationPane Width="100" Height="100"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Aqua" />
</Grid>
It continues to work properly if I attempt to style the control:
<Style TargetType="{x:Type panes:EquationPane}">
<Setter Property="Background" Value="Red" />
</Style>
However, things begin to fall apart if I target the style to PaneBase instead. If I change TargetType to PaneBase, then the style is simply not applied. However, if I override the dependency property metadata in PaneBase like this:
static PaneBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PaneBase),
new FrameworkPropertyMetadata(
typeof(PaneBase)));
}
Then the control ceases to render at all, leaving me with a blank window. Why is this happening?
Why is this happening?
WPF controls are "lookless" which means that they have no appearance unless you define a template for them.
The following indicates that there is a default style defined for PaneBase in themes/generic.xaml:
static PaneBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PaneBase),
new FrameworkPropertyMetadata(
typeof(PaneBase)));
}
You should then define such a style:
<Style TargetType="local:PaneBase">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you remove the static constructor in PanelBase it will inherit the template from the style of the UserControl base class.
If you then define a Style for PaneBase like this:
<Style TargetType="{x:Type local:PaneBase}">
<Setter Property="Background" Value="Red" />
</Style>
...it no longer has a template and that's why you don't see the control in your window.
Implicit Styles are not automatically applied to the derived types. To apply the style of PaneBase to EquationPane, you can do one of the following:
1.Write this on your EquationPane.xaml.cs
public EquationPane()
{
this.SetResourceReference(StyleProperty, typeof(PaneBase));
}
2.Explicitly provide the style where EquationPane is used
<panes:EquationPane Style="{DynamicResource {x:Type PaneBase}}"/>
3.Define a style that is basedon the parent style
<Style x:Key="{x:Type PaneBase}" TargetType="{x:Type PaneBase}">
<Setter Property="Background" Value="Red" />
</Style>
<Style TargetType="{x:Type panes:EquationPane}" BasedOn="{StaticResource {x:Type PaneBase}}"/>

Change style (in ResourceDictionary) from code

I have this ResourceDictionary
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MainMenuLabelStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "Foreground" Value="White"/>
<Setter Property= "FontSize" Value="18"/>
<Setter Property= "FontFamily" Value="Arial"/>
</Trigger>
</Style.Triggers>
</Style>
If I want change the font size or color, what can I do ? This code doesn't work .
Application.Current.Resources("MainMenuLabelStyle") = 25
This is the xaml
<TextBlock Text="Uscita" Grid.Row="1" Grid.Column="1" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}">
Just before a style is used for the first time in a WPF application, it is sealed for performance reasons and it is not possible to modify it anymore. You can read it on MSDN.
So, if you want to change your style, you have to options. The first one (the easiest one) is to declare as many styles as you need and put them in your ResourceDictionary.
The second solution is to consider that a Setter is a DependencyObject, so you can bind its dependency properties. In this case your style will become:
<Style x:Key="MainMenuLabelStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.Foreground, TargetNullValue=Red, FallbackValue=Red}" />
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.FontSize, TargetNullValue=18, FallbackValue=18}" />
<Setter Property="FontFamily" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.FontFamily, TargetNullValue=Arial, FallbackValue=Arial}" />
</Trigger>
</Style.Triggers>
</Style>
Now you can change the style just by setting the Tag property of every TextBlock control:
<StackPanel>
<TextBlock Text="Uscita" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}" />
<TextBlock Text="Uscita" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}">
<TextBlock.Tag>
<local:StyleConfig FontSize="50" FontFamily="Tahoma" Foreground="Orange" />
</TextBlock.Tag>
</TextBlock>
</StackPanel>
As you can see the first TextBlock will use the style as it was declared. On the other side, the second TextBlock will use a modified version of the original style.
Of course, in order to make this option work correctly, you must create a class (StyleConfig in my sample), which could be something like this:
public class StyleConfig
{
public string Foreground { get; set; }
public string FontSize { get; set; }
public string FontFamily { get; set; }
}
I hope it can help you.
In your code:
Application.Current.Resources("MainMenuLabelStyle") = 25
1) Wrong syntax. Application.Current.Resources["MainMenuLabelStyle"]
2) Application.Current.Resources["MainMenuLabelStyle"] this code will return object with type Style, not style property Font Size.
You can create new Style and replace it in ResourceDictionary.

Change the Color Displayed in WPF Charting Toolkit Legend

I have the following style which removes data point and randomly generates a line color for my line series plots
<Style x:Key="LineDataPointStyle"
TargetType="ChartingToolkit:LineDataPoint">
<Setter Property="Foreground" Value="DarkGreen"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Width" Value="NaN"/>
<Setter Property="Height" Value="NaN"/>
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource ColorBrushConverter}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ChartingToolkit:LineDataPoint">
<Grid x:Name="Root" Opacity="0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
where the converter is:
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return new SolidColorBrush(Utils.GenerateRandomColor());
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This generate the line in a random color, but the legend is a different color; either being auto-generated by the library itself or it is also calling my converter through the style template.
How can I get the legend to print the correct color?
Note: is the answer to the question of Killercam, that has been asked here. Answer to this question is particularly suitable for his bounty, so at his request I publish it here.
In this answer, the Button control is used to demonstrate working with templates.
Part I. Binding in ControlTemplate
If you want to use Binding in a ControlTemplate, you should use following construction:
<ControlTemplate TargetType="{x:Type SomeControl}">
<Rectangle Fill="{TemplateBinding Background}" />
Quoted from MSDN:
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.
Notes about using TemplateBinding
TemplateBinding doesn’t work outside a template or outside its VisualTree property, so you can’t even use TemplateBinding inside a template’s trigger. Furthermore, TemplateBinding doesn’t work when applied to a Freezable (for mostly artificial reasons), for example - VisualBrush. In such cases it is possible to use Binding like this:
<FreezableControl Property="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Background}" />
Also, you can always use an alternative for TemplateBinding:
<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Background}" />
As another possibility, you can also try the following:
<Rectangle Fill="{Binding Background,
RelativeSource={RelativeSource AncestorType={x:Type SomeControl}},
Path=Background}" />
Part II. Notes about your version
In your case, this may cause a conflict of names in the ControlTemplate, because you already are using Binding background is for Border. Therefore, remove it this Binding for a Border, or use another property, such as Tag or attached dependency property for binding Background color.
Example of using
Instead ChartingToolkit controls, took as a basis Button control, because it's easier to demonstrate the idea of ​​this styles.
Solution 1: using Tag
<Window.Resources>
<Style x:Key="TestButtonStyle" TargetType="{x:Type Button}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<!-- Here we are set Tag for Border Background -->
<Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Rectangle Width="24"
Height="24"
Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"
Stroke="{TemplateBinding BorderBrush}" />
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Name="TestButton"
Style="{StaticResource TestButtonStyle}"
Content="Test"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Tag="Green"
Background="Aquamarine"
Width="100"
Height="100" />
</Grid>
Output
Here for Rectangle, set two colors: default for Rectangle, in Tag for Border. I do not find this a good solution, and here's why:
If a Border and Rectangle need to set different values, such as: Background, BorderThickness, BorderBrush, etc. one Tag is not enough.
With one name property must be clearly its purpose, one name "Tag" us to nothing says.
Of these disadvantages can be concluded that we should find an alternative, as an alternative I use a extender-class with the attached dependency properties.
Extender class ButtonExt.cs
public static class ButtonExt
{
#region RectangleBackground Property
public static readonly DependencyProperty RectangleBackgroundProperty;
public static void SetRectangleBackground(DependencyObject DepObject, Brush value)
{
DepObject.SetValue(RectangleBackgroundProperty, value);
}
public static Brush GetRectangleBackground(DependencyObject DepObject)
{
return (Brush)DepObject.GetValue(RectangleBackgroundProperty);
}
#endregion
#region RectangleBorderBrush Property
public static readonly DependencyProperty RectangleBorderBrushProperty;
public static void SetRectangleBorderBrush(DependencyObject DepObject, Brush value)
{
DepObject.SetValue(RectangleBorderBrushProperty, value);
}
public static Brush GetRectangleBorderBrush(DependencyObject DepObject)
{
return (Brush)DepObject.GetValue(RectangleBorderBrushProperty);
}
#endregion
#region Button Constructor
static ButtonExt()
{
#region RectangleBackground
PropertyMetadata BrushPropertyMetadata = new PropertyMetadata(Brushes.Transparent);
RectangleBackgroundProperty = DependencyProperty.RegisterAttached("RectangleBackground",
typeof(Brush),
typeof(ButtonExt),
BrushPropertyMetadata);
#endregion
#region RectangleBorderBrush
RectangleBorderBrushProperty = DependencyProperty.RegisterAttached("RectangleBorderBrush",
typeof(Brush),
typeof(ButtonExt),
BrushPropertyMetadata);
#endregion
}
#endregion
}
MainWindow.xaml
<Window.Resources>
<Style x:Key="TestButtonExtensionStyle" TargetType="{x:Type Button}">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Setter Property="Background" Value="Green" />
<Setter Property="BorderBrush" Value="Pink" />
<Setter Property="BorderThickness" Value="4" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Rectangle Fill="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBackground}"
Stroke="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBorderBrush}"
Width="30"
Height="30" />
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource TestButtonExtensionStyle}"
PropertiesExtension:ButtonExt.RectangleBackground="Aquamarine"
PropertiesExtension:ButtonExt.RectangleBorderBrush="Black"
Content="Test" />
</Grid>
Output
Part III. Setting values for dependency properties
When you create and register your attached dependency property, you must declare the Set and Get methods to work with him:
public static void SetRectangleBackground(DependencyObject DepObject, Brush value)
{
DepObject.SetValue(RectangleBackgroundProperty, value);
}
public static Brush GetRectangleBackground(DependencyObject DepObject)
{
return (Brush)DepObject.GetValue(RectangleBackgroundProperty);
}
Then work with them will be as follows:
Set
ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);
Get
Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);
But in our case, it's not so simple. When I used the attached dependency property problems in updating values ​​were not. But in our case, the property is in the template, and in my case there was no update Button. I tried to set Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, in Binding and in the property declaration, GetBindingExpression().UpdateTarget(), but it was useless.
Note that for the property setting a new value, and notification from the template is not, that the property has been updated. Maybe I'm wrong, and you have will work, or maybe it's made specifically, for example to avoid memory leaks.
In any case, it is better not to update the dependency property directly, and bind to it the property of the Model and in the ViewModel to set the value.
Example:
<Button Style="{StaticResource TestButtonExtensionStyle}"
adp:ButtonExt.RectangleBackground="{Binding Path=Model.RectBackground,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
adp:ButtonExt.RectangleBorderBrush="{Binding Path=Model.RectBorderBrush,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
where RectBackground and RectBorderBrush implement the INotifyPropertyChanged interface.
As an alternative in this case, do not use dependency properties and use the DataTemplate for the control. DataTemplate ideal for MVVM, very flexible and dynamic.
For example, work with DataTemplate, you can see my answers:
Make (create) reusable dynamic Views
One ViewModel for UserControl and Window or separate ViewModels
I do something similar, where I generate plots with changing colors, but these colors are randomly selected from a preferred list (I have a black background and some colors just don't work very well on top of black) . I set the color from code behind and I am not sure this is something you can do.
In your case I would try something like this:
//If you declare your style in a resource dictionary, get that resource first
ResourceDictionary resD = (ResourceDictionary)Application.LoadComponent(new Uri("ResourcesPlot\\ResourceDictionaryPlot.xaml", UriKind.Relative));
//The actual style
Style lineDataPointStyle= (Style)resD["LineDataPointStyle"];
//Set the color
lineDataPointStyle.Setters.Add(new Setter(BackgroundProperty, Utils.GenerateRandomColor()));
Hope this works.
Edit:
For the legend I use this (I have an extra checkbox for showing/hiding a certain plot)
<Style x:Key="CustomLegendItemStyle" TargetType="{x:Type chartingToolkit:LegendItem}">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:LegendItem">
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" Margin="3" IsChecked="true" Checked="DisplaySeries_Checked" Unchecked="DisplaySeries_Unchecked"/>
<!--<Rectangle VerticalAlignment="Center" Width="8" Height="8" Fill="{DynamicResource MyBackgroundDiode1}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="5,5,5,5" />-->
<chartingToolkit:LegendItem VerticalAlignment="Center" Content="{TemplateBinding Content}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Command Binding in ControlTemplate with Attached Property

I'm trying to set up a system that would allow me to attach a string value to a ComboBoxItem and display it next to the ComboBoxItem's content, without having to explicitly nest a StackPanel inside every individual ComboBoxItem or use a custom control.
So what I've done is created a DependencyProperty called "Header" and attach it to the ComboBoxItem, and I've overridden the ComboBoxItem template to contain the stack panel with a TextBlock that binds its Text to the attached Header property of the ComboBoxItem.
The problem I am having is that the only text that ever appears in the TextBlock at runtime is whatever I set the default value of the dependency property to in metadata. Any changes to the attached property on the ComboBoxItems thereafter is not reflected in the TextBlock.
Here is my DependencyProperty definition:
public class AttHeader : DependencyObject
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.RegisterAttached("Header", typeof(string), typeof(AttHeader));
public static void SetHeader(DependencyObject d, string value)
{
d.SetValue(HeaderProperty, value);
}
public static string GetHeader(DependencyObject d)
{
return (string)d.GetValue(HeaderProperty);
}
}
Here is my style and template:
<Style TargetType="ComboBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<TextBlock Name="HeaderHost" Text="{Binding Path=(local:AttHeader.Header), RelativeSource={RelativeSource Self}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And here is where I create a few ComboBoxItems:
<ComboBox SelectedIndex="0">
<ComboBoxItem local:AttHeader.Header="Isometric">
<Image Source="../images/viewTypeIso.png" Stretch="None"/>
</ComboBoxItem>
<ComboBoxItem local:AttHeader.Header="Top">
<Image Source="../images/ViewTypeTop.png" Stretch="None"/>
</ComboBoxItem>
</ComboBox>
Even setting the value of the attached properties of these ComboBoxItems when they are created has no effect on the TextBlocks within.
I'm assuming the binding is valid, because I can set a default value for the dependency property and each ComboBoxItem will always display that value next to its image.
I've set a breakpoint in SetHeader and it is indeed called when those ComboBoxItems are constructed.
Is there something I am doing wrong or is there some limitation to Binding that I'm unaware of?
your binding isn't correct , TextBlock is now a child of the ComboBoxItem
try binding to :
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<TextBlock Name="HeaderHost"
Text="{Binding Path=local:AttHeader.Header,
RelativeSource={RelativeSource Mode=FindAncestor
,AncestorType=ComboBoxItem}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
I still haven't gotten the header binding to work with that implementation, but here's a super easy convenient workaround: Use HeaderedContentControls instead of ComboBoxItems, and instruct your ComboBox to orient the Header Horizontally by implementing the following Style:
<Style TargetType="HeaderedContentControl">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<ContentPresenter ContentSource="Header" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

MouseOver trigger doesn`t work after programmatically set Foreground

I'm new to WPF, but searching internet for some days I couldn't figure out my problem.
After I programmatically change Foreground property, IsMouseOver trigger doesn't work. Please be tolerant and thank in advance :)
<Style x:Key="ZizaMenuItem" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Margin" Value="5,0,5,0"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Label FontSize="14" Content="{TemplateBinding Content}" Name="ZizaMenuItemText" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ZizaMenuItemText" Property="Foreground" Value="#ff0000"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<StackPanel Height="30" Name="ZizaMenu" Orientation="Horizontal" Margin="0,12,0,0" VerticalAlignment="Top">
<Label Content="ZIZA" FontSize="11" FontWeight="Bold" Foreground="Black" Height="25" Margin="20,0,10,0" />
<Button Name="ZizaMenuInteresting" Click="ZizaMenuItemClicked" Content="ИНТЕРЕСНОЕ" Style="{StaticResource ZizaMenuItem}" />
<Button Name="ZizaMenuBest" Click="ZizaMenuItemClicked" Content="ЛУЧШЕЕ" Style="{StaticResource ZizaMenuItem}" />
<Button Name="ZizaMenuAuto" Click="ZizaMenuItemClicked" Content="АВТО" Style="{StaticResource ZizaMenuItem}" />
</StackPanel>
private void ZizaMenuItemClicked(object sender, RoutedEventArgs e)
{
// get label object from template
Button zizaMenuItem = (Button)sender;
Label zizaMenuItemText = (Label)zizaMenuItem.Template.FindName("ZizaMenuItemText", zizaMenuItem);
// set Foreground color for all buttons in menu
foreach (var item in ZizaMenu.Children)
if (item is Button)
((Label)(item as Button).Template.FindName("ZizaMenuItemText", (item as Button))).Foreground = Brushes.Black;
// set desired color to clicked button label
zizaMenuItemText.Foreground = new SolidColorBrush(Color.FromRgb(102, 206, 245));
}
That is horrible code, do not mess with controls inside control templates, ever. Template.FindName is something only the control that is being templated should call internally to get its parts, and only those, everything else should be considered uncertain.
If you need to change a property template bind it, and then bind or set said property on the instance. In terms of precedence you need to make sure not to create a local value which overrides the triggers (that is what you did). You can use a Style and Setter on the Label to bind the default Foreground.
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="{TemplateBinding Foreground}"/>
</Style>
</Label.Style>
Now you just need to set the Foreground of the Button itself, the Trigger should still internally have precedence over that Setter.
It has to do with dependency property value precedence. Local values have higher precedence than template triggers.
For more information read this: http://msdn.microsoft.com/en-us/library/ms743230.aspx

Categories