Binding Path Fill to Button Foreground in ContentPresenter - wpf

I have a Button Style with a Template containing a ContentPresenter, in which I am attempting to bind the Fill of a Path to the Foreground of a button:
<!-- This is inside the template of a button style -->
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Path}">
<Setter Property="Fill" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
I also have a Path with no Fill set, that I can reference in the button as the content, like so:
<Button Style="{DynamicResource MyButtonStyle}" Content="{DynamicResource PathIcon}" Foreground="Blue"/>
I would expect the Path inside the button to be blue, but it isn't... it doesn't grab the foreground from the button.
How can I get the Path to bind to the color of the button?
Thank you!
P.S.:
If I put a hardcoded color in the Value (i.e. Value="Red"), the Path inside the button is red... so I know that works...
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Path}">
<Setter Property="Fill" Value="Red"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
Edit:
Here is the complete Style and ControlTemplate:
<Style x:Key="Button_Style" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{StaticResource White_Brush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid" Background="Transparent">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Path}">
<Setter Property="Fill" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!-- Should affect Text as well as Paths in the Content property of the button! -->
<Setter Property="Foreground" Value="{StaticResource Black_Brush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Okay, let's order:
it doesn't grab the foreground from the button.
In styles this construction:
RelativeSource={RelativeSource AncestorType=Button}
will not work, because the Style is just the collection of setters, he does not know about control, are there, specifically about the content of the visual tree. Because RelativeSource should refer to the items above in the visual tree. For this purpose, usually using DataTemplate or ControlTemplate.
If I put a hardcoded color in the Value (i.e. Value="Red")
Yes, in this case, will be working, and always better to create the design of the form:
<SolidColorBrush x:Key="MyButtonColor" Color="Blue" />
And use it for control, like Button:
<Button Background="{StaticResource MyButtonColor}" ... />
and in Style or elsewhere:
<Setter Property="Fill" Value="{StaticResource MyButtonColor}" />
That is, it is better not to depend on the element parameters (background color, etc.) located in a visual tree, because it can:
May move to another panel (Grid, StackPanel) or UserControl
May leave from the project
And brushes in the as resources will always be in one place, changing them in this place, all the elements of their pick up. Also colors can be stored in a special data model that does not depend on the specific technical implementations (resources, variables) in which the data can come from an external source, such as the project/config settings.
If possible, it is better to avoid the use of dynamic resources due to unnecessary use of system perfomance (and in some cases memory leaks), in your cases they are not needed.
Dynamic resources are usually explicitly defined for SolidColorBrush and another species brushes, because by default they are frozen, and they not recommended changed because of the above mentioned reasons (memory leaks). More information can be found here:
Freezable Objects Overview on MSDN
Edit
As I understand it, you want to make universal Style for Button to make the contents of Path or Text (in the case of simultaneous use will be easier). As I have already mentioned above, RelativeSource should be around ControlTemplate, therefore, the Path will be in the Grid with the ContentPresenter.
To style knew, which is provided for the text or for the path, to the Tag (optional property) indicates two properties: OnlyText or OnlyPath.
To set the data for the Path, I've created a attached dependency property, and prescribed it in the ControlTemplate.
Below is a complete example:
XAML
<Window x:Class="ButtonPathHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ButtonPathHelp"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="Green_Brush" Color="Green" />
<SolidColorBrush x:Key="Black_Brush" Color="Black" />
<Style x:Key="Button_Style" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{StaticResource Green_Brush}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<ContentPresenter x:Name="MyContent"
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" />
<Path x:Name="MyPath"
SnapsToDevicePixels="True"
Width="20"
Height="18"
Stretch="Fill"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Data="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:MyDependencyClass.DataForPath)}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{StaticResource Black_Brush}"/>
</Trigger>
<Trigger Property="Tag" Value="OnlyText">
<Setter TargetName="MyPath" Property="Visibility" Value="Collapsed" />
<Setter TargetName="MyContent" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="Tag" Value="OnlyPath">
<Setter TargetName="MyPath" Property="Visibility" Value="Visible" />
<Setter TargetName="MyContent" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<WrapPanel>
<WrapPanel.Resources>
<sys:String x:Key="Save">
F1 M 20.5833,20.5833L 55.4167,20.5833L 55.4167,55.4167L 45.9167,55.4167L 45.9167,44.3333L 30.0833,44.3333L 30.0833,
55.4167L 20.5833,55.4167L 20.5833,20.5833 Z M 33.25,55.4167L 33.25,50.6667L 39.5833,50.6667L 39.5833,55.4167L 33.25,
55.4167 Z M 26.9167,23.75L 26.9167,33.25L 49.0833,33.25L 49.0833,23.75L 26.9167,23.75 Z
</sys:String>
<sys:String x:Key="Search">
F1 M 23.4454,49.2637L 31.7739,41.1598C 30.6986,39.2983 30.4792,37.1377 30.4792,34.8333C 30.4792,27.8377 35.7544,
22.1667 42.75,22.1667C 49.7456,22.1667 55.4167,27.8377 55.4167,34.8333C 55.4167,41.8289 49.7456,47.1042 42.75,
47.1042C 40.5639,47.1042 38.5072,46.9462 36.7125,45.9713L 28.3196,54.1379C 27.0829,55.3746 24.6821,55.3746 23.4454,
54.1379C 22.2088,52.9013 22.2088,50.5004 23.4454,49.2637 Z M 42.75,26.9167C 38.3777,26.9167 34.8333,30.4611 34.8333,
34.8333C 34.8333,39.2056 38.3777,42.75 42.75,42.75C 47.1222,42.75 50.6667,39.2056 50.6667,34.8333C 50.6667,
30.4611 47.1222,26.9167 42.75,26.9167 Z
</sys:String>
</WrapPanel.Resources>
<Button Name="SaveButton"
Style="{StaticResource Button_Style}"
Tag="OnlyPath"
local:MyDependencyClass.DataForPath="{StaticResource Save}"
Margin="10" />
<Button Name="JustText"
Style="{StaticResource Button_Style}"
Tag="OnlyText"
Content="Just Text"
Margin="10" />
<Button Name="SearchButton"
Style="{StaticResource Button_Style}"
Tag="OnlyPath"
local:MyDependencyClass.DataForPath="{StaticResource Search}"
Margin="10" />
</WrapPanel>
</Window>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyDependencyClass : DependencyObject
{
#region IsCheckedOnDataProperty
public static readonly DependencyProperty DataForPathProperty;
public static void SetDataForPath(DependencyObject DepObject, string value)
{
DepObject.SetValue(DataForPathProperty, value);
}
public static string GetDataForPath(DependencyObject DepObject)
{
return (string)DepObject.GetValue(DataForPathProperty);
}
#endregion
static MyDependencyClass()
{
PropertyMetadata MyPropertyMetadata = new PropertyMetadata(String.Empty);
DataForPathProperty = DependencyProperty.RegisterAttached("DataForPath",
typeof(string),
typeof(MyDependencyClass),
MyPropertyMetadata);
}
}
Note: In the Style I have not used TemplateBinding for attached property, because TemplateBinding doesn’t work outside a template or outside its VisualTree property, so you can’t even use TemplateBinding inside a template’s trigger. Therefore, we must use the construction {RelativeSource TemplatedParent} and a Path equal to the dependency property whose value you want to retrieve.
Output
To download the entire example please follow this link.

I stumbled across simillar problem but was wondering how to get to the 'Foreground Colour' of the Button in its DISABLED state (to have correct colour of my drawing). Here is a finally simple sollution. No templates, No styles, no code, nothing at all. Just the right relative binding :-) :
<StackPanel Orientation="Horizontal">
<Button Height="22" IsEnabled="False">
<Polygon Points="4,0 4,5 5,5 2.5,10 0,5 1,5 1,0 "
Fill="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}">
<Polygon.LayoutTransform>
<RotateTransform Angle="90"></RotateTransform>
</Polygon.LayoutTransform>
</Polygon>
</Button>
<Button Height="22" IsEnabled="True">
<Polygon Points="4,0 4,5 5,5 2.5,10 0,5 1,5 1,0 "
Fill="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}">
<Polygon.LayoutTransform>
<RotateTransform Angle="180"></RotateTransform>
</Polygon.LayoutTransform>
</Polygon>
</Button>
</StackPanel>

