I have got a custom control composed of many parts.
One of those parts is a Border.
I need to style that border from outside the control so i created a dependency property of type Style and bound it to the Border like this:
<ControlTemplate TargetType="{x:Type cc:DrawingLayer}" >
...
<Grid x:Name="grid" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" >
<Border x:Name="PART_AreaSelector" Style="{Binding AreaSelectorStyle}" BorderBrush="#FF3399FF" BorderThickness="1" Background="#55ADD8E6" />
</Grid>
</ControlTemplate>
In the window where i use the control i try to define its style this way:
<cc:DrawingLayer.AreaSelectorStyle>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="10" />
<Setter Property="Background" Value="Red"/>
</Style>
</cc:DrawingLayer.AreaSelectorStyle>
But it seems that the style is not applied. No property reflects the values in the style.
Can someone point out what i'm missing?
The "local" property values assigned in
<Border ... BorderBrush="#FF3399FF" BorderThickness="1" Background="#55ADD8E6" />
have higher precendence than the values set by Style Setters.
You need to set a default Style for those values.
See Dependency Property Value Precedence on MSDN.
Related
Style defined in Resource
<Setter Property="Background" Value="Red"/>
</Style>enter code here
</Window.Resources>
In window i am adding a groupbox with child label .
<Grid>
<GroupBox Header="Header">
<GroupBox.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Background" Value="white"/>
</Style>
</GroupBox.Resources>
<Label Content="dsfdsfdsf" Foreground="Black" />
</GroupBox>
</Grid>
My Expected Result was Label taking background of white . But actually it is taking Red Background (that is defined in style of Resource )
If i set the style of the Label to explicitly null it works fine
Label Content="dsfdsfdsf" Foreground="Black" Style={x:Null}
But Controls to GroupBox are dynamically added so i want to set
Style={x:Null} to all childrens that are being added to Group box
if i set OverrideDefalutStyle to True in Label the content of label is not comming ......................
Label Content="dsfdsfdsf" Foreground="Black" OverridesDefaultStyle="True"
That style in GroupBox.Resources has no effect on the GroupBox itself. The implicit GroupBox's style is the one of its closest ancestor on VisualTree. You put that style in the wrong place.
Or use Style property instead
<Grid>
<GroupBox Header="Header">
<GroupBox.Style>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Background" Value="white"/>
</Style>
</GroupBox.Style>
<Label Content="dsfdsfdsf" Foreground="Black" />
</GroupBox>
</Grid>
By setting
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Background" Value="White"/>
</Style>
you will set the background of all GroupBox controls within your GroupBox and the GroupBox itself to white.
So if you want to set/override the Background of all Labels within your GroupBox just add an additional Style to your GroupBox targeting Label
<Style TargetType="{x:Type Label}">
<Setter Property="Background" Value="White"/>
</Style>
If you want to reset the style property of your Label just add an empty style definition to your GroupBox
<Style TargetType="{x:Type Label}"/>
The next approach is used on your on risk :)
If you only want to reset the background color, you can do this trick/hack to reset:
<Style TargetType="{x:Type Label}">
<Setter Property="Background" Value="{Binding Background.DefaultValue, RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="Black" />
</Style>
Hint: Instead of Background.DefaultValue you can also write Background.ABC the main thing here is that the binding goes wrong.
I have a style for a stack panel which has a key, within the stack panel I have buttons which have a default style set via the stack panel's Style > Resources > Styles, for this reason the buttons style is not set via a key but instead is set as it is a child of the stack panel! I'm sure this is a little tricky to understand as it sure feels tricky to explain... here is the style code...
<Style x:Key="VerticalMenuPanel" TargetType="{x:Type StackPanel}">
<Setter Property="Background" Value="#115e9a" />
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="13" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Height" Value="27" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="White" BorderThickness="0,0,0,1">
<Border BorderBrush="#0160a2" BorderThickness="0,0,0,1.5">
<ContentPresenter VerticalAlignment="Center" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
So, basically, without getting into what I'm doing too much I require the need to change the style of the buttons within the stack panel based on certain events. In order to change the style I simply find the resource based on the style key! HOWEVER here is my problem, I can't reset the style back to the "default" style provided by the parent the "VerticalMenuPanel"s Style.Resources as I don't know how to retrieve the style without having a definitive key for it. The obvious thing to do would be to give the button style a key but then I would have to explicitly define the style for all of the buttons instead of the style being applied by default as it is a child of the stack panel!
The bottom line is how do I retrieve a style without a key from within a parent styles resources (programatically)? Obviously I can retrieve the parent style via its key.
I hope you understand the issue I am having, and please feel free to let me know if I can explain anything better, add more clarity or if you wish edit the post yourself :)
When the Key is not provided for a style, the TargetType becomes the key of that style.
Here is an example:
<Grid x:Name="layoutRoot">
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="100" />
</Style>
</Grid.Resources>
<Button />
</Grid>
then in the code-behind you can retrieve the default style using the Button type as the resource Key:
Style buttonStyle = layoutRoot.FindResource(typeof(Button)) as Style;
I've written a user control with popup, who's content is being set outside the control. The ControlTemplate of that control looks like the following:
<ControlTemplate TargetType="local:InfoIcon">
<Grid>
<ToggleButton x:Name="HelpButton" Style="{StaticResource HelpButton}" />
<Popup PlacementTarget="{Binding ElementName=HelpButton}" Placement="Bottom"
IsOpen="{Binding ElementName=HelpButton, Path=IsChecked, Mode=TwoWay}" StaysOpen="False">
<Border BorderBrush="#767676" BorderThickness="1"
Background="#f1f2f7">
<Border.Resources>
<!-- Important -->
<Style TargetType="Label">
<Setter Property="Foreground" Value="#575757" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#575757" />
</Style>
<!-- /Important -->
</Border.Resources>
<ContentPresenter Content="{TemplateBinding HelpContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
The Important part - I want to assign custom styles to items, which are being put inside the popup (it serves as a clickable hint)
I'm using my control in the following way:
<local:MyControl>
<local:MyControl.HelpContent>
<TextBlock>Ala ma kota</TextBlock>
</local:MyControl.HelpContent>
</local:MyControl>
But despite styles in the Border, TextBlock's text's color always inherit the value from its parent (checked using Snoop) - resulting in white text on white background.
You can downlad the small PoC application, which demonstrates the problem.
My observations:
The styling does work for Label. It only doesn't work for TextBlock.
When I add TextBlock.Foreground="Red" to the Border, TextBlock becomes red, still ignoring style (but now using color from Border).
Snoop informs, that this TextBlock actually has the Style resolved correctly. But despite it shouldn't, it uses the inherited value instead of one specified in the style.
How can I solve this problem and why does it occur?
I received answer on Microsoft forums; I'll leave it here in case someone encounters the same problem.
The difference is that a TextBlock is not a control, i.e. it doesn't have any ControlTemplate and because of this the implicit style doesn't get applied to it when it is located inside the StackPanel. Please see the following page for more information: http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx
You could use Label elements or set the style for the TextBlock elements explicitly.
-- Magnus (MM8)
Edit2
I've set the Foreground of the UserControl to something else. This behavior is because the child TextBlock controls of the UserControl inherit the Foreground-Settings somehow. This has nothing to do with the popup or some other approaches we tried yet.
I've stumbled upon another question with a similar problems here: Cannot override controls foreground colour in wpf
I suggest to accept this strange behavior and just set a Foreground Color of the UserControl instead:
<Style TargetType="{x:Type local:InfoIcon}">
<Setter Property="Foreground" Value="Red"/>
</Style>
previous Edit
You had my curiousity with this weird behavior, but after looking at your PoC it was rather obvious :) The Popup has some attached Properties TextElement.* where you can style the text elements in the popup. This was new to me, too and I will reseach a bit more afterwards. Nevertheless: Workaround for your Problem is to not style the TextBlock but the Popup instead. your code could look something like following :
<ControlTemplate TargetType="{x:Type local:InfoIcon}">
<ControlTemplate.Resources>
<Style TargetType="Popup">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ToggleButton x:Name="TB" Width="16" Height="16"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=TB}" IsOpen="{Binding ElementName=TB, Path=IsChecked}" StaysOpen="False">
<ContentPresenter Content="{TemplateBinding InfoContent}"/>
</Popup>
</Grid>
</ControlTemplate>
I changed the styles to be outside of the controls, of course you can just use the attached properties of the popup directly. But initially you wanted to know how it works with the styles attached at the border, it does not matter now where you add the styles. You can use a ResourceDictionary for example.
As a suggestion, shouldn't this:
TargetType="local:InfoIcon"
be like this?
TargetType="{x:Type local:InfoIcon}"
Maybe you have some TextBlock style defining that it shouldd take the parent's control foreground.
Did you try to add a BasedOn property like this ?
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="#575757" />
</Style>
I tried with your code example and this works :
<ContentPresenter Content="{TemplateBinding InfoContent}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Style>
</ContentPresenter.Style>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Red" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
That's kind of odd because when I put the Foreground setter for the Label control inside the ContentPresenter.Style then this time it's Label wich doesn't work...I think it's because Label is a considered as a ContentControl whereas TextBlock is just a FrameworkElement.
Had a similar issue caused by another problem:
There is a strange bug in WPF that prevents styles, defined in merged dictionaries, from being applied to the first element:
https://www.engineeringsolutions.de/wpf-fix-style-is-only-applied-to-first-element/
Problem
I've created a custom control (OmniBox), which has its base style set with:
<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
<Setter Property="Margin" Value="0,2" />
</Style>
But when I'm using my control, I want to be able to do something like:
<UserControl.Resources>
<Style TargetType="{x:Type ui:OmniBox}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,10"/> <!--Not Working?-->
</Style>
</UserControl.Resources>
<Grid>
<StackPanel>
<ui:OmniBox x:Name="One"... />
<ui:OmniBox x:Name="Two"... />
...
And have all instances of my control take on that default margin. Unfortunately, my controls are not responding to the style set in the resources. They are just keeping their default margin of "0,2".
Strangely, if I explicitly set the margin on my controls like so:
<ui:OmniBox x:Name="One" Margin="0,10" Style="OBDefaultStyle" ... />
<ui:OmniBox x:Name="Two" Margin="0,10" ... />
...
They DO use the margin of "0,10" rather than "0,2". How come the template type isn't working?
If it's relevant, my OmniBox control templates all look like this:
<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
<Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
<ControlTemplate TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultTemplate">
<Grid x:Name="PART_Grid" Style="{StaticResource GridStyle}">
... (Content)
</Grid>
</ControlTemplate>
First Attempt
In my grid style, I've tried setting Margin to
<Setter Property="Margin"
Value="{Binding Path=Margin, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:OmniBox}}}" />
But it didn't help in sucking down the templated margin.
Second Attempt
I tried creating a custom margin dependency property and binding the grid to that:
<Style x:Key="GridStyle" TargetType="Grid" BasedOn="{StaticResource BaseElement}">
<Setter Property="Margin" Value="{Binding Path=MyMargin, RelativeSource={RelativeSource TemplatedParent}}" />
</Style>
My custom property was defined as:
public static readonly DependencyProperty MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2), new PropertyChangedCallback(OnMarginChanged)));
Anyways it didn't work. The default margin set in the dependency property above is still overriding the margin I'm trying to set in the style template.
You can add a default style for a custom control by overriding the metadata for the DefaultStyleKey:
public class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
You then create a resource dictionary called Generic.xaml that is located in a directory called Themes in the root of the project (so the path will be "/Themes/Generic.xaml"). In that resource dictionary you create a default style for your control:
<!-- Base the style on the default style of the base class, if you don't want to completely
replace that style. If you do, remember to specify a new control template in your style as well -->
<Style TargetType="SomeNamespace:MyButton" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Margin" Value="10" />
</Style>
If you just add a MyButton control it will get the default style, but you can override properties set in the default style by applying a new style:
<Window x:Class="SomeNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:SomeNamespace="clr-namespace:SomeNamespace"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="SomeNamespace:MyButton">
<Setter Property="Margin" Value="20" />
</Style>
</Window.Resources>
<Grid>
<SomeNamespace:MyButton />
</Grid>
</Window>
GridStyle specifies TargetType="Grid", so the setter <Setter Property="Margin" Value="0,2" /> applies to the Grid at the root of the control template. Setting the Margin property of the containing OmniBox has no effect of the margin of that grid.
Try specifying this in the template:
<Grid x:Name="PART_Grid" Margin="{TemplateBinding Margin}">
Notice I did not set the Style property as you did in the template. This is because the grid's Margin property will always reflect the Margin property of the OmniBox containing it, negating the effect of the Margin property in GridStyle. Instead you will want to default the OmniBox.Margin property and remove GridStyle entirely:
<Style TargetType="{x:Type local:OmniBox}" x:Key="OBDefaultStyle">
<Setter Property="Margin" Value="0 2" />
<Setter Property="Template" Value="{StaticResource OBDefaultTemplate}" />
</Style>
Have you overridden the DefaultStyleKey property in your OmniBox control?
After happening on this question, I figured out what I needed to do. In the control's class, I need to override the margin property's default value:
static OmniBox()
{
MarginProperty.OverrideMetadata(typeof(OmniBox), new FrameworkPropertyMetadata(new Thickness(0,2,0,2)));
}
After that, I get rid of the margin on the "Grid" component of the omnibox completely, since the control itself carries a margin. Now when the user sets the "Margin" property on the OmniBox, it accepts it, if they don't, it uses the default value.
Thank you all so much for your suggestions and effort.
Let's say I have a UserControl with 4 Borders:
<Border />
<Border />
<Border />
<Border />
Now in my Resources I can go:
<Style TargetType="{x:Type Border}">
... change some properties here
</Style>
Now this is all good, but it will target all borders in my UserControl.
But what if I just want to target a subset of them?
I'd like to go:
<Border Class="Type1" />
<Border Class="Type1" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" TargetClass="Type1">
... change some properties here
</Style>
But this obviously doesn't exist, is there some other way I can achieve what I'm after?
Thanks
Though the syntax isn't quite as clean as in CSS, it is a lot more specific.
To build on your example, what you're looking for is:
<Border Style="{StaticResource Type1}" />
<Border Style="{StaticResource Type1}" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" x:Key="Type1">
... change some properties here
</Style>
Remember that WPF styles don't actually cascade like CSS does.
A more detailed styling reference:
https://web.archive.org/web/20141210000517/http://dotnetslackers.com/articles/wpf/StylesResourcesAndControlTemplatesInWPF.aspx
Something that I find most people are not aware of is WPF's ability to nest Styles within Style.Resources. For example:
<!-- Define a new style for Borders called InfoBox, that will have a red background,
and further override all buttons within it to have Yellow Text. An extra style,
"Strawberry" is also defined, that lets specific buttons be selected to be styled
as Green FG on DarkRed BG -->
<Style TargetType="{x:Type Border}" x:Key="InfoBox">
<Setter Property="Background" Value="Red"/>
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="DarkYellow"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="Strawberry">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Background" Value="DarkRed"/>
</Style>
</Style.Resources>
</Style>
...
<Border Style="{DynamicResource InfoBox}">
<StackPanel>
<Button Content="I am a banana!"/>
<Button Style="{DynamicResource Strawberry}" Content="I am red!"/>
</StackPanel>
</Border>
While not exactly the same as CSS (There isn't much support for standard pseudo-selectors), this gives you a huge amount of power and flexibility. Couple this with skillful use of ItemsControls and you can do some great things.
you can set the style directly on the <Border> using an x:key and the StaticResource (or DynamicResource) property of the Border. if you would like to change the style at runtime, then you should lean towards using the DynamicResource over the StaticResource.
<Style x:Key="something" TargetType="{x:Type Border}">
</Style>
<Border style="{StaticResource something}"/>
<Style x:Key="styleKey" TargetType="{x:Type Border}">
... change some properties here
</Style>
and
<Border Style="{StaticResource styleKey}"