WPF TabControl apply TabItem style insead of overriding it (while using ItemSource) - wpf

I have a parent TabControl with a custom TabItem style:
<TabControl ItemsSource="{Binding TabViewModels}" SelectedIndex="0" Padding="0,0,0,0" BorderThickness="0,0,0,0" Panel.ZIndex="1">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}" >
<Border Name="Border" CornerRadius="6,6,0,0" Margin="0,0,2,2">
<ContentPresenter TextBlock.TextAlignment="Center" TextBlock.FontSize="14"
Height="40" Width="auto" Content="{Binding Path=TabName}" Margin="12,2,12,2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="#00B6FA" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabPanel">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</TabControl.Resources>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=. }"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Inside one of the Tabs of the parent TabControl I have a child TabControl.
The problem is that the child TabControl automaticly takes the custom TabItem style because I override it as you can see above. What I want is that the child TabControl takes the default windows style for the TabItems.
Is this possible to apply the custom Tab Item instead of override it and using an ItemSource?

You can specify the style for the nested TabControl via its Style property:
<TabControl Style="{DynamicResource ResourceKey={x:Type TabControl}}">
...
</TabControl>
The default style key is usually the type of the control, hence {x:Type TabControl}.
Update:
I assume that you're using so-called implicit style for tab items, which is defined in the top-level TabControl's resources dictionary. By implicit style I understand a style without a key and with TargetType specified. To apply a default style to TabItem controls within a particular nested TabControl, simply add an empty implicit style to its resources dictionary, like so:
<TabControl x:Name="OuterTabControl">
<TabControl.Resources>
<Style TargetType="TabItem">
<!-- your style definition -->
</Style>
</TabControl.Resources>
...
<TabControl x:Name="InnerTabControl">
<TabControl.Resources>
<Style TargetType="TabItem"/>
</TabControl.Resources>
</TabControl>
...
</TabControl>

To avoid un-styling nested tab controls, see AnjumSKhan's answer, which puts the outer control's style inside a trigger whose binding cleverly targets just the one control:
<TabControl.Resources>
<Style TargetType="TabItem">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TabControl, AncestorLevel=2}}" Value="{x:Null}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" Background="Red">
<ContentPresenter ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style> </TabControl.Resources>

Related

WPF - Using Control Template just on one element

My first steps in WPF and C# and i don't get it to work... :(
I have an application with two grids inside one window. I have to change the style of the first grid, so i started reading and reached using ControlTemplate.
My Grid now looks as i wanted it. But i only want that the first grid looks this way. the second one on the same page should have another style.
Is it possible to bind the controltemplate only on one grid. Maybe by name or soemthing identifying?
My ControlTemplate Code for now i have written in window.resources looks:
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,0,0"
Background="Black"
BorderBrush="Black"
BorderThickness="0,2,0,0"
CornerRadius="0,0,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="0,0,0,20"
RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#FF454E54" />
<Setter TargetName="Border" Property="Background" Value="#FF0A3651" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter TargetName="Border" Property="Background" Value="#FF454E54" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You could give the Style or ControlTemplate an x:Key ( <Style x:Key="key" TargetType="{x:Type TabItem}"> ) and apply it to the element you want by setting the element's Style or Template property like this:
<TabItem Style="{StaticResource key}">
A Style without an x:Key is implicit and will be applied to all elements whose type matches the specified TargetType of the Style.
Instead of writing your template under Window.Resources do it under your Grid.Resources
Try this. All this is doing is setting the style of the TabItem directly.
<TabItem> <!-- This is your TabItem control -->
<TabItem.Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- ControlTemplate here -->
</Controltemplate
</Setter.Value>
</Setter>
</Style>
</TabItem.Style>
</TabItem>

How to set header style in TabItem?