Related

WPF ToggleButton style that changes text of TextBlock within a StackPanel based on IsChecked trigger

I have a WPF style for a toggle button that uses a stack panel to achieve stacked, vertical text. I want the button text to change based on the toggle button's IsChecked state.
Additionally, the number of characters changes so I need to hide one of the text blocks in the stack panel. I tried setting the Visibility property of the Letter4 text block to hidden but the text was not vertically centered.
The code below works but it's just a cheesy workaround — I change the font size to 1 so it seems to disappear. (I pulled out all the formatting to make it simpler.) What is the correct way to do what I need?
Thanks.
<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="Letter1"/>
<TextBlock x:Name="Letter2"/>
<TextBlock x:Name="Letter3"/>
<TextBlock x:Name="Letter4"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="S"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="T"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="O"/>
<Setter Property="TextBlock.Text" TargetName="Letter4" Value="P"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="R"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="U"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="N"/>
<Setter Property="TextBlock.Text" TargetName="Letter4" Value=""/>
<Setter Property="TextBlock.FontSize" TargetName="Letter4" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
IMHO, the biggest problem with the code is that you're trying to do everything in XAML instead of letting a view model mediate the changeable values. A smaller issue is how you're actually implementing the vertically-stacked text.
There is already another question with good advice about vertically-stacked text. See Vertical Text in Wpf TextBlock
We can combine the advice there, where they use ItemsControl to display the text vertically, along with a view model to provide the actual text, and a placeholder ItemsControl that is hidden, but not collapsed (so that it still takes up space), to display the toggle button much more simply than in the code you have now.
First, the view model:
class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get => _isChecked;
set => _UpdateField(ref _isChecked, value, _OnIsCheckedChanged);
}
private string _buttonText;
public string ButtonText
{
get => _buttonText;
set => _UpdateField(ref _buttonText, value);
}
public ViewModel()
{
ButtonText = _GetTextForButtonState();
}
public event PropertyChangedEventHandler PropertyChanged;
private void _OnIsCheckedChanged(bool previous)
{
ButtonText = _GetTextForButtonState();
}
private string _GetTextForButtonState()
{
return IsChecked ? "STOP" : "RUN";
}
private void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This view model just provides a property to receive the toggle button's state, as well as to provide the appropriate button text for that state.
Next, the XAML to use this view model:
<Window x:Class="TestSO68091382ToggleVerticalText.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO68091382ToggleVerticalText"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>
<Grid>
<ToggleButton IsChecked="{Binding IsChecked}"
HorizontalAlignment="Left" VerticalAlignment="Top">
<ToggleButton.Content>
<Grid>
<ItemsControl ItemsSource="STOP" Visibility="Hidden"/>
<ItemsControl ItemsSource="{Binding ButtonText}" VerticalAlignment="Center"/>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</Grid>
</Window>
The ToggleButton.IsChecked property is bound to the IsChecked property in the view model, so that it can update the text as necessary. Then the content of the ToggleButton includes the ItemsControl that will display the text vertically.
Note that button's direct descendent is actually a Grid. This is so that two different ItemsControl elements can be provided: one shows the text itself, and is bound to the ButtonText property; the other has hard-coded the longer of the two strings that might be displayed. This ensures that the ToggleButton's size is always the same, large enough for that longer text. The bound ItemsControl is then centered vertically; you can of course use whatever aligment you like there, but the way your question is worded it sounds like you want the text vertically centered.
For what it's worth, if you really want to do everything in XAML, that's possible to. I personally prefer to avoid this kind of use for triggers, but I admit there's no hard and fast rule that says you can't. My preference mainly has to do with my desire to keep the XAML as simple as possible, as I find it a less readable language, and harder to mentally keep track of all the different related elements, which adding triggers tends to make more complex.
If you do prefer a XAML-only solution, it would look like this:
<Window x:Class="TestSO68091382ToggleVerticalText.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO68091382ToggleVerticalText"
xmlns:s="clr-namespace:System;assembly=netstandard"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<s:String x:Key="runText">RUN</s:String>
<s:String x:Key="stopText">STOP</s:String>
</Window.Resources>
<Grid>
<ToggleButton HorizontalAlignment="Left" VerticalAlignment="Top">
<ToggleButton.Content>
<Grid>
<ItemsControl ItemsSource="STOP" Visibility="Hidden"/>
<ItemsControl VerticalAlignment="Center">
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Setter Property="ItemsSource" Value="{StaticResource runText}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}" Value="True">
<Setter Property="ItemsSource" Value="{StaticResource stopText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>
</Grid>
</ToggleButton.Content>
</ToggleButton>
</Grid>
</Window>
Mechanically, this is very similar to the view model-based example above, just using a DataTrigger to respond to the changes in the ToggleButton.IsChecked state instead of doing it in the view model.
Note that you really only need one trigger. You can use a Setter to provide the unchecked state's value, and then use a single trigger to override that value for the checked state.
You need to change Visibility:
<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="Letter1"/>
<TextBlock x:Name="Letter2"/>
<TextBlock x:Name="Letter3"/>
<TextBlock x:Name="Letter4" Text="P"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="S"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="T"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="O"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="TextBlock.Text" TargetName="Letter1" Value="R"/>
<Setter Property="TextBlock.Text" TargetName="Letter2" Value="U"/>
<Setter Property="TextBlock.Text" TargetName="Letter3" Value="N"/>
<Setter Property="TextBlock.Visibility" TargetName="Letter4" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But I agree with #Peter Duniho - you'd better use a different approach for vertical text.
For permanent text, to print it one letter on each line, it is enough to insert newline characters & # xA; between the letters.
Example:
<Style x:Key="RunStopToggle" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="PART_TextBlock" Text="R
U
N"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TextBlock.Text" TargetName="PART_TextBlock" Value="S
T
O
P"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Also see my answer with a converter for vertical text: https://stackoverflow.com/a/68094601/13349759

