Apply default WPF style - wpf

I have a global style that sets all my TextBox styles, but in some cases I want to revert just the Foreground color to the original non-custom-style color. I tried using {TemplateBinding Foreground} inside the specific TextBoxes that I wanted to revert. It didn't end up being valid XAML and I'm not sure that's the right way anyhow.
Any ideas? Thanks.

There's a few ways this could be done. If you look at the Precedence List on the MSDN
then you can see that the Forground set in ways 1-8 will override the Foreground from a default style. The easiest way being just to set the local value in the TextBox.
<TextBox Foreground="Red" />
Another thing that you can do is use the 'BasedOn' property of styles to override the other versions. This does require giving a key value to your default style, but that can then be used to also apply the default like in this example:
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="Foreground"
Value="Red" />
<Setter Property="FontWeight"
Value="Bold" />
</Style>
<!-- Style applies to all TextBoxes -->
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource myTextBoxStyle}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style BasedOn="{StaticResource myTextBoxStyle}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground"
Value="Blue" />
</Style>
</TextBox.Style>
</TextBox>
Edit:
In the case that the default style is applying a value and you want to revert it to the base value there are a few ways I can think of, off hand, to get this behavior. You can't, that I know of, bind back to the default theme value in a generic manner.
We can however do some other things. If we need the style to not apply some properties, we can set the style to {x:Null}, thus stopping the default style from applying. Or we can give the element it's own style that does not inherit from the base style and then re-apply only the setters that we need:
<TextBox Text="Hello" Style="{x:Null}" />
<TextBox Text="Hello">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontWeight"
Value="Bold" />
</Style>
</TextBox.Style>
</TextBox>
We could modify the default style so that the Foreground will only be set on certain conditions, such as the Tag being a certain value.
<Style TargetType="{x:Type TextBox}"
x:Key="myTextBoxStyle">
<Setter Property="FontWeight"
Value="Bold" />
<Style.Triggers>
<Trigger Property="Tag"
Value="ApplyForeground">
<Setter Property="Foreground"
Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
<TextBox Text="Hello" />
<TextBox Text="Hello" Tag="ApplyForeground" />

Related

Conditional BasedOn in XAML styles

Given:
<Style x:Key="ThirdLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" />
</Style>
<Style x:Key="SecondLevelGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource MetroGroupBox}">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</Style>
<Style TargetType="GroupBox" x:Key="WidgetControlTemplateStyle" BasedOn="{StaticResource ThirdLevelGroupBoxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding CanExecuteClickCommand}" Value="True">
<!-- TODO: SecondLevelGroupBoxStyle -->
<Setter Property="Background" Value="{DynamicResource AccentColorBrush2}" />
</DataTrigger>
</Style.Triggers>
</Style>
<ControlTemplate TargetType="ContentControl" x:Key="WidgetControlTemplate">
<GroupBox ... Style="{StaticResource WidgetControlTemplateStyle}">
<ContentPresenter />
</GroupBox>
</ControlTemplate>
ControlTemplate has style WidgetControlTemplate. I want to base (BasedOn) the WidgetControlTemplate style conditionally on ThirdLevelGroupBoxStyle or SecondLevelGroupBoxStyle, in order to avoid XAML duplication. Is there way to achieve this?
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.
Is there way to achieve this?
No, not in XAML. The base style to base the style on must be known at design-time.
If I can't do that, I have to duplicate the definition of SecondLevelGroupBoxStyle.
Since you have two different styles that are based on MetroGroupBox, each style that is based on either of this will always be a separate style because you cannot base a single style on more than one style.
What you could do is to use a trigger that sets the Background property of the GroupBox to either AccentColorBrush3 or AccentColorBrush2 instead of trying to inherit this property from another style. It seems like you are already doing this in your WidgetControlTemplateStyle style. And yes, if the ThirdLevelGroupBoxStyle/SecondLevelGroupBoxStyle sets more than one property you will have to set all these in the WidgetControlTemplateStyle as well.
I am afraid there is no way around this unless you merge your two style into one or define them programatically somehow.

Is it possible in WPF to define multiple 'keyed'-Styles but with differing TargetTypes?

