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

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.

Related

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

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

Why does my MenuItem have an Icon when I have overridden the DataTemplate?

I have successfully implement a WPF menu where the top-level items are drawn as large buttons and the lower level items are drawn as standard menu items (see my previous questions here and here).
In my original attempt at this my lower-level item template (SubItemTemplate in the example below) contained an image and a textblock. The result was something that looked like a normal menu item with an empty Icon area and the image next to the text in the text part of the menu item. I was not expecting to see the icon area in the visual display since I thought that the entire visual display would be determined by the contents of my template. The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
When I removed my image from teh lower-level template and replaced it with a style-setter for the Icon property, I got the display that I wanted.
I do not understand how and why the Icon property exists on my lower-level item DataTemplate.
Here's my code. The property HasParent is used to distinguish menu items that are not top-level (that is, the ones that are drawn with the SubItemTemplate). The section I don't understand is the DataTrigger.
Why is there an Icon property available inside that trigger?
<UserControl.Resources>
<Image x:Key="MenuIconResource16" Height="16" Width="16" Source="{Binding Icon32}" x:Shared="False" />
<HierarchicalDataTemplate x:Key="TopLevelItemTemplate" ItemsSource="{Binding Children}">
<StackPanel VerticalAlignment="Bottom" Orientation="Vertical">
<Image Width="32" Height="32" VerticalAlignment="Center" Source="{Binding Icon32}" ToolTip="{Binding UserHint}" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="SubItemTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<WrapPanel Height="Auto">
<Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}" ItemTemplateSelector="{StaticResource MenuItemTemplateSelector}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding EventType}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasParent}" Value="true">
<Setter Property="Icon" Value="{StaticResource MenuIconResource16}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
</Menu>
</WrapPanel>
I thought that the entire visual display would be determined by the contents of my template.
#dkozl noted the difference between DataTemplate and Template -- that is the important distinction. A data template is a XAML fragment that the owning control uses as part of the overall control, which may or may not include other (customizable or hard-coded) visual elements, and/or other data templates. The control template is where this visual structure of the control is defined. If you set/override a control template, then your expectation of not seeing any other visual content, will hold true.
The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
The other thing to note here is that the default style for Menu defines multiple control templates for its MenuItems. These templates are applied depending on the role "TopLevelHeader", "TopLevelItem", "SubmenuHeader", and "SubmenuItem". So you will see different behavior for these different menu items. Take a look at the default styles/templates, which should be illuminating (although they are kind of complex).
Why is there an Icon property available inside that trigger?
A style trigger has the capability of modifying any dependency property of the control it is applied to. Since the style trigger in question is being applied to the MenuItem control, it can modify the Icon dependency property, which is owned by that control.

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.

How do I apply a Style Setter for ListBoxItems of a certain DataType?

My WPF ListBox contains two types of object: Product and Brand.
I want my Products selectable. I want my Brands not selectable.
Each of the two types has its own DataTemplate.
By default, anything may be selected:
<ListBox ... >
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Product}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:Brand}">
...
</DataTemplate>
</ListBox.Resources>
</ListBox>
I can set Focusable with a Setter, but then nothing may be selected:
<ListBox ... >
<ListBox.Resources>
...
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.Resources>
</ListBox>
I cannot put the Setter within the DataTemplate.
I cannot put a DataType onto the Style.
How do I style only the ListBoxItems of type Brand?
Thanks to the StyleSelector class you can attach styles depending on type of data for the ItemContainerStyle. There is a really good example here : http://www.telerik.com/help/wpf/common-data-binding-style-selectors.html
Can you use a data trigger on your ListBoxItem style? If so, bind to the DataContext (your class) and use a value converter to test the type. If it's the one you're interested in, style the ListBoxItem so that it cannot appear selected.
I don't think you can disallow selection of an item in a Selector (parent of ListBox) without codebehind or a custom Behavior.

How do I apply a DataTemplate in a Dynamic Grid?

I have a Grid. The grid's columns are auto-generated at run-time based on the user's selection.
I need the cells in the grid to be red if the content is a negative number.
I have created a DataTemplateSelector. The DataTemplateSelector get's correctly called and returns my template if the cell is negative.
Since my columns are auto-generated, I cannot simply put the correct field in the binding in my template.
<DataTemplate x:Key="MontantNegatifTemplate">
<TextBlock Foreground="Red" Text="{Binding}" />
</DataTemplate>
If I do a Template like this the text is the name of the object the grid is bound on.
If I do something like:
<DataTemplate x:Key="MontantNegatifTemplate">
<TextBlock Foreground="Red" />
</DataTemplate>
The cell is empty since the Textblock seems to overwrite the standard auto-generated cell.
Is there a way to make this work? Should I use another approach?
I finally found the awnser to my question.
I needed to use a StyleSelector rather than a DataTemplateSelector.
In the same way I needed to define a Style instead of a DataTemplate in my Grid resources.
<style:NegativeStyleSelector x:Key="NegativeStyleSelector">
<style:NegativeStyleSelector.NegativeStyle>
<Style TargetType="GridViewCell">
<Setter Property="Foreground" Value="Red"/>
</Style>
</style:NegativeStyleSelector.NegativeStyle>
</style:NegativeStyleSelector>

Resources