Property set in a style cannot be overruled with binding in xaml? - wpf

The following control definition works ok:
<local:TextBoxEx Text="{Binding Title, UpdateSourceTrigger=PropertyChanged, Delay=900}"
Foreground="{Binding Selection.Error, Converter={StaticResource BoolToErrorBrush}}"/>
When I change it using a style:
<Style x:Key="TextBoxTitle" TargetType="local:TextBoxEx">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<local:TextBoxEx Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
Foreground="Blue"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<local:TextBoxEx Text="{Binding Title, UpdateSourceTrigger=PropertyChanged, Delay=900}"
Style="{StaticResource TextBoxTitle}"
Foreground="Pink"/>
The binding to Title overrules the style binding to Text and works.
Setting the Foreground color has no effect, it is still blue.
When I use Foreground="{TemplateBinding Foreground}" in the controltemplate style it works.
I cannot understand this behaviour, can you?
In the first case the local definition is leading, in the second case it is not.

I don't see what is wrong here. The RelativeSource to the TemplatedParent will look for the Text property in the original component at runtime, which property is bound to Title. It's intrinsically equivalent to a TemplateBinding to Text (but it's slower).
About the Foreground, it'll always be blue except if you use a TemplateBinding in which case it'll take the color you defined in the TemplatedParent.
I don't understand your problem here.

In the control template you need to bind the data to the control properties i.e the TextBoxEx and when you use the control and bind it to a DataContext you pass the data to the control template via the control , when you set Foreground to blue in the control template you brake the pipe and the control color will be blue no matter what you do
DataContext -> Control -> ControlTemplate
when you set the blue in the control template you cut the second arrow
DataContext -> Control XXXXX ControlTemplate
so the blue is blue no matter what you are doing
see MSDN ControlTemplate for more info

Related

Binding to Template's parent's parent's child's property

I have an Expander style which applied template on both Header and Content
I wish to have one of the TextBlock inside content's template to match the Header's TextBlock's Foreground color
<Style TargetType="Expander">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Foreground="Blue"/> <!--Header TextBlock-->
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock/> <!--Match Header TextBlock's Foreground-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I have tried ElemenName binding but it seems like the name scope is different since I am 2 template level deep.
I thought about TemplateBinding but I only want one of the column in the content to match the color of header instead of the whole expander.
I could apply the same trigger for the Header TextBlock on the Content TextBlock too but I am trying to see if there is a way to avoid duplicating the code.
ElementName can't work across templates; with a template, you could have multiple elements with the same name.
Anything with different template instances reaching out to grope each other via the visual tree is going to be fraught with nameless horrors, whatever you do.
Instead, I would suggest that they both get their brushes from the same source. This is much more in line with how WPF is happy doing things.
If the color won't change, use an appropriately-named Brush resource for both.
If it will change, bind both to a viewmodel Brush property (kinda squicky, but not the end of the world), or use triggers driven by some other viewmodel property which represents the state being indicated by the color. The triggers would reference any number of appropriately-named Brush resources: ErrorBrush, HappyBrush, SadBrush, etc. By "name" I mean x:Key of course:
<SolidColorBrush x:Key="HappyBrush">GreenYellow</SolidColorBrush>
...etc.

WPF ComboBox: How to you utilise a generic ItemContainerStyle with binding

I want to utilise a generic style for my ComboBoxItem content and have the text content bound to different properties on my underlying class. So this is the best I can come up with but the bindings are hard coded. So for every class bound to a combobox using this ItemContainerStyle I'd have to implement a "MainText" and "SubText" property.
Question is, is there a way to have the binding soft coded so where the style referenced from a combobox I can specify which string properties of the underlying class are used.
<Style TargetType="{x:Type ComboBoxItem}" x:Key="ComboBoxItemStyleA1">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="BB" Padding="8,3,8,3" Background="DarkGreen">
<StackPanel Margin="0">
<TextBlock Foreground="White" FontSize="16" Text="{Binding MainText}"/>
<TextBlock Foreground="White" FontSize="8" Text="{Binding SubText}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="BB" Value="#FF256294"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And to use the style...
<ComboBox ItemsSource="{Binding Items}"
ItemContainerStyle="{StaticResource ComboBoxItemStyleA1}" />
Further to dowhilefor's answer (many many thanks - WPF is great but sure is a voyage of discovery)
I used a data template to define the cell look originally - and then wanted to use a comboboxitem based style with a control template defined where I could specify the onmouseover triggers. i.e. these were to change the background color etc.
Butj
a) I couldn't remove the Border section of the template above - the triggers are tied to it by targettype="BB". so I kind of wanted to get the trigger bound to the container such that the datatemplate would pick up the background from the template binding but not sure how to get this plumbed in.
b) I realised that even if I comment out the BB specific bindings on the triggers just to get it to run - the combobox doesn't find and use the DataTemplate I defined. Seems that defining the controltemplate in my comboboxitemstyle stops it picking up the datatemplate.
I hope I make sense here - bottom line is I just want a style that I can apply with triggers in that set the background color of my cobobox item. It should not know what the data is - i.e. be able to plug in a datatemplate that will (template ?) bind to this background color.
Many thanks for the very fast response.
btw I'm using ItemContainerStyle in conjuction with ItemTemplate so I can have a different representation in the dropdown to what appears in the combobox list
First of all don't use the ItemContainerStyle for that. To be more precise never have any Bindings to the datacontext inside an ItemContainerStyle, at least try not. Why? The Style is used for defining the appearance of a combobox item disregarding the content. If you want to define how the content should look like, you use a DataTemplate for that. There are multiple ways to tell the combobox where he can find a proper DataTemplate for the Data you supply. Checkout the property ItemTemplate, ItemTemplateSelector and search for implicit styles, to find out more about them.
So to your problem, create one ItemContainerStyle for you combobox (if you really have to anymore) which doesn't care about the object that will be put into. Now you still need to provide multiple DataTemplates each and everyone with the knowledge of the data object that you want to be templated. There is no way around it, there is no soft databinding. Just try to keep your templates small and simple. If for some reason you need the exact same template, but your properties are just named differently, why not use a wrapper item for the DataContext with the properties Caption, Description and you can decide in code how these properties are filled with your real data wrapped into this object.