I am trying to get to grips with WPF Styles.
I was wondering if it is possible to define one Style key and then describe how it should be applied to different TargetTypes.
This approach is not working for me. I get an error message to say that "TargetType 'TextBlock'" does not match the Element "Image".'
It seems strange that every style:type combination needs it's own key name. I am doing something wrong? Is it completely the wrong approach?
e.g. in the Window.xaml:
<TabControl TabStripPlacement="Bottom">
<TabItem Content="{Binding UserContent}">
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="users_24.gif" Style="{StaticResource TabHdr}"/>
<TextBlock Text="{x:Static r:Messages.Tab_Users}" Style="{StaticResource TabHdr}"/>
</StackPanel>
</TabItem.Header>
</TabItem>
</TabControl>
and in the Resources.xaml
<Style x:Key="TabHdr" TargetType="{x:Type Image}">
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Margin" Value="2, 1, 2, 1"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="5, 1, 1, 1"/>
</Style>
You can do that if you plan on explicitly setting the Style as you are (i.e. they are keyed styles - implicit styles are always found using the exact type of the class). You just have to set the TargetType to be the lowest base type that defines all the dependency properties you are setting. So in your case you are setting properties that are defined on FrameworkElement so you can just set the TargetType to FrameworkElement.
<Style x:Key="TabHdr" TargetType="FrameworkElement">
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Margin" Value="2, 1, 2, 1"/>
</Style>
Note: If however you wanted to set something like Background (which is defined on Control) then you wouldn't be able to share that style with Image/TextBlock (which don't derive from Control) but you could create a style where the TargetType is Control in that case. You could even set the BasedOn of that style to the style you have for FrameworkElement so you can still share the other settings. E.g.
<Style x:Key="ctrl" TargetType="Control" BasedOn="{StaticResource TabHdr}">
<Setter Property="Background" Value="Red" />
</Style>
And then use it on multiple controls. E.g.
<TextBox Style="{StaticResource ctrl}" />
<Button Content="Foo" Style="{StaticResource ctrl}" />
To short answer your question.. you can't!
You are assigning TabHdr style to a TextBlock, but the style is defined as a Image control style. You can't do that.
If all of your control have to have a specific style, you can define a style without a key but with a specific TargetType. For example, in the code you provided, the TextBlock is applied to all TextBlocks.
PS: If you have to create a kind of theme for your application you can inherit and extend a base style using the BasedOn attribute.

Can I get this binding into a style?

I would like to take the xaml I currently have for a ComboBox (below), and condense it into something like the Style also shown below. I think this should work, but I have a 'Type'ing issue and not sure how to resolve it
"Cannot resolve the Style Property 'Margin'. Verify that the owning type is the Style's TargetType, or use Class.Property syntax to specify the Property.)
As I look at the existing ComboBoxStyle (also below) that I'm looking to base this new style off of, I see that I hadn't used x:Type, but it does seem to work.
Is there any reason this new style shouldn't work? What must I change?
Cheers,
Berryl
combo box, as is, working):
<ComboBox
x:Name="cboDepartmentFilter" Style="{StaticResource ComboBoxStyle}"
Margin="{StaticResource FliterPanelItem_Margin}" Width="{StaticResource FilterPanelItem_Width}"
ItemsSource="{Binding Path=DepartmentFilterControl.Choices}"
ToolTip="{Binding DepartmentFilterControlData.ToolTipTitle}"
/>
what I want:
<ComboBox Style="{StaticResource FilterPanelComboBoxStyle}" DataContext="{Binding DepartmentFilterControl}" />
<!- in some resource file ->
<Style x:Key="FilterPanelComboBoxStyle" BasedOn="{StaticResource ComboBoxStyle}">
<Setter Property="Margin" Value="{StaticResource FliterPanelItem_Margin}" />
<Setter Property="Width" Value="{StaticResource FilterPanelItem_Width}" />
<Setter Property="ItemsSource" Value="{Binding Choices}" />
<Setter Property="ToolTip" Value="{Binding ToolTipTitle}" />
</Style>
<!--
This style defines a common margin for items in a filter panel.
-->
150
existing ComboBoxStyle:
<!-- ComboBox Style -->
<Style x:Key="ComboBoxStyle" TargetType="ComboBox">
<Setter Property="Background" Value="{StaticResource headerBrush}" />
...
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle" Value="{StaticResource ComboBoxItemStyle}" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
You still need to specify the TargetType in the derived style. (Or you prefix the properties with "ComboBox.")

Changing Hyperlink foreground without losing hover color

