Weird overriding operation of a resource dictionary in WPF, C# [duplicate] - c#

This question already has answers here:
Overriding application wide style
(2 answers)
Closed 3 years ago.
I'm in pretty new to C# WPF and recently meet a very weird problem.
I'm managing style sheet xaml files as following pictures and code.
File Structure
[ App.xaml ]
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/StyleText.xaml"/>
<ResourceDictionary Source="Styles/StyleButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
[ StyleText.xaml ]
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
</Style>
[ StyleButton.xaml]
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock x:Name="TblockTest" Background="Gray" Foreground="Blue">
<TextBlock.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Green"/>
</Style>
</TextBlock.Resources>
<ContentPresenter x:Name="CpCustomButton" ContentSource="Content"/>
</TextBlock>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="CpCustomButton" Property="TextBlock.Foreground" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
These are test codes to reproduce the problem.
What I want to do is ignore the style of StyleText.xaml inside StyleButton.xaml without addressing key to the textblock style in StyleText.xaml
As you know, textblock style without key will serve the global style, and so I don't have to insert annoying 'style={~~}' code per textblock.
But now, I can't avoid that it infects the style of textblock inside of control template for other control like button.
As you can see in the StyleButton.xaml, I tried several things that I can think and googled so much times, but nothing solves this problem clearly.
The content of a button always shows in Red which is the color addressed by StyleText.xaml.
What should I do to solve the problem?
I also attach my test solution noted above.
https://www.dropbox.com/s/no0j60px2qnl51y/WeirdProblem.zip
Thanks to read.

I downloaded the solution and here's a fix: move the TextBlock Foreground style from the TextBlock.Resources to ContentPresenter.Resources like this
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock
x:Name="TblockTest"
Background="Gray"
Foreground="Blue">
<ContentPresenter x:Name="CpCustomButton" ContentSource="Content">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Green" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</TextBlock>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="CpCustomButton" Property="TextBlock.Foreground" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'm not sure why it didn't use the style from the TextBlock.Resources when that is a parent to the ContentPresenter.
Hope that helps. Michal

Related

Why is my button style not applied when declared in the resources of another style? [duplicate]

This question already has answers here:
How do you change Background for a Button MouseOver in WPF?
(6 answers)
Closed last month.
I don't understand why this doesn't change the Background and Foreground while hovering the Button. It changes the CornerRadius of the Border tho.
ButtonStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button" x:Key="RoundedCorners">
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Green"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>
</ResourceDictionary>
LoginView.xaml
<Button Height="50"
Width="200"
Style="{StaticResource RoundedCorners}">
LOGIN
</Button>
In WPF, an explicit Style set locally through Style={StaticResource StyleKey} takes precedence over an implicit style matched through a TargetType, as detailed here.
Hence, only the outer Style is applied to your Button because it's given a key and epxlicitly set.
To fix it, you should meger the inner Style into the keyed Style like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button" x:Key="RoundedCorners">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Green"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>
</ResourceDictionary>
This will properly change the Foreground when you hover it. The Background will still not change, but that's an entire different problem.

How do I access an element of a parent template style in XAML?

I am having trouble accessing a named element of a parent template style in WPF.
I have a custom control for a Button (written per this StackOverflow question):
CustomButton.xaml:
<ResourceDictionary ...>
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CurrentImage" Property="Source" Value="{Binding ImageHover, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<Window ...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
ImageButton inherits Button, and Image and ImageHover are defined as dependency properties. This code works great and does what expected.
Now, I want to extend this template style for a single particular button, adding an additional Trigger for changing the image of the button. I am trying to do the following:
<Window...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CustomButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource ButtonCustomStyle}"/>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="CurrentImage" Property="Source" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<local:ImageButton Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
<local:ImageButton Style="{StaticResource Disableable}" Image="\Images\image.png" ImageHover="\Images\image_hover.png" Width="20" Height="20"/>
</Grid>
But Visual Studio underscores the new setter line and says 'The name "CurrentImage" is not recognized'.
Is there a way to alter the CurrentImage element?
I am having trouble accessing a named element of a parent template style in WPF.
This is not possible. You can't base a ControlTemplate on another ControlTemplate.
I am afraid you will have to re-define the entire ControlTemplate from scratch in the derived Style if you want the setter to be able find the CurrentImage element that is defined in the template of the base Style.
WPF: Is there a way to override part of a ControlTemplate without redefining the whole style?
But instead of using a ControlTemplate trigger, you should be able to use a Style trigger that sets the Image property of the control itself rather than setting the Source property of the Image element in the template:
<Style x:Key="ButtonCustomStyle" TargetType="{x:Type local:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid>
<Image Source="{TemplateBinding Image}" Stretch="Fill" x:Name="CurrentImage"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image" Value="{Binding ImageHover, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type local:ImageButton}"
BasedOn="{StaticResource ButtonCustomStyle}"
x:Key="Disableable">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>
</Style.Triggers>
</Style>
Unlike ControlTemplete trigger, Style trigger doesn't have access to template elements. since you exposed Image property, use it in Style trigger setter:
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Image" Value="\Images\disabled.png"/>
</Trigger>

How can I change WPF Button Image in Setter