Override property of custom style

I have Style that applies to all of the buttons of my application:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse x:Name="StatusButtonCircle" Stroke="Black" StrokeThickness="0" Fill="AliceBlue" Stretch="Uniform">
<Ellipse.Width>
<Binding ElementName="StatusButtonCircle" Path="ActualHeight"/>
</Ellipse.Width>
</Ellipse>
<Ellipse x:Name="StatusButtonCircleHighlight" Margin="4" Stroke="Black" StrokeThickness="2" Stretch="Uniform">
<Ellipse.Width>
<Binding ElementName="StatusButtonCircleHighlight" Path="ActualHeight"/>
</Ellipse.Width>
</Ellipse>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
... some Triggers here
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I change properties (e.g. FontWeight, FontSize etc.) in XAML? I tried this:
<Button FontWeight="Bold" FontSize="30" Foreground="Red">
</Button>
In the designer-view, I see the changes. But during runtime those changes are not applied.
After some investigation, I also have a Style for all TextBlock like this:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontFamily" Value="Segoe UI Semibold" />
<Setter Property="Foreground" Value="White" />
</Style>
This Style seems to override the TextBlock that is used on the Button. I still can't change the Text Properties in XAML.
Here's what it looks like if I use the Styles above in an empty project:
In the designer, the changes are applied, during runtime the one from the TextBlock are applied. If I assign a x:Key to the TextBlock, it works fine. But then I have to assign this Style to every TextBlock used in the app manually.
You are facing typical style inheritance issue in wpf.
A control looks for its style at the point when it is being initalized. The way the controls look for their style is by moving upwards in logical tree and asking the logical parent if there is appropriate style for them stored in parent's resources dictionary.
In your case, you are using ContentPresenter in button as a default behaviour. and it is using TextBlock to represent text in button by default.
Therefore at the time of initialization, ContentPresenter finding TextBlock style and applying to represent content in button.
If you want to restrict ContentPresenter to look for the style then you have to bind a blank style to content presenter so that it will not look for any further style.
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse x:Name="StatusButtonCircle" Stroke="Black" StrokeThickness="0" Fill="AliceBlue" Stretch="Uniform">
<Ellipse.Width>
<Binding ElementName="StatusButtonCircle" Path="ActualHeight"/>
</Ellipse.Width>
</Ellipse>
<Ellipse x:Name="StatusButtonCircleHighlight" Margin="4" Stroke="Black" StrokeThickness="2" Stretch="Uniform">
<Ellipse.Width>
<Binding ElementName="StatusButtonCircleHighlight" Path="ActualHeight"/>
</Ellipse.Width>
</Ellipse>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock" BasedOn="{x:Null}"/>
<!-- Assigned Blank style here therefore it will not search for any further style-->
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can do it with the BasedOn. I show you an example.
<Window.Resources>
<Style TargetType="ToggleButton" BasedOn="{StaticResource DefToggleButton}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Content" Value="Some Cool Stuff"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="More Stuff"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
Here in my resources I have DefToggleButton, now in my xaml file I can set up any Property according to my need (which in this case is the FontWeight and Content Property).
I think if you remove the Template from your Style, then you can do what you want to do, like this:
<Window.Resources>
<Style TargetType="Button" x:Key="stBtn>
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI Semibold" />
</Style>
</Window.Resources>
The Template that you have says, that all Buttons should be shown as a Border with a ContentPresenter inside, which is not what you have asked.
Without the Template, you can define your Buttons like this:
<Button Content="Hi!" Style="{StaticResource stBtn}" Foreground="Red" >
Like this, you have a Blue Button with Red Foreground.
=================
Edit
So what if you define a Template, and use it in you style, like this?
Then, by TemplateBinding you can define that the Foreground and teh Content come later, when the Button is actually defined.
<Window.Resources>
<ControlTemplate x:Key="ctBtn" TargetType="{x:Type Button}">
<Label Background="Green" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}"/>
</ControlTemplate>
<Style x:Key="stBtn2" TargetType="{x:Type Button}">
<Setter Property="Template"
Value="{StaticResource ctBtn}" />
</Style>
<Window.Resources>
Then by defining the Button:
<Button Content="Hi!" Style="{StaticResource stBtn2}" Foreground="Red" >
===============
Edit2
So the general idea is that you can define a TemplateBinding for the properties of the elements in your template. So for example,you have an Ellipse in your template:
<Ellipse Fill="{TemplateBinding BorderBrush}" />
This defines that the Fill property of your Ellipse comes from the BorderBrush of your Button (Assuming that the template is targeting a Button)
Accordingly, you can put a Label in your Template, and set a TemplateBinding for its Forground and FontWeight property.
<Label Foreground="{TemplateBinding Foreground}" />
First, for this issue to be reproduced, Styles need to be set within a ResourceDictionary which is then added to Application.Resources (precisellyTextBlock global style). Setting Styles within for example Window.Resources will not reproduce the issue.
Global TextBlock Style is applied to the TextBlock created by ConentPresenter
As noticed in the question, the issue is that the global (keyless) Style for TextBlock is applied to the TextBlock created by ContentPresenter when it concludes the content to display is a string. For some reason this doesn't happen when that Style is defined within Window.Resources. As it turns out, there is more to this than just "controls are looking for their styles within their parent's resources".
ControlTemplate is a boundary for elements not deriving from Control class
For TextBlock (which doesn't derive from Control class, but from UIElement) within ControlTemplate, it means that wpf will not look for it's implicit Style beyond it's templated parent. So it won't look for implicit Style within it's parent's resources, it will apply application level implicit Style found within Application.Resources.
This is by design (hardcoded into FrameworkElement if you will), and the reason is exactly to prevent issues like this one. Let's say you're creating a specific Button design (as you are) and you want all buttons in your application to use that design, even buttons within other ControlTemplates. Well, they can, as Button does derive from Control. On the other hand, you don't want all controls that use TextBlock to render text, to apply the implicit TextBlock Style. You will hit the same issue with ComboBox, Label... as they all use TextBlock, not just Button.
So the conclusion is: do not define global Style for elements which don't derive from Control class within Application.Resources, unless you are 100% sure that is what you want (move it to Window.Resources for example). Or, to quote a comment I found in source code for MahApps.Metro UI library: "never ever make a default Style for TextBlock in App.xaml!!!". You could use some solution to style the TextBlock within your Button's ControlTemplate, but then you'll have to do it for Label, ComboBox, etc... So, just don't.

How to create a common IconButton in WPF

Here is my case,
To create a common icon button, which can be used with different icons whereever.
Here are my steps:
1). create a custom IconButton derived from Button, it has a DefaultImageSource dependency property. Here it is:
public class IconButton : Button
{
public static readonly DependencyProperty DefaultImageSourceProperty =
DependencyProperty.Register("DefaultImageSource", typeof(ImageSource), typeof(IconButton), new FrameworkPropertyMetadata(null));//, new FrameworkPropertyMetadata(OnImageSourceChanged));
public ImageSource DefaultImageSource
{
get { return (ImageSource)GetValue(DefaultImageSourceProperty); }
set { SetValue(DefaultImageSourceProperty, value); }
}
}
2). Create a style for IconButton in project resource where supppose to use this IconButton with this style,
<Style x:Key="IconButtonTemplate" TargetType="{x:Type WpfApplication3:IconButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type WpfApplication3:IconButton}">
<Grid>
<Image Source="{Binding RelativeSource={RelativeSource Self}, Path=DefaultImageSource}" Stretch="Uniform" Width="Auto" Height="Auto" x:Name="imageArt"/>
<Ellipse x:Name="focusEllipse" Fill="Transparent"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="focusEllipse" Property="Fill" Value="#AAFFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="focusEllipse" Property="Fill" Value="#AA808080"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="focusEllipse" Property="Fill" Value="#80808080"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
3). Create multi IconButton in my WPF window, and binding them to different icon, but no luck; icons don't show up when openning this window,
<Grid>
<DockPanel>
<WpfApplication3:IconButton Width="30" Height="30" DefaultImageSource="{DynamicResource RefreshIcon}" Style="{DynamicResource IconButtonTemplate}"></WpfApplication3:IconButton>
<WpfApplication3:IconButton Width="30" Height="30" DefaultImageSource="{DynamicResource RunIcon}" Style="{DynamicResource IconButtonTemplate}"></WpfApplication3:IconButton>
</DockPanel>
</Grid>
It might be issue of the wrong binding for the image resource, but can't figure out with several tries. Any suggestion? Thanks.
I can see the issue in Binding . Using
<Image Source="{Binding RelativeSource={RelativeSource Self}, Path=DefaultImageSource}" Stretch="Uniform" Width="Auto" Height="Auto" x:Name="imageArt"/>
using Self Mode you are trying to Bind Source to DefaultImageSource property of Image Control. And Image doesnot have any such property so bang.
Try this
<Image Source="{TemplateBinding DefaultImageSource}" Stretch="Uniform" Width="Auto" Height="Auto" x:Name="imageArt"/>
This will try to bind the Source of Image to DefaultImageSource property of Button.
I hope this will help.

