TabControl Styles - wpf

I am learning to use styles in wpf and I am creating a style for a Tab Control. I was wandering if someone can please tell how I can stop a style propagating down, for example I have a Tab control that where one of the tabitems holds another tabcontrol, of closable tabitems, (yes Nested TabControl O.o).
So in my first UserControl it holds the "Master" TabControl this UserControl also has a UserControl.Rescource that has a style for this TabControl. This style propogates down to the nested tabcontrol, how can I stop this from happening?
The other tab control is kept in a seperate usercontrol class.
Looks Something like this:
<UserControl.Resources>
<Style TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
...
<!-- The Style -->
...
</UserControl.Resources>
<TabControl SelectedIndex="{Binding Path=TabIndexFocus}">
<TabItem Header="Tab1" IsEnabled="{Binding Path=IsEnabled_WorkSpace}" >
<View:NestedTabControl/>
</TabItem>
<TabItem Header="Tab2">
<View:SomeOtherView />
</TabItem>
.....
</TabControl>
Thanks All :D

Make a copy of the entire default Style Template, then I would recommend putting it in a separate resource dictionary but either way you will give the style template a unique x:Key name so like;
<Style x:Key="NonDefaultTabControlStyle" Target="{x:Type TabControl}">
Then in your tab control itself call your specific Style template like;
<TabControl Style="{StaticResource NonDefaultTabControlStyle}" ....>
When you specify the uniquely named Style template it will use it, when you don't it will use the default. Hope this helps and best of luck!

Related

How to "inject" style in a DataTemplate

I am trying to separate the DataTemplates from the Styles in my code.
I use DataTemplate to define, e.g. that the data should be displayed as two buttons and I use styles to define, e.g. that the background of those buttons should be green.
What I am trying to achieve is having a DataTemplate defined in one file and using that in multiple UserControls where the style comes from the UserControls.
Let's say I have the following style in the Resources of a UserControl:
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Another UserControl might have something similar with different colors.
Then, I have a ContentControl in that UserControl that will have some view model and some DataTemplate:
<ContentControl Content="{Binding SelectedViewModel}"
ContentTemplate="{Binding SelectedDataTemplate}"/>
The DataTemplate can be something as simple as this:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One"/>
<Button Content="Two"/>
</StackPanel>
</DataTemplate>
I would like the two buttons to have the ButtonStyle from the UserControl.Resources without directly referencing it. (So the DataTemplate can come from a different file or being able to use the DataTemplate in a similar context with another UserControl's style).
I tried to change the TargetType of ButtonStyle to ContentControl, assign the style to the ContentControl and set Foreground="{TemplatedParent Foreground}" on the Buttons, but in this way both Foreground will change when any of them (i.e. the ContentControl itself) is hovered.
Is there any way of "inheriting" a style in the DataTemplate or "injecting" the style from the UserControl?
P.S. I understand if I move the style into a separate file and reference that in the DataTemplate file I can simply use it as StaticResource, but that will couple the DataTemplate to that specific style and I won't be able to re-use it with other styles.
try DynamicResource:
<DataTemplate x:Key="TwoButtonsTemplate">
<StackPanel>
<Button Content="One" Style="{DynamicResource ButtonStyle}"/>
<Button Content="Two" Style="{DynamicResource ButtonStyle}"/>
</StackPanel>
</DataTemplate>
when TwoButtonsTemplate template is instantiated in UserControl, which declares ButtonStyle resource, that resource will be found and applied to buttons.

XAML : Set Label Margin inside a DataTemplate

I have a DataTemplate directly inside a Resource Dictionary. Inside the template is a label. The margin property isn't being applied how I expected (it has no effect)
<DataTemplate x:Key="HeaderContainerStyle">
<Label Margin="10" Text="{Binding}"/>
</DataTemplate>
And I can't solve the issue with a border as it appears its illegal (?)
<DataTemplate x:Key="HeaderContainerStyle">
<Border Padding="10">
<Label Text="{Binding}"/>
</Border>
</DataTemplate>
I get an error saying cannot resolve symbol Border.
When I try to add it in a ViewCell, application throws an exception:
System.ArgumentException: Value was an invalid value for HeaderTemplate
Parameter name: value
In Xamarin.Forms there is no Border class. Instead you should use Frame class which I think is equivalent for Border in WPF.
Like #Nick said, if you want to use DataTemplates in Xamarin.Forms you need to add ViewCell in DataTemplate and then next next element inside that (for example Grid, StackLayout or Label).
If it comes to Padding, in Xamarin.Forms its only applicable for layout (e.g. Grid, StackLayout) classes. Margin can be specified for view (e.g. Label, Button) and layout classes.
Getting back to your code basing on your HeaderContainerStyle I think you are trying to create style for Label, right?
To do that in Xamarin.Forms you should add new create new Style in ResourceDictionary for specific TargetType.
Example Style for Label class:
<ResourceDictionary>
<Style x:Key="labelRedStyle" TargetType="Label">
<Setter Property="HorizontalOptions"Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="15" />
<Setter Property="TextColor" Value="Black" />
<Setter Property="Margin" Value="15,10" />
</Style>
<ResourceDicrionary>
And example usage:
<Grid>
<Label Style="myLabelStyle" />
</Grid>
Let me know if it helped! Waiting for more questions :)
I'm assuming this is Xamarin.Forms and not WPF but if your DataTemplate is used by a ListView it is missing a ViewCell.
<DataTemplate x:Key="HeaderContainerStyle">
<ViewCell>
<Label Margin="10" Text="{Binding}"/>
</ViewCell>
</DataTemplate>
However, if your DataTemplate is used in your own custom control that was created then the ViewCell may not be needed and another issue with that control may be the cause of why Margin is not working.