I've created in my Window.Resource a Style that hold the background of the selected TabItem. What I want achieve is set a custom height for the TabItem header, this is my Style:
<Window.Resources>
<ResourceDictionary>
<Style x:Key="CustomTabItem" TargetType="{x:Type TabItem}" BasedOn="{StaticResource MetroTabItem}">
<Setter Property="Width" Value="Auto"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Height" Value="Auto"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" Background="Transparent">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Left"
ContentSource="Header"
Margin="10,3">
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True" SourceName="Border">
<Setter TargetName="Border" Property="Background" Value="Gainsboro" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="#FFC5C5C5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
usually I add the style to my TabControl like so:
<TabItem Tag="Tab1" Style="{StaticResource CustomTabItem}">
I just want set the header height for all the TabControls that have the CustomTabItem style, any suggestion?
You are making a confusion between TabControl and TabItem.
A TabControl has many TabItems.
Each TabItem is actually a Tab.
So the solutions are :
1 Set Style to all TabItems of all TabControls
A very simple way to apply the style is to remove the x:Key attribute of the TabItem style, and the style will apply to all TabItems
<!-- No x:Key="CustomTabItem" below -->
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource MetroTabItem}">
2 Set Style for all tabs of a TabControl
<TabControl ItemContainerStyle="{DynamicResource TabItemStyle1}" >
<TabItem Header="Hello">Hello content</TabItem>
<TabItem Header="Goodye">Goodbye content</TabItem>
</TabControl>
3 Set Style per TabItem
<TabControl>
<TabItem Style="{StaticResource CustomTabItem}" Header="Hello">Hello content</TabItem>
<TabItem Header="Goodye">Goodbye content</TabItem>
</TabControl>
Regards

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>

In an ItemsControl, how can I change the opacity of all the items not selected?

I know the question sounds a little strange, but I'd like to change the opacity of all the items not selected in an ItemsControl. In other words, I'd like to make more "visible" the selected item, showing the others item more "obfuscated".
I have a custom control "MyCustomControl" derived from ItemsControl, where each item is an instance of a class "MyObject".
I made a style for my custom control where I set the ItemTemplate to be an Image with Source property bound to the property "LargeImage" of "MyObject".
Here comes the problem. When I select an item I'd like to set the opacity of the others element, but I haven't found a way!
Here's my (simplified) XAML code:
<Style TargetType="{x:Type MyCustomControl}" x:Key="MyStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ui:MyCustomControl}">
<Border Height="{TemplateBinding Height}" Width="Auto" Background="{TemplateBinding Background}">
<ItemsPresenter VerticalAlignment="Center" IsHitTestVisible="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Button>
<Image Source="{Binding Path=LargeImage}" Stretch="Uniform"/>
</Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I think a very simple solution would be adding a trigger to the data template that uses the "IsSelected" property, using either a Trigger or a DataTrigger,something like that:
<DataTemplate.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Opacity" Value="Yourvalue"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="Opacity" Value="Yourvalue"/>
</DataTrigger>
</DataTemplate.Triggers>
<ListBox>
<ListBox.ItemContainerStyle>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="Opacity" Value="{Binding IsSelected, Converter={StaticResource YourOpacityConverter}}"/>
</ListBox.ItemContainerStyle>
</ListBox>
The above shows how you could do this with a ListBox, just to avoid confusion with your own types. It assumes your data items (MyObject) have an IsSelected property and that you've put a converter resource in your visual tree somewhere.
You could forgo the converter and instead trigger a state change when IsSelected changes, but you get the idea.

Why does ItemsPresenter override my DataGrid's foreground style?

I have a DataGrid style in App.xaml:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Foreground" Value="{StaticResource DataGridItemTextBrush}" />
<Setter Property="VerticalGridLinesBrush" Value="{StaticResource GridBrush}" />
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource GridBrush}" />
<Setter Property="RowBackground" Value="Transparent" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="AlternatingRowBackground" Value="#77000000" />
</Style>
This works great for all of my datagrid's in my applications.
However, for one of my datagrids, I want to group my rows if a specific column shares the same values. So I use the following on that particular datagrid:
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" Padding="3"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
</Style>
</Expander.Resources>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name, StringFormat=Set: {0}}" Margin="5,0"/>
<TextBlock Text="{Binding Path=ItemCount, StringFormat=(\{0\} Games)}"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
Problem: Now this DataGrid displays everything correctly based on my DataGrid style, except it displays the text (foreground) as black instead of my style.
Solution: I can fix the problem (though I don't understand why this is necessary) by modifying my ItemsPresenter to either of the following:
<ItemsPresenter TextElement.Foreground="{StaticResource DataGridItemTextBrush}"/>
or
<ItemsPresenter TextBlock.Foreground="{StaticResource DataGridItemTextBrush}" />
Question: Can anyone explain why this happens and/or offer a better solution that will guarantee that my ItemsPresenter does not override any of my DataGrid styles?
Thank you!
Styling cascades down unless the child opts to override the imposed style. The ItemsPresenter in your case has default values; which you are not overriding within your DataGrid style. Either create an ItemsPresenter style within App.xaml to meet your needs or modify the value locally via an explicit or implicit style, or opt for your proposed solution.
Also keep in mind that you can use the BasedOn property to inherit the default styling; overriding only certain properties.
BasedOn="{StaticResource {x:Type DataGrid}}"

Resources