MouseBinding overrides EventBindings in style - wpf

I created a named style on an image, and in the style I set an EventBinding for the MouseDown event to a handler at the ResourceDictionary's code-behind, it worked good.
When I use the image as the following:
<Style TargetType="{x:Type Image}" x:Key="ImageStyle">
<EventSetter Event="MouseDown" Handler="Image_MouseDown"/>
</Style>
<!---->
<Image Style="{StaticResource ImageStyle}">
<Image.InputBindings>
<MouseBinding Command="Save" MouseAction="LeftClick"/>
</Image.InputBindings>
</Image>
It causes the styled MouseDown eventbinding not to work.

It's hard to say without the style code, but I suppose you're defining InputBindings property in that style, then the Image setting just override it. Whatever you set in the actual element XAML overrides whatever you define in that element's style.
If that's the case, there's no easy way to merge style properties with the actual properties.
HTH.

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.

Clarification about WPF Styles

In the below code, why might the first example fail to set the background to Blue, but, the second example work as one might expect they both would – that is, is sets the background to Blue? Interestingly, when the style is applied in the second example, even though the BorderThickness is not specified in the Style, the property value of "3" also gets picked up, presumably because the new Style does not set it at all.
Code 1:
<GroupBox Margin="4,12,4,4"
Grid.ColumnSpan="4"
Grid.Column="0"
Grid.Row="3"
Header="{x:Static res:UIResources.DepreciationText}"
BorderBrush="{DynamicResource MainControlBorderBrush}"
BorderThickness="3"
Background="Blue" />
Code 2:
<GroupBox Margin="4,12,4,4"
Grid.ColumnSpan="4"
Grid.Column="0"
Grid.Row="3"
Header="{x:Static res:UIResources.DepreciationText}"
BorderBrush="{DynamicResource MainControlBorderBrush}"
BorderThickness="3">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="Background"
Value="Blue" />
</Style>
</GroupBox.Style>
</GroupBox>
You could reason from the above observation that the following are true:
1) Some Style is getting applied to the GroupBox further up the tree – perhaps even to some base class of GroupBox, such as Control, since a search for a Style targeting GroupBox was not found.
2) A property set on a control instance will not override the same property set in a Style targeting the control.
3) There is not a way to augment an inherited Style, other than using the BasedOn property. Using the BaseOn property implies you must know the Key of the Style you would like to base it on, unless, if you want to use BasedOn with a Style applied to a Type, you could somehow specify that – perhaps using the Type name in BasedOn?
Can anyone confirm or correct the above assertions, and whether they correctly explain the observed result?
Are you using any sort of theme pack for your application that restyles controls? My guess would be that you have a style somewhere overriding the control template in such a way that the Background property is completely ignored. If the control template doesn't contain a {TemplateBinding Background}, the Background property does nothing.
You can definitely override properties by setting them explicitly, even if they are also set in a style.
By setting the style yourself without using a BasedOn, it implicitly uses the default control template for the GroupBox, rather than resolving to a style imported with your resources. If you wanted to use the imported resource style, you could do this:
<Style TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="Background" Value="Blue" />
</Style>
I suspect this would give you the same result as in the first case, as now you would be inheriting the offending control template that ignores your Background value.

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

How do I update a label that is in a ControlTemplate of a Toolbar in WPF?

I have a ControlTemplate that is made up of a ToolBarTray and a ToolBar. In my ToolBar, I have several buttons and then a label. I want to be able to update the label in my toolbar with something like "1 of 10"
My first thought is to programatically find the label and set it, but I'm reading that this should be done with Triggers. I am having a hard time understanding how to accomplish this. Any ideas?
<Style x:Key="DocViewerToolBarStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ToolBarTray... />
<ToolBar.../>
<Button../>
<Button..>
<Label x:Name="myStatusLabel" .. />
The purpose of a ControlTemplate is to define the look of a control. For your problem, I'm not sure if a control template is the right solution.
As Bryan also points out, you should bind the Content property of the Label to a property that is already present in your control. This should be done via TemplateBinding.
<Label x:Name="myStatusLabel" Content={TemplateBinding MyStatusLabelProperty} ../>
The property MyStatusLabelProperty then has to exist at your control class.
Usually, you would create your own UserControl that has a dependency property of the correct type (either object or string) that is named MyStatusLabelProperty.
I would set the label to the "Content" attribute of your control e.g.
<Label x:Name="myStatusLabel" Content="{TemplateBinding Content}"/>
Then you can set your label's text with your top level object's Content attribute.
I would create a view model which implements INotifyPropertyChanged interface and use DataTemplate to display it using something like this:
<DataTemplate DataType={x:Type viewmodel:MyToolBarViewModel}>
<Label Content={Binding CurrentPage} />
<Label Content={Binding TotalPages} ContentStringFormat="{}of {0}" />
</DataTemplate>
<ToolBar>
<ContentPresenter Content={Binding <PathtoViewModel>} />
</ToolBar>
With using bindings you don't have to explicitly update label's content. All you have to do is set property's value in view model and raise proper PropertyChanged event which causes the label to update its content.

Resources