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

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.

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-MVVM: Prevent the user select something else in the listbox

I've a small WPF application(.Net 3.5 :/ ).
In this application I've a listbox, which allows me to select an element to edit on the right part of the application.
If the right part is invalid, I need to prevent the user changing the selection of elements.
I've made a lot of search on this:
Some were telling to change the background/brush to make it look like the selection is not possible(but the selection is still possible)
Some other were telling me to update the IsFocusable property of sub elements:
<ListBox itemsSoutces={Binding Test}>
<ListBoxt.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False"/>
</Style>
</listBox.ItemContainerStyle>
</ListBox>
The problem with this solution is that this value has to comes from a value of my ViewModel, and I don't know(nor it's possible) to bind the Value of the Style Setter to a property of my ViewModel?
Is it possible? How?
I would simply disable the listbox if the right part is invalid by setting IsEnabled to false. If your ViewModel contains a corresponding property, you can bind to that:
<ListBox IsEnabled="{Binding IsValid}" ...> ... </ListBox>

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.

WPF - Combobox with a Prompt

I'm looking for a way to add a "SELECT An Item" to a combobox that does not have an item selected. This would be different than a selected item as a default.
I want the combobox to say "SELECT an Item" and be bound to one list for the possible selections and another model for the selected item.
I'd prefer a style that I can apply to multiple comboboxes and have a way to set the prompt. I've seen something similiar at http://marlongrech.wordpress.com/2008/03/09/input-prompt-support-in-wpf/, but the it does not work smoothly, requiring 2 clicks to get to the list.
Thanks!
You could use the adorner solution you linked to with a couple of changes, or you could do this with a style and converter.
Adorner solution
The adorner solution is more complex, but has a better interface and encapsulation. The changes you would need to make are straightforward but possibly difficult if you're not a WPF expert. They are:
Recognize ComboBox as another special case (like TextBox). Subscribe to its SelectedItemChanged, and update adorner visibility using SelectedItem==null.
Don't handle input events (HitTestVisible=False, Focusable=False, etc)
In this case, your ComboBox style will be very simple, just setting the attached property.
Style and converter
Doing it with a style and converter may be simpler for you. Here is the body of the converter:
object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value==null ? Visibility.Visible : Visibility.Hidden;
}
Your style will replace the default ComboBox style and contain a copy of the ControlTemplate from the theme, wrapped with something like this (or use an adorner):
<Style TargetType="{x:Class ComboBox}">
<Style.Setters>
<Setter Property="local:MyInputPromptClass.PromptText" Value="SELECT an item" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Class ComboBox}">
<Grid>
... existing XAML from theme ControlTemplate ...
<TextBlock
Text="{Binding local:MyInputPromptClass.PromptText, RelativeSource={RelativeSource TemplatedParent}}"
Visibility="{Binding SelectedItem, Converter={x:Static local:MyInputPromptClass.Converter}, RelativeSource={RelativeSource TemplatedParent}}"
HitTestVisible="False" Focusable="False"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
This solution is less satisfying than the other, since by copying the default ComboBox template from a theme you end up with an app that doesn't track the current Windows theme. It's possible to get around this using multiple ControlTemplates along with StaticResource and some tricky binding, but at that point I would recommend just using the adorner and attached property.

Resources