WPF styling deep nested elements - wpf

I can't override a default style attribute of a third-party component that I use in a clean way. The visual tree looks something like this:
A
--B
...
------------Z
--------------TextBlock
I try to override the style of TextBlock like this:
<Style
TargetType="A">
<Style.Resources>
<Style
TargetType="TextBlock">
<Setter
Property="TextWrapping"
Value="Wrap" />
</Style>
</Style.Resources>
</Style>
But this doesn't work. In live visual tree, I confirm that component A sees my custom style but TextBlock doesn't see it. However, when I try:
<Style
TargetType="Z">
<Style.Resources>
<Style
TargetType="TextBlock">
<Setter
Property="TextWrapping"
Value="Wrap" />
</Style>
</Style.Resources>
</Style>
It works though. Therefore, it seems to me that WPF forces me to add all of the child components one by one as Style.Resources to be able to edit the innermost child. But I don't want to define all of the resources between component A to Z just to add one simple style. What are my options?

I'm not sure if I fully understand your question, as you've provided a working answer, and I can't see why would WPF force you to 'define all of the resources between component A to Z'.
However, here is a tip:
You can create a custom control that inherits Z and overrides OnApplyTemplate(). When OnApplyTemplate() is called you're guaranteed that the template was applied, so you can find any child control (your textbox) you want. (Just google for e.g 'wpf find child control by name' if you don't know how.) Once you have your textbox, you can change its' wrapping from code.
I had to customize a Ribbon control once, and that approach greatly simplified the task.

Related

Custom control not inheriting parent's styles

I'm trying to maintain a uniform look and feel across elements in my WPF application, and at the same time I want to create a modified TextBox. However, when I do this, styles that I define at the application level for TextBox aren't being applied to the class I created, even though the style created for my custom control is using the BasedOn property.
Is there something I'm missing that's causing this to behave differently than I expect?
I reproduced the issue in a brand-new WPF project in VS2010 with this setup:
C# Code:
public class CustomTextBox : TextBox
{
static CustomTextBox() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(typeof(CustomTextBox)));
}
}
XAML in Themes\Generic.xaml:
<Style TargetType="{x:Type local:CustomTextBox}" BasedOn="{StaticResource {x:Type TextBox}}"/>
XAML in App.xaml:
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red"/>
</Style>
</Application.Resources>
However, in the designer and when I run the app, the CustomTextBox falls back onthe default styling for the text box instead of having a red background, even though the documentation for the BasedOn property suggests that my derived class should have this styling...
There are several ways that styles in WPF can be extended or inherited. Styles can be based on other styles through this property. When you use this property, the new style will inherit the values of the original style that are not explicitly redefined in the new style.
...
Note: If you create a style with a TargetType property and base it on another style that also defines a TargetType property, the target type of the derived style must be the same as or be derived from the type of the base style.
Short Answer: Your style is based on a StaticResource
<Style TargetType="{x:Type local:CustomTextBox}" BasedOn="{StaticResource {x:Type TextBox}}"/>
When you did this, you are not changing the StaticResource
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red"/>
</Style>
So CustomTextBox is not supposed to inherit the red background.

Can an application level style reference a UserControl resource?

In my WPF application I created a resource of type SolidColorBrush and a button style which uses this resource as a background.
In my UserControl I override the color resource with the same name but a different color.
Can I create it in a way that a button in that UserControl will use the application Style but with the color defined in the UserControl?
What you are trying to do here is a little bit against the idea of resources in WPF as you try to mix up the top-down nature of resource definitions. So even if you get it to work, it will lead to untypical behavior, making it hard for the next person working on the code (or the future-you) to understand what's going on.
I would suggest to do the following to get exactly the behavior you want, still keeping things in order:
Suppose your app level resource like this:
<SolidColorBrush x:Key="MyColor" Color="Blue" />
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource MyColor}" />
... (other setters)
</Style>
If you want to override the Foreground with a local brush, do this explicitly when using the TextBlock (might be your UserControl as well) and use BasedOn to inherit everything else from the app level style:
<SolidColorBrush x:Key="MyOverridingColor" Color="Red" />
<Style x:Key="OverridingTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="Foreground" Value="{StaticResource MyOverridingColor}" />
</Style>
I would suggest to use a different name for the second color (MyOverridingColor in the example above) to make clear that it is a different color.

WPF: Why are nested styles not always working?

