Change of Custom Property Does Not Trigger Binding Update - c#

I try to create a custom ComboBox called MyComboBox. It has a button for switching between the previous and next items.
I store the Background color of the base in BaseBackground. This is useful, as I don't want the FrontGlyph to have the Background inherited from the templated parent.
This is my WPF code:
<Style x:Key="{x:Type local:MyComboBox}" TargetType="{x:Type local:MyComboBox}">
</Style.Resources>
<Setter Property="Focusable" Value="False" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.CanContentScroll" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyComboBox}">
<Grid Cursor="Hand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- A glyph will come here -->
<ColumnDefinition Width="*" /> <!-- The base combo box can take as much space as it can. -->
</Grid.ColumnDefinitions>
<Path x:Name="FrontGlyph" Grid.Column="0" Data="M 0.0 16.0 L 6.0 0.0 L 6.0 16.0 Z" Fill="{TemplateBinding BaseBackground}" Stretch="Fill" />
<Grid x:Name="BaseComboBox" Grid.Column="1" Background="{TemplateBinding BaseBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- Previous item -->
<ColumnDefinition Width="Auto" /> <!-- Next item -->
<ColumnDefinition Width="*" /> <!-- Content Presenter -->
<ColumnDefinition Width="Auto" /> <!-- Drop down button -->
</Grid.ColumnDefinitions>
<Button x:Name="Prev" Grid.Column="0" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 4.5 0.5 L 0.5 4.5 L 4.5 8.5 Z" Fill="Black" />
</Button>
<Button x:Name="Next" Grid.Column="1" Background="{TemplateBinding Background}" Style="{StaticResource MyUIButton}">
<Path VerticalAlignment="Center" Data="M 0.5 0.5 L 4.5 4.5 L 0.5 8.5 Z" Fill="Black" />
</Button>
<ContentPresenter x:Name="ContentSite" Grid.Column="2" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
<ToggleButton x:Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="3" Focusable="false" ClickMode="Press" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Background="{TemplateBinding Background}" />
<Popup x:Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid x:Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" BorderThickness="1" BorderBrush="{TemplateBinding Background, Converter={StaticResource LightenBrushColor}, ConverterParameter=0.5}" Background="{TemplateBinding Background}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BaseBackground" Value="Goldenrod" /> <!-- This does not work -->
<!--<Setter TargetName="FrontGlyph" Property="Fill" Value="Goldenrod" />
<Setter TargetName="BaseComboBox" Property="Background" Value="{Binding Path=Fill, ElementName=FrontGlyph}" />--> <!-- These 2 lines do work. -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the source code of MyComboBox:
class MyComboBox : ComboBox
{
public static readonly DependencyProperty BaseBackgroundProperty;
public SolidColorBrush BaseBackground { get { return (SolidColorBrush)GetValue(BaseBackgroundProperty); } set { SetValue(BaseBackgroundProperty, value); } }
static MyComboBox()
{
BaseBackgroundProperty = DependencyProperty.Register("BaseBackground", typeof(SolidColorBrush), typeof(MyComboBox), new FrameworkPropertyMetadata(Brushes.Lime, FrameworkPropertyMetadataOptions.AffectsRender, OnBaseBackgroundPropertyChanged));
}
public MyComboBox()
{
DefaultStyleKey = typeof(MyComboBox);
}
private static void OnBaseBackgroundPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// This is not called when the trigger tries to set BaseBackground when the mouse is over the control
}
}
When I hover the mouse over the control, it should change colors. I should be able to do this via changing the BaseBackground, since both FrontGlyph and BaseComboBox have their background color bound to that. Nevertheless the code compiles but the change does not happen. Not just on the UI, but if I debug the code, I don't see the change in C# either.
If I change the background color of the FrontGlyph and bind the BaseComboBox.BackgroundColor to that, it works nicely.
Could someone explain why the changes of my custom property BaseBackground is not registered? It must not be the standard "Implement the INotifyPropertyChanged interface." issue, as I use Brush as my property and Brushes work pretty well everywhere else. :)
My implementation might look silly. Well, I'm kinda new to WPF. Plus I didn't want to burden you with the whole implementation, just tried to replicate the critical parts.
UPDATE
I've found out that in my source code I set BaseBackground = new SolidColorBrush(...) if some conditions are fulfilled. If I remove this line of code, now the triggers work and BaseBackground gets assigned the Goldenrod color.
But I wonder, why changing a DependencyProperty from C# code prevents it working from XAML markup. Besides, I need both of them to work.
Thank you.