Mouseover border in a custom control for a textblock

I am trying to create a custom control for a text block that when moused over, a border will appear. I am pretty new to WPF and have only made some very simple custom controls. I need to implement this in a XAML UserControl.
Any suggestions would be greatly appreciated. Thanks again, StackOverflow.
EDIT: I am going to have to bind a persistence property to several different controls, so I really need to do this in a custom control. This is what I have, and it isn't working:
xmlns:customControls="clr-namespace:****.CustomControls"
....
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
And the UserControl:
<UserControl
x:Class="****.MouseOverBorder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="MouseOverBorder" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" />
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Border Style="{DynamicResource MouseOverBorder}" BorderThickness="1" CornerRadius="3" SnapsToDevicePixels="True"/>
No need to make a UserControl. I've managed to accomplish this with the following markup:
<Border Style="{DynamicResource BorderStyle1}" BorderThickness="1" CornerRadius="3" >
<TextBlock Text="TextBlock" />
</Border>
Here's the style:
<Style x:Key="BorderStyle1" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="3"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#FF123BBA"/>
</Trigger>
</Style.Triggers>
</Style>
EDIT:
Still don't get it why do you need a UserControl (please don't call it custom control - these are different things), but let's consider your example.
When you write the following
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
you are actually setting MouseOverBorder.Content property. Originally it's Content is defined in MouseOverBorder.xaml file. So you are replacing all your UserControl structure with TextBlock. But still I got your idea and have solution for it.
First, add custom DependencyProperty and CLR wrapper for it to MouseOverBorder class:
public static readonly DependencyProperty MyContentTemplateProperty =
DependencyProperty.Register("MyContentTemplate", typeof(DataTemplate), typeof(MouseOverBorder), null);
[Browsable(true)]
[Category("Other")]
public DataTemplate MyContentTemplate
{
get { return (DataTemplate)GetValue(MyContentTemplateProperty); }
set { SetValue(MyContentTemplateProperty, value); }
}
Second, make something inside MouseOverBorder use this property, e.g.
<ContentPresenter ContentTemplate="{Binding MyContentTemplate, ElementName=userControl}"/>
<!-- userControl is the Name of MouseOverBorder, defined in xaml -->
At last, you can use your UserControl as following:
<customControls:MouseOverBorder>
<customControls:MouseOverBorder.MyContentTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</DataTemplate>
</customControls:MouseOverBorder.MyContentTemplate>
</customControls:MouseOverBorder>