I'm trying to apply a nested WPF style to a Toolbar. I'd like to have all children of the Toolbar (MenuItems, Buttons, ToggleButtons etc.) to have the specified style.
The problem is, that the nested style definition is applied correctly to some controls like MenuItems, but not to Buttons.
What am I doing wrong?
The MenuItem is correctly placed at the bottom of the Toolbar, but the ToggleButton is in the middle:
<Window.Resources>
<Style x:Key="MyToolbarStyle" TargetType="ToolBar">
<!-- Setters for Toolbar properties -->
<Setter Property="Height" Value="80" />
<!-- Nested setters for children of the Toolbar -->
<Style.Resources>
<Style TargetType="MenuItem">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
<Style TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<Grid >
<ToolBar VerticalAlignment="Top" Style="{StaticResource MyToolbarStyle}">
<MenuItem Header="MyMenuItem" /> <!-- Appears on the bottom like defined in the style-->
<ToggleButton Content="MyToggleButton" /> <!-- Nested style does not seem to be applied-->
</ToolBar>
</Grid>
The WPF ToolBar is a special type of control that defines some custom styles for some WPF controls like Button, ToggleButton... full list here, you can identify them by ElementName + StyleKey property name. If you'd like to change a default style for a specific control you will have to modify one of these styles.
Try replacing your style for the ToggleButton with the following:
<Style x:Key="{x:Static ToolBar.ToggleButtonStyleKey}" TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Bottom" />
</Style>
What you are doing wrong is thinking that WPF Styles are like CSS styles. In WPF, Styles are just not used that way. Sure, if we could, we'd probably save a few lines of XAML, but we can't. The best that we can do is what you have done... I'm assuming that you've created a Style for a top level element like Control. As you have seen, not all controls will extend the Control class, so the Style won't be applied to all of them.
Instead, Styles in WPF are more like the .class styles in CSS... one Style per type and then we can apply a further Style per UI element. There are lots of situations like this in WPF where we wish we could write less code, but it is how it is and the sooner that everybody realises it, the better.
UPDATE >>>
In response to your first comment, you seem to be mistaken. Just to clarify, if what you are calling nested Styles are the Styles that you defined in the outer Style.Resources section, then there is nothing wrong with that... no problem what-so-ever. Just take those inner Styles out of the Resources section and you will see the same UI.
Now you're probably thinking of changing your question title to something like 'Why isn't my default ToggleButton Style being applied inside a ToolBar control?'. While I can't say for sure, I can only assume that this behaviour is caused by a Style that has been defined within the ToolBar ControlTemplate.
I'm thinking that because of the following points:
A custom implicit Style (no x:Key) will not work inside the ToolBar control.
A custom explicit Style (named) will work as expected inside the ToolBar control.
A Style property set on the element will work as expected inside the ToolBar control.

Xaml Grid Styling

New to WPF, I'm having some trouble creating a styles in my code, I was able to make some buttons styles by drawing rectangles and making them into buttons, this opened a template editor so I was able to do it.
Now I'm wanting to create a template for a repeating stackpanel/grid layout, and I wrote it by hand this time, but I am getting an error that says the "template is not a valid member"
This is the kind of thing I was trying to create, but the Property="Template" bit is underlined in red. Can somebody explain to me the reason behind this? How do I create or initialize the template?
<Style x:Key="LaneStyle" TargetType="{x:Type Grid}">
<Setter Property="Width" Value="760"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Grid}">
<!-- Things here -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If someone could direct me to a tutorial on styles/templates that would be nice as well, haven't been able to find one that explained it in more detail.
Grid is not a control, therefore you cannot apply a ControlTemplate to it. If you're looking for a "repeater" kind of thing, you should be using an ItemsControl.
The best way to create templates/styles is by using Microsoft Blend 3.0/4.0
Over there one can easily find out what's the progress after doing each change.
In your case, a grid cannot be styled as it is a container not a control. If you wish to customize some control need to modify the control template of the control.

What is the most economical way to implement your own window border and title bar?

I am pretty new to WPF and am sitting here with my book trying to figure out the best approach to this application.
The title bar is not part of the client area so I am making my own title bar.
Which way would it be easiest to make this into some sort of resource to apply to all new windows I create?
<Application.Resources>
<Style x:Key="WindowTheme">
<Setter Property="Window.WindowStyle" Value="None"/>
</Style>
<!--Would I create a user control here for the title bar/border and title bar buttons? Or would it be a style?-->
</Application.Resources>
In WPF, there are two ways to use styles: Named styles and typed styles. A named style has an x:Key="..." attribute. A typed style doesn't have a name, but a TargetType="..." attribute (Rem: Named styles can and very often do have a TargetType as well, so named styles and unnamed styles would be more precise). Typed styles automatically get applied to all controls in the scope, which are of type TargetType (not a derived type).
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Blue" />
</Style>
To create your own window, you can set it's template property to a UserControl in the style:
<Style TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The professional way to implement the control template is to implement it 'from scratch', this means not using a UserControl which derives from Window. To do this, you define the visual tree of the Window, and use the WPF feature TemplateParts to define what part of your control template is responsible for what functionality of the window.
Here is a tutorial which describes pretty exactly what you want to do:
CodeProject tutorial

Resources