Summary
I think what you probably want to do is register the BaseBackgroundProperty under the name "BaseBackground" (rather than "Background") since your xaml is trying to set a property with that name.
Details
Looking at your code, I think the reason your MyComboBox.BaseBackground property is never set is because it is being registered with the dependency property system using the name "Background". This means you have a property registered in a base class as "Background" (Control.BackgroundProperty), and a property registered in your derived class as "Background" (MyComboBox.BaseBackgroundProperty). So in theory, setting "Background" in xaml should end up setting MyComboBox.BaseBackgroundProperty while setting "Control.Background" should end up setting the Control.Background property.
While that works in theory, I have no idea if it works in practice. It is also not really the way to go about doing things. You could override the metadata for Control.BackgroundProperty in your type initializer (aka static constructor) if you wanted to modify the existing property for your class in some way, but I don't think that is what your intention is here. Your xaml is trying to set the non-existent property named "BaseBackground".

Based on your question update, it looks like what you are doing is setting a local value on the dependency property, which might be breaking the template binding. This can happen with one way bindings in certain situations where the target gets set by some other means. It seems like you are setting the source in this case, but it is possible that it is causing the binding to break.
You can debug the binding by setting the PresentationTraceSources.TraceLevel property on it to "High". (You might need to use the standard {Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay} syntax rather than the {TemplateBinding} shortcut syntax in order to attach the property – I am not sure.) Then, run the application and look at the debug output. It will tell you if the binding gets broken.
If the binding is in fact being broken, there are different things you can do to fix it, depending on your use cases.
You can set the binding mode to TwoWay to keep it from breaking.
You can try only setting it from code using SetCurrentValue instead of SetValue, as many controls tend to do when modifying their own dependency properties from code.
Potentially related information:
DependencyProperty Value Precedence
In WPF Why Doesn't TemplateBinding Work Where Binding Does?
Debugging Data Bindings
Control Authoring Overview

Related

C# WPF The global style not working in some parts of the code [duplicate]

I am trying to learn something about WPF and I am quite amazed by its flexibility.
However, I have hit a problem with Styles and DataTemplates, which is little bit confusing.
I have defined below test page to play around a bit with styles etc and found that the Styles defined in <Page.Resources> for Border and TextBlock are not applied in the DataTemplate, but Style for ProgressBar defined in exactly the same way is applied.
Source code (I just use Kaxaml and XamlPadX to view the result)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="SkyBlue"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="CornerRadius" Value="5"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Height" Value="10"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
<XmlDataProvider x:Key="TestData" XPath="/TestData">
<x:XData>
<TestData xmlns="">
<TestElement>
<Name>Item 1</Name>
<Value>25</Value>
</TestElement>
<TestElement>
<Name>Item 2</Name>
<Value>50</Value>
</TestElement>
</TestData>
</x:XData>
</XmlDataProvider>
<HierarchicalDataTemplate DataType="TestElement">
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" Margin="5,5" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="{Binding XPath=Name}"/>
<ProgressBar Value="{Binding XPath=Value}"/>
</StackPanel>
</Border>
</HierarchicalDataTemplate>
</Page.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Item 1"/>
<ProgressBar Value="25"/>
</StackPanel>
</Border>
<Border Height="45" Width="120" Margin="5,5">
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Item 2"/>
<ProgressBar Value="50"/>
</StackPanel>
</Border>
</StackPanel>
<ListBox Margin="10,10" Width="140" ItemsSource="{Binding Source={StaticResource TestData}, XPath=TestElement}"/>
</StackPanel>
</Page>
I suspect it has something to do with default styles etc, but more puzzling is why some Styles are applied and some not. I cannot find an easy explanation for above anywhere and thus would like to ask if someone would be kind enough to explain this behaviour in lamens' terms with possible links to technical description, i.e. to MSDN or so.
Thanks in advance for you support!
I discovered a simple workaround for this. For any elements that are not able to search outside the data template encapsulation boundary (i.e. are not being implicitly styled), you can just declare an empty style within the data template for that element type and use the BasedOn attribute of the style to find the correct implicit style outside the data template to apply.
In the example below, the TextBox is able to search outside the data template encapsulation boundary (because it inherits from Control?), but the TextBlock is not able to, so I declare the empty style for it which can search outside the data template.
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}" />
</DataTemplate.Resources>
<DockPanel>
<TextBlock Text="{Binding Name}" />
<TextBox Text="{Binding Value}" />
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
This is actually by design. Elements that do not derive from Control will not pick up implicit Styles, unless they are in the application resources.
This link explains this in more detail, or you can view the Connent bug report.
I've looked into this also, and I personally think it's a bug. I've noticed that the style is set if you name your styles like so:
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
etc...
and explicitly set your DataTemplate to use those styles:
<HierarchicalDataTemplate DataTemplate="TestElement">
<Border Height="45" Width="120" Margin="5,5", Style="{StaticResource BorderStyle}">
I think that it's possible that for DataTemplates (and maybe ControlTemplates), they default to having a null style, unless you explicitly set them.
That to me is not meant to happen - it's not a logical way of WPF working...
This is because ListBox is a logical parent of your datatemplate items, now remember, all properties those are "inheritable" like font, forecolor etc, are derived from the logical parent and ListBox already overrides it in its own default style, thats why this will not work. However in this case, you can use named styles as Mr. Dave has suggested, but I think if it does not work then this is a known problem in case of List Box etc, you can refere to my question here, i had similar problem in listbox, and the answers in my question are in more detail.