I have a very strange problem trying to change the controltemplate for a button in WPF (c#).
I now have the following code:
<Window.Resources>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MacOSX-Close" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Black" BorderThickness="0">
<Image x:Name="bgimg" Source="bin/debug/Ressources/Images/GUI/frame-btn-defaulted.png"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="bgimg" Property="Source" Value="bin/debug/Ressources/Images/GUI/frame-btn-defaulted.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
So this isn't working. It says
"{DependencyProperty.UnsetValue}" is no valid value for property "System.Windows.Controls.Image.Source" on a setter.
And when I tried using multiple images in a grid (because you can only set one child) and modified the "visibility" property, the button didn't show up at all!!! No button, no image. And, yes, I am sure the image exists.
But the source is relative to the solution root, not to the compiled program... does this matter? Please help me :) I'm gettin' little desperate here... :(
Best regards ;)
And please ask if somethin's not clear to you.
Create folder like say Resources in your project and put image in your resources folder and their build action should be set to Resource. Then you can set Source = "/Resources/frame-btn-defaulted.png"

ToggleButton Style only works on last ToggleButton

I'm trying to customize my ToggleButtons so that when checked they say 'Yes' in green and when not checked, say 'No' in red.
I've created the following style which is sitting in my Styles resource dictionary.
<!-- ToggleButtons -->
<Style x:Key="YesNoToggleStyle" TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="SpringGreen" />
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Yes"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Crimson" />
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="No"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
This works ... sort of. If the ToggleButton is the last one of either value, then it displays correctly. All previous buttons with the same value are blank. The height was also shrinking, but I fixed that with the 'Height' Setter above the triggers. To illustrate, when a new record is being created it looks like:
and after I've clicked buttons 1, 2, and 3 and 1 again:
I originally had the style referenced from the surrounding grid:
<Grid>
...
<Grid.Resources>
<Style BasedOn="{StaticResource YesNoToggleStyle}" TargetType="{x:Type ToggleButton}" />
</Grid.Resources>
But changing that so each ToggleButton references the style individually (<ToggleButton Style="{StaticResource YesNoToggleStyle}" ... />) hasn't made a difference.
I looked at Customizing the toggle state of a toggle button in wpf, and Override ToggleButton Style where the effect is the same, but they talk about external images, and my issues is all within wpf.
I also looked at the second answer to: i want to change backcolor of toggle button when toggle button ischecked and viceversa in WPF but a) I only have the blend + sketchflow preview that comes with VS2012, and b) i'm a total noob with blend and can't get from Select the "Checked State" to Reset the Background Color instruction in the answer (plus i'd be surprised if this task requires the blend tool).
Can anyone show me what to do to get multiple ToggleButtons to use the same style properly?
This works for me. Somewhere in Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="YesNoToggleStyle" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="Crimson" />
<Setter Property="Content" Value="No"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="SpringGreen" />
<Setter Property="Content" Value="Yes"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Note, that style is based on ToolBar.ToggleButtonStyle.
<Grid>
<Grid.Resources>
<ResourceDictionary Source="pack://application:,,,/Dictionary1.xaml"/>
</Grid.Resources>
<ItemsControl ItemContainerStyle="{StaticResource YesNoToggleStyle}">
<ToggleButton />
<ToggleButton />
<ToggleButton />
</ItemsControl>
</Grid>
try to replace Content property to ContentTemplate:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="Yes"/>
</DataTemplate>
</Setter.Value>
</Setter>
In my case I wanted to have a "Locked" ToggleButton in a common dll defined and reused across my Apps.
Here's my result, which worked for me. Maybe someone find it useful (put this in a Resourcedictionary.xaml):
<BitmapImage x:Key="LockedLock"
UriSource="/...;component/Resources/Lock_closed_16p.png" />
<BitmapImage x:Key="OpenLock"
UriSource="/...;component/Resources/Lock_open_16p.png" />
<Style x:Key="LockButton"
TargetType="ToggleButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{DynamicResource OpenLock }"
Width="12"
Height="12"
Name="contentimage" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton , AncestorLevel=1, Mode=FindAncestor }, Path=IsChecked}"
Value="True">
<Setter Property="Image.Source"
TargetName="contentimage"
Value="{DynamicResource LockedLock }" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Credits to:
Setting Button's Content to <Image> via Styles
Setter Target Name not recognized

Change a button's content in a style?

I'm trying to do something similar to this:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content"
Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Content">
<Setter.Value>
<CheckBox Content="Mouse is over" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Window>
However, I get a run-time XamlParseException with a message of:
Cannot add content of type
'System.Windows.Controls.CheckBox' to
an object of type 'System.Object'.
Error at object
'System.Windows.Controls.CheckBox
I'm actually trying to draw different icons for the button's content depending on external conditions. So I'm actually trying to use a DataTrigger, but the example above simplifies the problem. Any ideas?
The actual error is occurring because Visuals can not be directly set as a Setter value.
You can get the behavior you are looking for though, by setting the ContentTemplate using a DataTemplate, or by creating your content as a resource, either specific to the button or located elsewhere.
<Button>
<Button.Resources>
<CheckBox x:Key="Local_MouseOverContent" Content="Mouse is over" />
</Button.Resources>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content"
Value="{StaticResource Local_MouseOverContent}" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="Button">
<CheckBox Content="Mouse is over" />
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
REMARK! Exactly your example works in .NET Framework 4 without any Changes !!!!
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content"
Value="No mouse over" />
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Content">
<Setter.Value>
<CheckBox Content="Mouse is over" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
If you are making a generic style to be used by buttons all around your app, you will get visual tree conflicts using the approach where the image is a resource. So the template is your only choice in that case.
WARNING: This may not be the best or correct way to do it. Make sure you read the other answers on this page as well.
Pretty sure you'd want to use a control template in this sort of situation. Something like:
<style>
<Setter Property="Content">
<Setter.Value>
<ControlTemplate>
<Image Img="something.jpg" />
</ControlTemplate>
</Setter.Value>
</Setter>
</style>
And add a control template in the trigger for the on-hover.
Here's a good link

Categories