WPF ControlTemplate breaks style

The stuff that does work
I need to style controls of a certain type that are children of a StackPanel. I'm using:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">...</Style>
</StackPanel.Resources>
<TextBlock ...>
...
</StackPanel>
And this works fine! Each TextBlock looks to the resources of it's parent (the StackPanel) to find out how it should be styled. It doesn't matter how far down you nest the TextBlock down a StackPanel... if it doesn't find a style in its direct parent, it will look at its parent's parent and so on, until it finds something (in this case, the style that was defined in ).
The stuff that doesn't work
I ran into a problem when I nested a TextBlock inside a ContentControl, which had a Template (see code below). The ControlTemplate seems to disrupt the way a TextBlock retrieves its style from its parents, grandparents,...
The use of a ControlTemplate effectively seems to knock out cold the TextBlock's means of finding its rightful style (the one in StackPanel.Resources). When it encounters a ControlTemplate, it stops looking for its style in the resources up the tree, and instead defaults to the style in MergedDictionaries of the Application itself.
<StackPanel Orientation="Vertical" Background="LightGray">
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
</StackPanel.Resources>
<TextBlock Text="plain and simple in stackpanel, green" />
<ContentControl>
<TextBlock Text="inside ContentControl, still green" />
</ContentControl>
<ContentControl>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<StackPanel Orientation="Vertical">
<ContentPresenter />
<TextBlock Text="how come this one - placed in the template - is not green?" />
</StackPanel>
</ControlTemplate>
</ContentControl.Template>
<TextBlock Text="inside ContentControl with a template, this one is green as well" />
</ContentControl>
</StackPanel>
Is there a way - besides duplicating the Style in StackPanel.Resources to ControlTemplate.Resources - to make the TextBlock inside that ControlTemplate find the defined style?
Thanks...
WPF considers ControlTemplates to be a boundry, and will not apply implicit styles (styles without an x:Key) inside of templates.
But there is one exception to this rule: anything that inherits from Control will apply implicit styles.
So you could use a Label instead of a TextBlock, and it would apply the implicit style defined further up your XAML hierarchy, however since TextBlock inherits from FrameworkElement instead of Control, it won't apply the implicit style automatically and you have to add it manually.
My most common way to get around this is to add an implicit style in the ControlTemplate.Resources that is BasedOn the existing implicit TextBlock style
<ControlTemplate.Resources>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Type TextBlock}}" />
<ControlTemplate.Resources>
Other common ways of getting around this are:
Place the implicit style in <Application.Resources>. Styles placed here will apply to your entire application, regardless of template boundaries. Be careful with this though, as it will apply the style to TextBlocks inside of other controls as well, like Buttons or ComboBoxes
<Application.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
</Application.Resources>
Use a Label instead of a TextBlock since it's inherited from Control, so will apply implicit Styles defined outside the ControlTemplate
Give the base style an x:Key and use it as the base style for an implicit TextBlock styles inside the ControlTemplate. It's pretty much the same as the top solution, however it's used for base styles that have an x:Key attribute
<Style x:Key="BaseTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Green" />
</Style>
...
<ControlTemplate.Resources>
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource BaseTextBlockStyle}" />
<ControlTemplate.Resources>