WPF Dependency Properties Settings binding doesnt update

Ive already been through about 30 different posts and changed how I handle this and it gets closer but still wont work.
I have a Custom Button control in a library, that I am using in Main application. The button displays, handles mouse overs and such, but the dependency property for the text, (or Icon but Ill get to that later) wont update. When I set up the Dependency property with a default value that the only value it displays, it wont display anyhting I set in the designer, or through code.
public static readonly DependencyProperty FileTextProperty;
//Constructor
static FileButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FileButton), new FrameworkPropertyMetadata(typeof(FileButton)));
// Initialize dependency properties
FileTextProperty = DependencyProperty.Register("FileText", typeof(string), typeof(FileButton), new UIPropertyMetadata ("Default File Text"));
}
/// <summary>
/// The Filename text displayed by the button.
/// </summary>
[Description("The text displayed by the button."), Category("Common Properties")]
public string FileText
{
get { return (string)GetValue(FileTextProperty); }
set { SetValue(FileTextProperty, value); }
}
here is the XAML (Somewhat abbreviated)
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GradientStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FileButton}">
<Grid x:Name="main" MinHeight="38" MaxHeight="38">
<Grid Margin="4,0" Name="DisplayMain">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock x:Name="fileText" HorizontalAlignment="Stretch" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:FileButton, AncestorLevel=1}, Path=FileText}" TextWrapping="NoWrap" VerticalAlignment="Top" Grid.Column="0" Margin="4,0,0,0" TextTrimming="WordEllipsis"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ControlTemplate.Resources>
<Grid x:Name="LayoutRoot" Margin="0">
<local:FileButton Style="{DynamicResource GradientStyle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I run it the only data displayed on the button is the defaulted text. Even though I know I am setting the Dependency Property. I even when as far as using the binding Path=FileText.Length in the XAML to make sure its reaching it and it is, and it displayed 0 on the button, so it seems like Im not setting the data, even when I manually create the button, and insert it into a container, it still doesnt display what I set it to. (Yes I stepped into it in the debugger to make sure)
Any ideas, its been most of the day trying to figure this out.
PLEASE HELP THANKS!
You don't need to create a custom button for this. You can put any content you want inside a Button:
<Grid x:Name="LayoutRoot">
<Button VerticalAlignment="Top" Margin="132,140,168,0" Height="54.96">
<StackPanel>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding FileName}" HorizontalAlignment="Left"/>
</StackPanel>
</Button>
</Grid>
Where FileName is a property in a viewmodel you create, which you set to be the DataContext of your Window (or grid or button).
With this MVVM setup, when you change FileName in your code, it will automatically display it in your button.
You can still style the button any way you want.
I'm not sure why you're using RelativeSource binding instead of TemplateBinding
With all else being the same, I would do this
<TextBlock Text="{TemplateBinding FileText}" />

Resources