I'm writing a WPF app, and I want some of my hyperlinks to be the default blue, and others to be green. No problem -- I can just set Foreground:
<TextBlock><Hyperlink Foreground="#0C0">Mark as done</Hyperlink></TextBlock>
The trouble is, when I do this, the hover effect goes away. In a normal Hyperlink, when I move the mouse over the link, it turns red, and when I move the mouse away, it goes back to blue. But in a Hyperlink with the Foreground property assigned, it's always that color -- the red-on-hover is totally suppressed.
How can I change a Hyperlink's color, without losing the default hover behavior and color?
Setting the Foreground directly (as you've done) doesn't work, and setting it in a Style doesn't work either, unless you "derive" that style from the default Hyperlink style (which must include the OnMouseOver trigger). So this works:
<TextBlock>
<Hyperlink>
<Hyperlink.Style>
<Style TargetType="Hyperlink"
BasedOn="{StaticResource {x:Type Hyperlink}}">
<Setter Property="Foreground" Value="#0C0"/>
</Style>
</Hyperlink.Style>
Mark as done
</Hyperlink>
</TextBlock>
Extracting that style back into the Window resources and referencing it with a key would probably make for more-readable XAML, but the above code does the job.
You should build your Hyperlink like this
<TextBlock Width="Auto" HorizontalAlignment="Center">
<Hyperlink Click="ForgotPassword_Clicked">
<TextBlock Text="Forgot Password?"/>
</Hyperlink>
</TextBlock>
And then this style should work for you
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Foreground" Value="Blue" />
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="TextBlock.TextDecorations" Value="Underline" />
</Trigger>
</Style.Triggers>
</Style>

Can you define multiple TargetTypes for one XAML style?

In HTML/CSS you can define a style which can be applied to many types of elements, e.g.:
.highlight {
color:red;
}
can be applied to both P and DIV, e.g.:
<p class="highlight">this will be highlighted</p>
<div class="highlight">this will also be highlighted</div>
but in XAML you seem to have to define the TargetType for styles, otherwise you get an error:
<Style x:Key="formRowLabel" TargetType="TextBlock">
is there a way to allow a XAML style to be applied to multiple elements or even to leave it open as in CSS?
The setters in WPF styles are checked during compile time; CSS styles are applied dynamically.
You have to specify a type so that WPF can resolve the properties in the setters to the dependency properties of that type.
You can set the target type to base classes that contain the properties you want and then apply that style to derived classes. For example, you could create a style for Control objects and then apply it to multiple types of controls (Button, TextBox, CheckBox, etc)
<Style x:Key="Highlight" TargetType="{x:Type Control}">
<Setter Property="Foreground" Value="Red"/>
</Style>
...
<Button Style="{StaticResource Highlight}" Content="Test"/>
<TextBox Style="{StaticResource Highlight}" Text="Test"/>
<CheckBox Style="{StaticResource Highlight}" Content="Test"/>
<!-- Header text style -->
<Style x:Key="headerTextStyle">
<Setter Property="Label.VerticalAlignment" Value="Center"></Setter>
<Setter Property="Label.FontFamily" Value="Trebuchet MS"></Setter>
<Setter Property="Label.FontWeight" Value="Bold"></Setter>
<Setter Property="Label.FontSize" Value="18"></Setter>
<Setter Property="Label.Foreground" Value="#0066cc"></Setter>
</Style>
<!-- Label style -->
<Style x:Key="labelStyle" TargetType="{x:Type Label}">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="0,0,0,5" />
</Style>
I think both of these methods of declaring a style might answer your question.
In the first one, there is no TargetType specified, but the property names are prefixed with 'Label'. In the second one, the style is created for Label objects.
Another method to do it is:
<UserControl.Resources>
<Style x:Key="commonStyle" TargetType="Control">
<Setter Property="FontSize" Value="24"/>
</Style>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ListBox"/>
<Style BasedOn="{StaticResource commonStyle}" TargetType="ComboBox"/>
</UserControl.Resources>
I wanted to apply a style to a Textblock and a TextBox but the selected answer didn't work for me because Textblock doesn't inherit from Control, in my case I wanted to affect the Visibility property, so I used FrameworkElement
<Style x:Key="ShowIfRequiredStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowIfRequiredStyle, UpdateSourceTrigger=PropertyChanged}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBlock Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
<TextBox Style="{StaticResource ResourceKey=ShowIfRequiredStyle}"/>
This works for the Visibility property because both items inherit from Frameworkelement and the property is defined there. Of course this will not work for properties defined only in Control, you can search the hierarchy tree and try to find a base class, anyway I thought this could help someone since this is a top search result and the selected answer is a little incomplete.
There is an alternative answer to the question. You CAN leave the TargetType parameter off the style altogether which will allow it to apply to various different controls, but only if you prefix the property name with "Control."
<Style x:Key="Highlight">
<Setter Property="Control.Foreground" Value="Red"/>
</Style>
Obviously, this only works for properties of the base control class. If you tried to set ItemsSource say, it would fail because there is no Control.ItemsSource
I got this working
<Style x:Key="HeaderStyleThin" TargetType="{x:Type Border}">
<Setter Property="Background" Value="Black" />
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background=" Value="Red" />
</Style>
</Style.Resources>
</Style>

Resources