Access property of element in controltemplate in XAML

I want to use templated ComboBoxItems which consist of an Image and a Label. If I assign the template to a ComboBoxItem, can I somehow set the Source-Property of the Image? The goal is to use the same template for different ComboBoxItems but with different pictures in each Item.
I also thought about binding the Image.Source-Property in the Template, but this fails because the "parent" ComboBoxItem has of course no Source-Property I could bind to.
The code illustrates my problem:
<Style x:Key="ComboBoxPictureItem" TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<StackPanel Orientation="Horizontal">
<Image x:Name="StatusImage" />
<Label x:Name="StatusLabel" Content="Green"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ComboBox>
<ComboBoxItem Style="{StaticResource ResourceKey=ComboBoxPictureItem}"
-> sth. like: StatusImage.Source="PathToMyImage.png"/>
</ComboBox>
Thank you!
You should use template bindings to expose internal properties, e.g. bind the Label's content to the ComboBoxItem's content:
<Label Content="{TemplateBinding Content}"/>
If you now set the Content outside it is transferred to the label, you can do the same for the image, you may run out of properties though so if you want to do things that way you could inherit from ComboBoxItem and create more properties.
Here i do not think you want to mess with control templates really, just use the ItemTemplate to specify how the items look.

Silverlight: Make all descendants of an element have a margin?

Is there a way in Silverlight 4 to dictate that all elements within a StackPanel must have a margin, instead of specifying margin="10,0" on each one?
I'm afraid it's not possible declaratively in XAML with the StackPanel directly. It's the conceptual philosophy in Silverlight/WPF that a panel should not modify properties of its children. So you could implement your own Panel that does so anyway, or you could use an ItemsControl like that:
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Margin="10,0" Content="{Binding Content}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
[...]
</ItemsControl>
An ItemsControl uses a StackPanel by default, you can use its ItemsPanel property to define another Panel as an ItemsPanelTemplate if you wish so.
The way I'd do it is by defining implicit styles in the StackPanel's resources, for each control type that will be used within the StackPanel. To save defining the value repeatedly for each control type, you can create a named base style that targets FrameworkElement and defines the style(s), from which the style for each control type can inherit. An example is below:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style x:Key="CommonStyle" TargetType="FrameworkElement">
<Setter Property="Margin" Value="10,0" />
</Style>
<Style TargetType="Button" BasedOn="{StaticResource CommonStyle}" />
<Style TargetType="TextBlock" BasedOn="{StaticResource CommonStyle}" />
<Style TargetType="CheckBox" BasedOn="{StaticResource CommonStyle}" />
</StackPanel.Resources>
<Button>Button</Button>
<TextBlock Text="Text" />
<CheckBox>Check Box</CheckBox>
</StackPanel>
Note how each control in the StackPanel will have the margin applied, without needing to define it on each control.
Hope this helps...
Chris Anderson
PS. Blatant self promotion - this is based upon the inheritance trick in my book Pro Business Applications with Silverlight 4 :).
Put your stackpanel within a Border element and set the Border Padding to "10 0"
You can also do this programmatically; your StackPanel has a Children collection. You could use this to iterate through them and set the margin.

Resources