Why does adding ContentControl cause my application to enter break mode? [duplicate]

This question already has answers here:
Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception
(5 answers)
Closed 5 years ago.
Brief
I've created a beautiful WindowChrome style to apply to my windows. When I add ContentControl to my style, however, the application enters break mode.
I've pieced together code from this youtube video, this article, this SO question and Microsoft's documentation and I've come up with the following code.
Note: The code below is all considered relevant since the application cannot run with either of these parts (yes I know it can run without the code-behind, but it's annoying having to stop the application from Visual Studio instead of the close button - which is also what I'm trying to accomplish). I've actually slimmed down the code below so that it's easier to work with.
Code
Window.xaml
<Style x:Key="TestWindow" TargetType="{x:Type Window}">
<Setter Property="Background" Value="#FF222222"/>
<Setter Property="BorderBrush" Value="WhiteSmoke"/>
<Setter Property="BorderThickness" Value="5,30,5,5"/>
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="20"
CornerRadius="0"
GlassFrameThickness="0,0,0,-1"
NonClientFrameEdges="None"
ResizeBorderThickness="5"
UseAeroCaptionButtons="True"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
<DockPanel LastChildFill="True" VerticalAlignment="Top" Height="30">
<StackPanel DockPanel.Dock="Right"
Orientation="Horizontal"
VerticalAlignment="Center">
<Button x:Name="Button_Close"
WindowChrome.IsHitTestVisibleInChrome="True"
Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
Click="CloseClick">
<ContentControl Template="{StaticResource Icon_Close}" Height="10"/>
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Left"
Orientation="Horizontal"
VerticalAlignment="Center">
<Image x:Name="PART_WindowCaptionIcon"
Width="16"
Height="16"
Margin="0,0,6,0"
Source="{TemplateBinding Icon}"/>
<TextBlock x:Name="PART_WindowCaptionText"
Margin="6,0,0,0"
Padding="0">
<Run BaselineAlignment="Center"
Text="{TemplateBinding Title}"
Foreground="Black"/>
</TextBlock>
</StackPanel>
</DockPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="PART_WindowCaptionIcon" Property="Source" Value="{x:Null}">
<Setter TargetName="PART_WindowCaptionIcon" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="PART_WindowCaptionText" Property="Margin" Value="5,0,0,0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Icons.xaml
Window.xaml makes reference to this file for the ContentControl Template attribute values through App.xaml.
<ControlTemplate x:Key="Icon_Close">
<Viewbox>
<Polygon Points="357,35.7 321.3,0 178.5,142.8 35.7,0 0,35.7 142.8,178.5 0,321.3 35.7,357 178.5,214.2 321.3,357 357,321.3 214.2,178.5" Fill="Black"/>
</Viewbox>
</ControlTemplate>
Window.xaml.cs
public partial class Window : ResourceDictionary
{
public Window()
{
InitializeComponent();
}
private void CloseClick(object sender, RoutedEventArgs e)
{
var window = (System.Windows.Window)((FrameworkElement)sender).TemplatedParent;
window.Close();
}
}
Issue
When the line <ContentControl Template="{StaticResource Icon_Close}" Height="10"/> is present (line 38), the following message is received. When the same line is removed/commented out the application runs without entering break mode.
Looking at the Output window I'm getting the following messages:
An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception.
Questions
This code worked when placed directly in the XAML code for the Window, but the moment I try to place it in the template it fails.
My questions are:
Why does my application enter break mode when ContentControl is placed in the Window's template?
How can I resolve this problem?
Please note that I must use my ControlTemplate from the Icons.xaml file and that the call to this content must remain in the window's Style (and not the window's actual xaml).
Brief
The issue was due to an incorrect order of my styles as per the answer on this question. I'm marking my question as a duplicate of that one, but felt I should share this as the answer in case it helps anyone else.
I love that Microsoft doesn't handle this with an appropriate exception and that you need to bang your head against a wall until either your head or the wall breaks.
Code
My App.xaml contains the following code in ResourceDictionary.MergedDictionaries
<ResourceDictionary Source="pack://application:,,,/MyProject;component/Window.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MyProject;component/Icons.xaml"/>
I changed the order to the following
<ResourceDictionary Source="pack://application:,,,/MyProject;component/Icons.xaml"/>
<ResourceDictionary Source="pack://application:,,,/MyProject;component/Window.xaml"/>

