Binding to Control which applied the Style/Template - wpf

I want to (re-)template a Control, for example a ComboBox.
XAML:
<ComboBox Style="{StaticResource MyComboBoxStyle}" ... >
<!-- ... -->
</ComboBox>
In the ControlTemplate I want to have a Button.
ResourceDictionary:
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!-- ... -->
<Button Command="{TemplateBinding Tag}" CommandParameter="{Binding ???}" />
<!-- ... -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But I want to set the CommandParameter to the Control which applied the Template (in this example the ComboBox).
How can I solve this?

Ok, i found the right solution.
I have set the Binding to Binding RelativeSource={RelativeSource TemplatedParent}
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!-- ... -->
<Button Command="{TemplateBinding Tag}" CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}" />
<!-- ... -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

WPF: How to use BasedOn Style

I am new to WPF and I created the following simple style example. But it doesn't work properly and button's content doesn't show although I can still click on it. Can anyone tell me why it is broken?
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Button.Click" Handler="ButtonHandler" />
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</StackPanel.Resources>
<Button Name="OkBtn">OK</Button>
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>
You are using the BasedOn property in the correct way. The problem is that your ContentPresenter is not binded to the control it renders (i.e. the button).
Just try to replace your ControlTemplate XAML with this one:
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
By using TemplateBinding you can bind the ContentPresenter to the Content property of your templated control.

WPF: apply style only to one tabcontrol containing other tabControl

In my wpf application, I have a tabControl (parent) that contains another tabcontrol (child).
I would like to apply a style to the tabItem of the parent tabControl without affecting the child one.
I tried with this:
<TabControl x:Name="Parent" TabStripPlacement="Left"
ItemsSource="{Binding Path=ParentTabItems, Mode=OneWay}" >
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<!-- template is defined here-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</TabControl.Resources>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter>
<ContentPresenter.Content>
<!--Here there is the child TabControl-->
</ContentPresenter.Content>
</ContentPresenter>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
But this results in applying the style also to the child tabControl TabItem.
How can I apply the style only to the parent tabItem leaving the child TabControl using the default style defined in the application?
You should be able to use the TabControl.ItemContainerStyle to set a named Style on the TabItems of the outer TabControl. Try this:
In Resources:
<Style x:Key="ItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<!-- template is defined here-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<TabControl x:Name="Parent" TabStripPlacement="Left"
ItemsSource="{Binding Path=ParentTabItems, Mode=OneWay}"
ItemContainerStyle="{StaticResource ItemStyle}">
<TabControl.Resources>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</TabControl.Resources>
</TabControl>

How can I style certain WPF listboxes to be radio buttons?

I want to style some of my listboxes' items to be radiobuttons. Here is the code I have but this style gets applied to every listboxitem.
<Window.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
How can I do something so I can designate a listbox to have radio buttons. I imagine the designation would go something like this:
<ListBox Name="ListBox1" Width="120" Visibility="Visible" Background="{x:Null}" BorderThickness="0" Style="{StaticResource radioListBox}">
I know part of the problem is that this only styles listboxitems but I am not sure how to style the listbox itself. I would of course prefer to add in the background and border properties.
Any help would be appreciated.
You need to give your Style a key:
<Window.Resources>
<Style x:Key="radioListBoxItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And then you would apply it to a ListBox like:
<ListBox ItemContainerStyle="{StaticResource radioListBoxItem}" />
If you want to create a style for a ListBox that contains the radio list box items, and some other properties, you could do that too:
<Style x:Key="radioListBox" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle" Setter="{StaticResource radioListBoxItem}" />
<Setter Property="Background" Value="Navy" />
</Style>
And you'd apply it:
<ListBox Style="{StaticResource radioListBox}" />

How to configure Contentpresenter in nested ControlTemplate?

I have the following code:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Button>
<Button.Template>
<ControlTemplate>
<DockPanel>
<Image Source="{...}"/>
<ContentPresenter .../>
</DockPanel>
</ControlTemplate>
</Button.Template>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
I need button inside template for binding possibility. The nested template defines the appearance.
The question: <ListBoxItem Content="Start"/> does not work with code above. I need something like root Contentpresenter that refers to Contentpresenter inside button template. How can I do this?
Thanks in advance!
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Button>
<Button.Template>
<ControlTemplate>
<DockPanel>
<Image Source="{...}"/>
<ContentPresenter Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=Content}"/>
</DockPanel>
</ControlTemplate>
</Button.Template>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>

WPF templating/styling issue

Given this piece of XAML
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Red" />
</Style>
</Border.Resources>
<ContentPresenter ContentSource="Header" />
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top">
...
...
I would like to know why the group box header is not displayed in red letters.
I've already tried styling the Label type with no success either.
(sorry about the overly generic post title... I wasn't able to think of something more meaninful)
This code solved the problem:
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="Label">
<Style.Setters>
<Setter Property="Foreground" Value="Red" />
</Style.Setters>
</Style>
</DataTemplate.Resources>
<Label Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top" Header="GroupBox header" DockPanel.Dock="Top">
...
...
However, I still don't know why the proposed code didn't worked.
It seems that the ContentPresenter doesn't use TextBlock to show the string you provide as header or explicitly sets its style, so the style you defined cannot be applied.
If you are certain that you will only use text as group box header, you can remove the ContentPresenter and use a TextBlock on your own.
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Border.Resources>
<TextBlock Text="{TemplateBinding Header}"></TextBlock>
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top"/>
</DockPanel>
try this:
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="Foreground" Value="Red" />
</Style>
</DockPanel.Resources>
You don't need a templet for this. But if you demand on using a Templete, you probably have to set the Groupbox.HeaderTemplet not the GroupBox.Templet.
Edit:
This is what i got so far, but i keep getting an XamlPraseException.
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="HeaderTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="{TemplateBinding GroupBox.Header}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources