What if I wanted to make all TextBlock elements in UserControl, or section of a UserControl, have FontWeight="Bold" and TextAlignment="Right"? Is there some style I can set for TextBlock elements within a certain scope so I don't have to repeat all those attributes?
Yes, create a style with no x:Key and it will be applied to all items of the specified TargetType within that scope
For example, to make all TextBlock have FontWeight="Bold" and TextAlignment="Right" within a specific UserControl only, you can use something like this:
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="TextAlignment" Value="Right" />
</Style>
</UserControl.Resources>
If you put this in your resources, all your textblocks become the same.
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="TextAlignment" Value="Right"/>
</Style>
Alternatively, you can also subclass from TextBlock (say, BoldTextBlock) and use it as the target type. This is so you can use regular textblocks in the same control as special textblocks
<Style TargetType="{x:Type BoldTextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="TextAlignment" Value="Right"/>
</Style>
Related
Is it possible to define a ResourceDictionary in a Style?
For example, suppose I wanted to have two different Styles for StackPanels and in one I want all the buttons to be blue and the other I want them to be red. Is this possible?
Something like
<Style x:Key="RedButtonsPanel" TargetType="{x:Type StackPanel}">
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="StackPanel.Resources">
<Setter.Value>
<ResourceDictionary>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
</Style>
</ResourceDictionary>
</Setter.Value>
</Setter>
</Style>
The above code fails with an error about the Property value of a Setter cannot be null (even though it's obviously not null).
I can do something like
<ResourceDictionary x:Key="RedButtons">
<Style TargetType="{x:Type Button}">
<Setter Property="Width" Value="100" />
<Setter Property="Background" Value="Red" />
</Style>
</ResourceDictionary>
<StackPanel Resources={StaticResource RedButtons} />
However I was wondering if there was a way to merge the ResourceDictionary into the style.
Try adding the Style(s) for each TargetType to the DockPanel Style.Resources.
I did something similar with a DockPanel Style. Wanted all Buttons or Separators added to the DockPanel to get styled in a consistent manner.
Here's a sample:
<Style x:Key="DockPanelToolBarStyle" TargetType="{x:Type DockPanel}">
<Style.Resources>
<Style TargetType="Button" BasedOn="{StaticResource ButtonToolBarStyle}" />
<Style TargetType="Separator" BasedOn="{StaticResource SeparatorToolBarStyle}" />
</Style.Resources>
<Setter Property="Height" Value="45"/>
<Setter Property="Background" Value="{StaticResource ToolBarBrush}"/>
</Style>
StackPanel.Resources is not a DependencyProperty and therefore I don't believe you will be able to set that property within the style.
I am experimenting with creating a custom button in WPF.
I have basic XAML for a Button, with two TextBlock controls inside the button. One will be an image rendered by FontAwesome, and one will be text.
<TextBlock Style="{DynamicResource mediumLabel}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="4">SETTINGS</TextBlock>
<Button Grid.Column="1" Grid.Row="5" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Style="{DynamicResource mainButton}" Template="{DynamicResource mainButtonTemplate}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Image" Grid.Row="1" Grid.Column="0">cogs</TextBlock>
<TextBlock x:Name="Label" Grid.Row="1" Grid.Column="1" Text="SETTINGS" />
</Grid>
</Button>
I have global styles defined in App.xaml.
I can target each of these three elements individually, with individual styles in my App.xaml.
What I would like to do, I guess just for organization and ease of future use, I want to have a style for the Button, with nested styles to target each of the two TextBlock controls. Each will be styled differently, so I cant target the TextBlock type. I want to reference them by name.
I have tried referencing the main control_name.childcontrol_name, as well as just the control name.
I can't seem to get enough info on how to do this when searching, as I might be searching for the wrong terminology...
My Attempt at the nested style, with two attempts for the nested styles targeting.
<Style x:Key="mainButton" TargetType="Button">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="DarkSlateGray"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="DodgerBlue"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="{x:Reference Image}">
<Setter Property="FontFamily" Value="/Genesis_desktop;component/tools/fontawesome-free-5.15.1-desktop/otfs/#Font Awesome 5 Free Solid"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="ForeGround" Value="orange"/>
</Style>
<Style TargetType="{x:Reference mainButton.Label}">
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</Style.Resources>
</Style>
You are thinking the wrong way. It's not the Style that searches its target. It's the target that searches its Style.
The XAML parser creates an instance of the markup object e.g. a <TextBlock> and then, after checking the local FrameworkElement.Style property, traverses the logical tree to find if there is somewhere a Style defined that targets this instantiated type or that has a key, that matches the requested resource key.
What you want would be also quite inefficient as the name inside a namescope must be unique and namescopes are always quite "small". Such a element name driven mapping would require an explicit style that only matches a single control. And it would require to know the name of this control in advance.
This means you, the author of this individual Style, have direct access to the named control.
You could therefore define the style directly in the local ResourceDictionary e.g. TextBlock.Resources.
This has the same effect: the Style now applies to a single TextBlock element, that you know by name.
If you need to declare the exclusive style on a different parent ResourceDictionary or as merge resource, you usually name the Style by applying the x:Key directive and then request this resource explicitly e.g. by using StaticResource markup extension.
Also x:Reference returns a name and not a Type. The TargetType property is of type Type. This property uses a TypeConverter, which allows to assign a string value which is then converted to Type.
What you want is not possible by default. Styles are pure type specific (when defined as implicit resource) or are mapped explicitly by their x:Key value (as additional constraint). On the other hand, what you want is already there in a different form and can be achieved by using local resources or the x:Keydirective together with the StaticResource or DynamicResource markup extension.
The issue with your style is that the nested styles are defined in its Resources. These can only be accessed in the scope of this style. Consequently, the button cannot, because it has a different scope.
I do not think that it is possible to nest styles this way, but you could alternatively create a DataTemplate for the Content of the button and nest the styles there. Assign a key to them and reference them from your TextBlocks. In your mainButton style, you can add a setter to set the ContentTemplate to this template. You could also set the ContentTemplate on your button explicitly (but this would mean you have to move the data template out of the style). I hope this is the kind of nesting or encapsulation that you wanted to achive.
<Style x:Key="mainButton" TargetType="Button">
<Style.Resources>
<DataTemplate x:Key="MyButtoDataTemplate">
<DataTemplate.Resources>
<Style x:Key="ImageStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="/Genesis_desktop;component/tools/fontawesome-free-5.15.1-desktop/otfs/#Font Awesome 5 Free Solid"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="orange"/>
</Style>
<Style x:Key="LabelStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="14"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Image" Grid.Column="0" Style="{StaticResource ImageStyle}">cogs</TextBlock>
<TextBlock x:Name="Label" Grid.Column="1" Style="{StaticResource LabelStyle}" Text="SETTINGS" />
</Grid>
</DataTemplate>
</Style.Resources>
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="DarkSlateGray"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="ContentTemplate" Value="{StaticResource MyButtoDataTemplate}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="DodgerBlue"/>
</Trigger>
</Style.Triggers>
</Style>
Your Button definition is simplified to this, as the style already defines everything:
<Button Grid.Column="1"
Grid.Row="5"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
Style="{DynamicResource mainButton}"
Template="{DynamicResource mainButtonTemplate}"/>
I have set all my TextBoxes and ComboBoxes to have a default Padding of "1,3". When a ComboxBox is editable, the padding looks identical to the TextBoxes. But when IsEditable is defaulted to false, the padding doesn't look the same.
In App.xaml I already have:
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="Padding" Value="1,3"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Padding" Value="1,3"/>
</Style>
</Application.Resources>
In App.xaml, how can I set all ComboBoxes with an IsEditable="False" property to have Padding="6,3,5,3"?
You can use a trigger to change the padding based on the property (in your case IsEditable) of the ComboBox. To do this, style your ComboBox like this:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Padding" Value="1,3"/>
<Style.Triggers>
<Trigger Property="IsEditable" Value="False">
<Setter Property="Padding" Value="6,3,5,3"/>
</Trigger>
</Style.Triggers>
</Style>
This is a great opportunity to use a keyed style on all applicable ComboBoxes:
<Style TargetType="ComboBox" x:Key="EditableComboBoxStyle">
<Setter Property="IsEditable" Value="False"/>
<Setter Property="Padding" Value="6,3,5,3"/>
</Style>
Add this as a keyed resource in your App.xamland apply it like so:
<ComboBox Style={StaticResource EditableComboBoxStyle}"/>
This will override your default style for any ComboBox to which you apply it.
I am trying to set the Shape.Stroke property for several shape types using a style in WPF.
<Style.Resources>
<Style TargetType="{x:Type Polyline}">
<Setter Property="Stroke" Value="White"/>
</Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Stroke" Value="White"/>
</Style>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="Stroke" Value="White"/>
</Style>
...
</Style.Resources>
It does not seem possible to just set the style for the base class Shape.
<Style.Resources>
<Style TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="White"/>
</Style>
</Style.Resources>
Is there no better way than the first option I listed?
When WPF searches for an implicit Style, it looks for a resource whose key matches the DefaultStyleKey of the element to be styled. The convention in WPF is that every control T overrides the DefaultStyleKey to be typeof(T). WPF will not attempt to fall back to the base type's style key if a match is not found.
Ellipse, for example, has an implied[1] default style key of typeof(Ellipse), so WPF will only attempt to resolve an implicit style with that key; a resource keyed on typeof(Shape) will not be applied.
If you want to use implicit styles, you will need to define an implicit Style per concrete type. However, those styles may inherit setters and triggers from a common base Style:
<Style x:Key="x" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
</Style>
<Style TargetType="Ellipse" BasedOn="{StaticResource x}" />
<Style TargetType="Path" BasedOn="{StaticResource x}" />
<Style TargetType="Polyline" BasedOn="{StaticResource x}" />
Note that while implicit styles for base types will not be applied automatically, they are still compatible, and they can be applied explicitly:
<Style x:Key="StrokedShape" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
</Style>
<!-- ... -->
<Ellipse Style="{StaticResource StrokedShape}" />
[1] Some WPF elements do not override DefaultStyleKey. Shape and its subclasses are among them. In such cases, WPF assumes the default convention.
I have a style element which has minor edits and mostly repeated. How do i make it more generic - so setter properties are set within based on value versus repeating the code twice
<ResourceDictionary>
<Style x:Key="TextBlockStyleEnvironment" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5,4,0,0" />
<Setter Property="FontSize" Value="8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style x:Key="TextBlockStyleLocation" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5,4,0,0" />
<Setter Property="FontSize" Value="10" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ResourceDictionary>
As you see from the code all setter properties are same except Margin and FontSize. Also attached is the screenshot of it on rendering.
Please note - want to keep this self contained within a Style and not have declare at local level in XAML when this being consumed.
Possible values of Env can be Dev,QA,Prod and possible values of location can be TK, LN
Consuming in XAML snippet as follows:
<Grid DataContext="{....}">
<StackPanel>
<TextBlock Text="{Binding Environment}" Style="{StaticResource TextBlockStyleEnvironment}"/>
<TextBlock Text="{Binding Location}" Style="{StaticResource TextBlockStyleLocation}"/>
You can use style inheritance:
<ResourceDictionary>
<Style x:Key="BaseTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5,4,0,0" />
<Setter Property="FontSize" Value="8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style x:Key="TextBlockStyleEnvironment" BasedOn="{StaticResource BaseTextBlockStyle}" TargetType="{x:Type TextBlock}" />
<Style x:Key="TextBlockStyleLocation" BasedOn="{StaticResource BaseTextBlockStyle}" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="10" />
</Style>
</ResourceDictionary>
Additionally you can create attached properties and bind to those from within your control templates. This gives you the flexibility of not having to create hundreds of styles just because something minute needs to be different.
A good example of that is a button that has an image. When the mouse is over the image, the image needs to change. Typically you'd have to create a new control template/style for each button that implements that behavior. However, if you create two attached properties - NormalStateImageSource and MosueOverImageSource, you can bind to those in your control template. This allows you to have a single full blown style for the button, and later to declare individual styles for other buttons that only change the values of these attached properties.
There are few techniques in WPF world.
First, if styles are same type, it's possible to use BasedOn attribute:
<Style x:Key="GenericTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5,4,0,0" />
<Setter Property="FontSize" Value="8" />
</Style>
<Style x:Key="TextBlockStyleEnvironment"
BasedOn="{StaticResource GenericTextBlockStyle}"
TargetType="{x:Type TextBlock}">
</Style>
However the BasedOn attribute can get really messy sometimes. It's also possible to do it this way, which will work for elements that are not the same type:
<Thickness x:Key="LeftBorderMargin" Top="10" />
<Style x:Key="TextBlockStyleEnvironment" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="{StaticResource LeftBorderMargin}" />
</Style>
It's common practice to refactor all the colors / margins out from the Style element, for reusability.