WPF: Binding Property to an object in Style

I have this style:
<Style x:Key="{x:Type TextBox}" TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True" CornerRadius="0,10,10,0" Padding="5,0,10,0" MinWidth="0" VerticalAlignment="Stretch">
<Grid VerticalAlignment="Center">
<Label x:Name="label" Content="{Binding LContent}" HorizontalAlignment="Left" VerticalAlignment="Stretch" Foreground="Gray" Padding="0,0,5,0" Margin="0" BorderBrush="#FF2C2C2C" BorderThickness="0,0,1,0"/>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" Template="{DynamicResource ComboBoxScrollViewerControlTemplate}" Margin="30,1,0,0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" />
</Grid>
</Grid>
</Border>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="CaretBrush" Value="#FF646464"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="CaretBrush" Value="#FF323232"/>
</Trigger>
</Style.Triggers>
</Style>
and in behind code, I wrote this function as new property for Label object to binding LContent value to label object:
public string LabelContent
{
get { return (string)GetValue(LContent); }
set { SetValue(LContent, value); }
}
public static readonly DependencyProperty LContent =
DependencyProperty.Register("LabelContent", typeof(string), typeof(CustomizedTextBox), new PropertyMetadata("Label"));
but label content doesn't change.
can you help me?
You've approached template binding in the same way as you would a normal control, and this is wrong. Think of it this way: it is a total and utter waste of time to define a template if you are going to explicitly bind to a specific property. A template is supposed to be reused across multiple instances of a control, and they can't all be binding to that one property, could they?
Instead what you need to do is use:
{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Name}
or its shorted version:
{TemplateBinding Name}
This tells the binding subsystem to use the parent control (the one you are templating) as a source for the binding.
This cheat sheet might be a valuable reference for you. The previous SO question What is the template binding vs binding? also has a nice simple example of this.
First of all I'm not sure what x:Key="{x:Type TextBox}" does, but make sure the data context is propely set. Like if you're using it in a window
You can do
<Window.DataContext>
<local:CustomizedTextBox></local:CustomizedTextBox>
</Window.DataContext>
Though it's not a good practice.
And also I noticed that you've Registered the Property Name as LabelContent and you are using LContent in the Binding. Changing it to LabelContent can help.
<Label x:Name="label" Content="{Binding LabelContent}" >
You've set you're TargetType to be TextBox and even if your custom control is derived from TextBox it will not apply to it, you should directly Specify your custom controls class name that is CustomTextBox.
To make things clear, I would say that If you've a x:Key attribute in you're style, if will not automatically get applied to all the targetType element. Then you have to explicitly specify the style for each control.
And to get you exited, I have made this work. If you want I will later post what I have done. But now I am in a hurry.
Hope it, helps.
To set the label content directly from the text box text, we need to set the content of the label to the Text property of the Text box
<Label x:Name="label" "{Binding Text, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"
To read the set value of the label which can be done directly using the property with INotifyPropertyChanged or Dependency property in your case where MyMainWindow is the my usercontrol window name
<TextBox Grid.Row="0" x:Name="CustomizedTextBox" Text="{Binding ElementName=MyMainWindow, Path=LabelContent }"
In Code base we have DP defined as below
public static readonly DependencyProperty LContentProperty =
DependencyProperty.Register("LabelContent", typeof(string), typeof(MainWindow));
public string LabelContent
{
get { return (string)GetValue(LContentProperty); }
set { SetValue(LContentProperty, value); }
}

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>