WPF Styles - Please help me understand why this works the way it does

<Style x:Key="MyStyle">
<Setter Property="Window.Background" Value="Orange"/>
</Style>
<Button Content="Ok" Style="{StaticResource MyStyle}"/>
Why does the button actually get an orange background, if the setter is specified as Window.Background?
This does not give the TextBlock an orange background:
<TextBlock Style="{StaticResource MyStyle}"/>
Thanks
Neither Button nor Window actually define the Background property, they both inherit it from Control.
So even though you wrote Window.Background, the setter is actually bound to the property by using the Control.BackgroundProperty field which also applied to Button.
It works because the Background property is attached to Control class which both Window and Button have as ancestor

Difference between Label and TextBlock

According to the Windows Applications Development with Microsoft .NET 4 70-511 Training Kit
What is the difference between the Label control and TextBlock control since both are content controls and just displaying text?
TextBlock is not a control
Even though TextBlock lives in the System.Windows.Controls namespace, it is not a control. It derives directly from FrameworkElement. Label, on the other hand, derives from ContentControl. This means that Label can:
Be given a custom control template (via the Template property).
Display data other than just a string (via the Content property).
Apply a DataTemplate to its content (via the ContentTemplate property).
Do whatever else a ContentControl can do that a FrameworkElement cannot.
Label text is grayed out when disabled
Label supports access keys
Label is much heavier than TextBlock
Source
Some more interesting reads below
http://www.wpfwiki.com/WPF%20Q4.1.ashx
What is the difference between the WPF TextBlock element and Label control?
Labels usually support single line text output while the TextBlock is intended for multiline text display.
For example in wpf TextBlock has a property TextWrapping which enables multiline input; Label does not have this.
Label is ContentControl which means that you can set anything as a content for it. Absolutely anything including strings, numbers, dates, other controls, images, shapes, etc. TextBlock can handle only strings.
Although TextBlock and Label are both used to display text, they are quite different under the covers.
=> Label inherits from ContentControl, a base class that
enables the display of almost any UI imaginable.
=> TextBlock, on the other hand, inherits directly from FrameworkElement, thus missing out on the behavior that is common to all elements inheriting from Control.
The shallow inheritance hierarchy of TextBlock makes the control lighter weight than Label and better suited for simpler, noninteractive scenarios.
PS: However, if you want access keys to work or want a more flexible or graphical design, you’ll need to use Label.
Probably the most annoying feature of TextBlock is the implicit style lookup behavior, which is scoped to only to the closest DataTemplate. This is a default behavior for non Control xaml elements.
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red"/>
</Style>
</StackPanel.Resources>
<ContentControl Content="Test">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
<ContentControl Content="Test">
<ContentControl.ContentTemplate>
<DataTemplate>
<Label Content="{Binding}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</StackPanel>
Yields a result of:
You can read more about it here.

