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:Key
directive together with the StaticResource
or DynamicResource
markup extension.