WPF What is the difference between setting the background to transparent and setting opacity on the control?

So I realized that my graph viewer had the axis displaying over the actual items in the graph, so I changed the ZIndex on the grid to display the items over the axis instead.
However, I noticed that I couldn't actually see anything under the actual items because the background of the items were opaque. I think I have two options then, to either set the background of the items to transparent, or to set the opacity of the items. Is there any difference between these two options?
<Grid
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ZIndex="1"
>
<Components:SignalGraphAxis
x:Name="signal_axis"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
GraphHeight="{Binding Path=GraphHeight}"
PenColor="{Binding Path=AxisColor, Mode=OneWay}"
PenWidth="{Binding Path=GraphPenWidth, Mode=OneWay}"
MinHeight="10"
MinWidth="10"
AxisTimeScale="{Binding Path=GraphTimeScale}"
NumberOfPixelsPerDivision="{Binding Path=NumberOfPixelsPerDivision, Mode=OneWay}"
MinDisplayValue ="{Binding Path=MinDisplayValue, Mode=OneWay}"
UnitsOfGraphTimePerInch="{Binding Path=UnitsOfTimePerInch, Mode=OneWay}"
/>
</Grid>
<ScrollViewer
x:Name="signal_scrollviewer"
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.ZIndex="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
CanContentScroll="True"
Style="{StaticResource SignalScrollViewerStyle}"
>
<ItemsPresenter />
</ScrollViewer>
</Grid>
Background property is defined on Control class and Opacity is defined much higher on UIElement.
From MSDN Page Control.Background Property
This property only affects a control whose template uses the
Background property as a parameter. On other controls, this property
has no impact.
Let's try to create a Custom Control to see how this works.
CustomControl1.cs
public class CustomControl1 : ContentControl
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
}
Default Template For CustomControl1
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="My Custom Control " Grid.Row="0" />
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Please note, the above template doesn't use Background property at all in it's Template.
Now, Let's try to use that in a Form and see how it behaves:
Code from Window1.xaml
<Grid>
<wpfApplication5:CustomControl1 Background="Green">
<Button Content="Button Within Custom Control" Margin="25"/>
</wpfApplication5:CustomControl1>
</Grid>
The resultant output:
See, there was no Green background for the rendered CustomControl even though we set the Background to Green in Window1.xaml.
Now, Lets modify the template to use Background property.
Template with Background Property
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="My Custom Control " Grid.Row="0" />
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the resultant output will now have a the specified background applied.
I think (couldn't find any references though), Opacity gets applied to the element/Control whether the Control's Template worry about Opacity property or not.
Window1.xam with Opacity Set on CustomControl
<Grid>
<wpfApplication5:CustomControl1 Background="Green" Opacity="0.2">
<Button Content="Button Within Custom Control" Margin="25"/>
</wpfApplication5:CustomControl1>
</Grid>
and resultant Output
See, the Opacity got applied even though our Custom Control's template doesn't worry anything about Opacity property.
Finally, to answer your question: Though either setting Opacity to 0 or Background to Transparent may give you the same visual result. But, for Background property, it totally depends on the Control implementation and how it handles Background property. Whereas, with Opacity it gets applied from parent elements to down the elements tree to child elements irrespective of the control.
Refer to MSDN Page, UIElement.Opacity Property to read more on Opacity property and how it behaves when Opacity is set at multiple levels in an element tree.

Categories