Puzzle - Dynamically change data template control from another data template

I have a DataTemplate that contains an Expander with a border in the header. I want the header border to have round corners when collapsed and straight bottom corners when expanded. What would best practice be for achieving this (bonus points for code samples as I am new to XAML)?
This is the template that holds the expander:
<DataTemplate x:Key="A">
<StackPanel>
<Expander Name="ProjectExpander" Header="{Binding .}" HeaderTemplate="{StaticResource B}" >
<StackPanel>
<Border CornerRadius="0,0,2,2">
This is the expander datatemplate:
<DataTemplate x:Key="B">
<Border x:Name="ProjectExpanderHeader"
CornerRadius="{Binding local:ItemUserControl.ProjectHeaderBorderRadius, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}"
Background="{StaticResource ItemGradient}"
HorizontalAlignment="{Binding HorizontalAlignment,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}},
Mode=OneWayToSource}">
<local:ItemContentsUserControl Height="30"/>
</Border>
</DataTemplate>
Bind the CornerRadius property to the Expander.IsExpanded property and attach an IValueConverter that returns rounded corners when false and straight bottom corners when true. It's not the most elegant, but it will get the job done.
The other way to do this, if using MVVM, would be to expose a boolean property and bind it to the Expander.IsExpanded property. Then expose another property for the CornerRadius, which checks the boolean property and returns the appropriate values. This is definitely the "best practice" way to go about this.
Another way to do this is by editing the control template. The argument can be made that this is the best practice, though I'm not sure I'm ready to commit to that.
It's straightforward to do this if you have Expression Blend. An advantage of editing the control template is that it separates the behavior of the Expander from your data template, so that you can reuse it across different types of data. A disadvantage is that you end up embedding the properties of the header's Border in the control template, so you can't really change them for any individual instance of the control. (Other disadvantages: you have to have Expression Blend, and it produces a big bolus of XAML that you have to put in your resource dictionary.)
In Expression Blend, create an empty page and put an Expander on it. Right-click on the Expander and pick "Edit Template/Edit a Copy...". Give it a name like "ExpanderRoundedCorners".
This will add about 200 lines of XAML to Page.Resources, but most of this is used to create the graphics on the expand button. Switch to XAML view and search for the ToggleButton named "HeaderSite". This is the expand button. Note that its Content property is set to {TemplateBinding Header}. We'll want to fix that.
Delete the Content property, and add a child element to the ToggleButton like this:
<ToggleButton.Content>
<Border x:Name="HeaderBorder" BorderBrush="Red" BorderThickness="2">
<ContentPresenter Content="{TemplateBinding Header}"/>
</Border>
</ToggleButton.Content>
Now find the trigger that makes ExpandSite visible when the ToggleButton is pressed. Add this setter to it:
<Setter TargetName="HeaderBorder" Property="CornerRadius" Value="4"/>
That's it. Now every time you create an Expander with the ExpanderRoundedCorners style, the header content will be enclosed in a Border whose corners are rounded when the Expander is expanded.
You'll probably want to jigger around with this a little more when you've got it working. At the least, you'll want to remove the Border from the header template in your style, since it's now part of the control template.

Resources