Style Trigger on Attached Property - wpf

I have created my own Attached Property like this:
public static class LabelExtension
{
public static bool GetSelectable(DependencyObject obj)
{
return (bool)obj.GetValue(SelectableProperty);
}
public static void SetSelectable(DependencyObject obj, bool value)
{
obj.SetValue(SelectableProperty, value);
}
// Using a DependencyProperty as the backing store for Selectable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectableProperty =
DependencyProperty.RegisterAttached("Selectable", typeof(bool), typeof(Label), new UIPropertyMetadata(false));
}
And then I'm trying to create a style with a trigger that depends on it:
<!--Label-->
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="Util:LabelExtension.Selectable" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<TextBox IsReadOnly="True" Text="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
But I'm getting a run time exception:
Cannot convert the value in attribute 'Property' to object of type 'System.Windows.DependencyProperty'. Error at object 'System.Windows.Trigger' in markup file
How can I access the value of the attached property in a style trigger? I have tried using a DataTrigger with a RelativeSource binding but it wasn't pulling the value through.

Your trigger declaration is fine, but your attached property declaration has a glitch. The owner type of a dependency property needs to be the type that declares it, not the type you plan to attach it to. So this:
DependencyProperty.RegisterAttached("Selectable", typeof(bool), typeof(Label)...
needs to change to this:
DependencyProperty.RegisterAttached("Selectable", typeof(bool), typeof(LabelExtension)...
^^^^^^^^^^^^^^^^^^^^^^

Related

How to use depenceny property in the style of tooltip to set the text?

In a button, I am using a dependency property to pass information from the view model to the style of the button, so I can set the color of the color according to some conditions.
The code for the button is this:
The style in my xaml file:
<Style x:Key="BotonesColorEstadosTemplate" TargetType="{x:Type Button}">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="dp:BotonesEstadosAttachedProperty.CodigoEstado" Value="0"/>
<Condition Property="dp:BotonesEstadosAttachedProperty.AccionHabilitada" Value="true"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<ContentTemplate>
</ContentTemplate>
</Style>
The dependency properties:
public static class BotonesEstadosAttachedProperty
{
public static readonly DependencyProperty CodigoEstadoProperty =
DependencyProperty.RegisterAttached(
"CodigoEstado",
typeof(short),
typeof(BotonesEstadosAttachedProperty));
public static short GetCodigoEstado(DependencyObject obj)
{
return (short)obj.GetValue(CodigoEstadoProperty);
}
public static void SetCodigoEstado(DependencyObject obj, short value)
{
obj.SetValue(CodigoEstadoProperty, value);
}
public static readonly DependencyProperty AccionHabilitadaProperty =
DependencyProperty.RegisterAttached(
"AccionHabilitada",
typeof(bool),
typeof(BotonesEstadosAttachedProperty));
public static bool GetAccionhabilitada(DependencyObject obj)
{
return (bool)obj.GetValue(AccionHabilitadaProperty);
}
public static void SetAccionHabilitada(DependencyObject obj, bool value)
{
obj.SetValue(AccionHabilitadaProperty, value);
}
}
How to use in the button:
<Button Name="btnAlmacenesActualizar" Content="..." Height="23" Margin="3,0,0,0" Width="23"
ap:BotonesEstadosDependencyProperty.CodigoEstado="{Binding CodigoEstadoActualizarAlmacenes}"
ap:BotonesEstadosDependencyProperty.AccionHabilitada="{Binding EsAccionActualizarAlmacenesHabilitada}">
With this, I can use a property of my view model and pass to my style, that use the information in the trigger to set the color of the button.
Now, I would like to have a style for the tooltip, to have the default configuration for all the tooltips, and I would like to can pass the text of the tooltip at first, but later I would like to pass another variables. By the moment, to test, I would like to try with the text.
I have this style:
<Style TargetType="ToolTip" x:Key="ToolTipDefaultStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="dp:ToolTipAttachedProperty.Texto"/>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
My StackPanel with the tooltip:
<StackPanel Name="spTiposIva" Orientation="Vertical" Margin="5,0,0,0"
ap:ToolTipDependencyProperty.Texto="{Binding TiposIvaTooltip}">
<StackPanel.ToolTip>
<ToolTip Style="{StaticResource ToolTipDefaultStyle}"/>
</StackPanel.ToolTip>
</StackPanel>
But in this case the text that is shown is "dp:ToolTipAttachedProperty.Texto". So I would like if it is possible to do the same than with the button, use a dependency propety to pass information from view model to the style.
Thanks.
You are currently not binding the attached property, you are assigning a string as Value. For binding to attached properties, you need to use the binding markup extension and parentheses, e.g.:
<Setter Property="Text" Value="{Binding (dp:ToolTipAttachedProperty.Texto)"/>
However, in your case you need to refer to the parent control of ToolTip, which your property is attached to. Normally, you would do this with a RelativeSource binding and AncestorType, but this does not work here, because ToolTip is not within the same visual tree as the parent control.
Instead, you can access the control via the PlacementTarget property on the parent ToolTip.
<Setter Property="Text" Value="{Binding PlacementTarget.(dp:ToolTipAttachedProperty.Texto), RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
Please also check your XAML for typos. The attached properties type does not match on the StackPanel and in the ToolTip style: ToolTipDependencyProperty or ToolTipAttachedProperty?

WPF Custom Control Property Setter

I have a very simple Button-based control that displays an ellipse with color taken from custom dependency control called "Brush".
The template displays ellipse with proper color, but the Setters in Trigger do not recognize the "Brush" property (errors highlighted in the XAML file below).
How to access the "Brush" property in the setter so I can change its value on MouseOver?
XAML:
<Button x:Class="WpfTest.EllipseButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest"
Style="{DynamicResource localStyle}"
Name="ellipseButton">
<Button.Resources>
<Style x:Key="localStyle"
TargetType="local:EllipseButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Fill="{Binding ElementName=ellipseButton, Path=Brush}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<!-- ERROR HERE: The property "Brush" is not a dependency property. -->
<Setter Property="Brush"
Value="Blue" />
<!-- ERROR HERE: The "BrushProperty" is not recognized or is not accessible. -->
<Setter Property="BrushPropety"
Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Resources>
<Grid>
</Grid>
</Button>
code-behind:
public partial class EllipseButton : Button
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
public Brush Brush
{
get
{
return (Brush)GetValue(BrushProperty);
}
set
{
SetValue(BrushProperty, value);
}
}
public EllipseButton()
{
InitializeComponent();
}
}
Your property is called "Fill" not "Brush":
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Fill", //Error is here
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
Change that to:
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Brush",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Gray)));
Okay, several things needed to be done:
1) Rename dependency property name to "Brush" (it was wrongly named "Fill") - thanks to HighCore
2) When the control is used in code, remove setting the "Brush" property - the local value overriden the setters from style.
3) Move the style from custom control to higher level (e.g. under "Themes\Generic.xaml")
4) Remove x:Key attribute from the style and keep just the type name (still don't know why...)
5) Add default value of the "Brush" property to the style setter (again, not sure why...)
Fixed EllipseButton.xaml:
<Button x:Class="WpfTest.EllipseButton" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid/>
</Button>
fixed code-behind:
public partial class EllipseButton
{
public static readonly DependencyProperty BrushProperty = DependencyProperty.Register(
"Brush",
typeof(Brush),
typeof(EllipseButton),
new FrameworkPropertyMetadata(null));
public Brush Brush
{
get
{
return (Brush)GetValue(BrushProperty);
}
set
{
SetValue(BrushProperty, value);
}
}
public EllipseButton()
{
InitializeComponent();
}
}
fixed style (Generic.xaml):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTest">
<Style TargetType="local:EllipseButton">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Fill="{Binding ElementName=ellipseButton, Path=Brush}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Brush" Value="Pink"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Brush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

How to override base style control template in derived style in WPF?

I have a style for button. That style contains the ControlTemplate for Button. The ControlTemplate contains an Image with name "ImgButton".
I want to make this style as base style for other Buttons and want to override the "Source" property of Image control for different buttons.
Any ideas?
You may create attached behavior that will offer a property to assign Source. You should bind your image to this property in a template using TemplatedParent as RelativeSource. In derived styles you can simply use Setter(s) to specify a different Source.
Attached behavoir:
public static class ImageSourceBehavior
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached(
"Source", typeof(ImageSource), typeof(ImageSourceBehavior),
new FrameworkPropertyMetadata(null));
public static ImageSource GetSource(DependencyObject dependencyObject)
{
return (ImageSource)dependencyObject.GetValue(SourceProperty);
}
public static void SetSource(DependencyObject dependencyObject, ImageSource value)
{
dependencyObject.SetValue(SourceProperty, value);
}
}
Styles:
<Style x:Key="Style1"
TargetType="Button">
<Setter Property="local:ImageSourceBehavior.Source"
Value="..."/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Image Source="{Binding Path=(local:ImageSourceBehavior.Source),RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Style2"
BasedOn="{StaticResource Style1}"
TargetType="Button">
<Setter Property="local:ImageSourceBehavior.Source"
Value="..."/>
</Style>

UserControl Custom Properties for Triggers

I am writing a custom user control, called MyUserControl. I have many DependecyProperties for it, which I use in the MainWindow where several MyUserControl are defined multiple times. What I would like to know is how can I create custom Properties that the Triggers/Properties for a style would fire?
For example, if I have a custom property BOOL IsGoing and a custom property MyBackgroung(the background of the UserControl), both defined as :
public bool IsGoing
{
get { return (bool)this.GetValue(IsGoingProperty); }
set { this.SetValue(IsGoingProperty, value); }
}
public static readonly DependencyProperty IsGoingProperty = DependencyProperty.RegisterAttached(
"IsGoing", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false));
public Brush MyBackground
{
get { return (Brush)this.GetValue(MyBackgroundProperty); }
set { this.SetValue(MyBackgroundProperty, value); }
}
public static readonly DependencyProperty MyBackgroundProperty = DependencyProperty.Register(
"MyBackground", typeof(Brush), typeof(MyUserControl), new PropertyMetadata(Brushes.Red));
and if I define my UserControl in MainWindow.xaml, how can I access the Triggers and set MyBackground, depending on whether or not the IsGoing property is true/false?
I tried many things, but in essence, I'm trying to achieve something like:
<custom:MyUserControl MyBackground="Green" x:Name="myUC1" Margin="120.433,0,0,65.5" Height="50" Width="250" VerticalAlignment="Bottom" HorizontalAlignment="Left" >
<Style>
<Style.Triggers>
<Trigger Property="IsGoing" Value="True">
<Setter Property="MyBackground" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</custom:MyUserControl>
I hope my explanation is good enough for you to understand. I've been working on this for a couple of days now, and I can't seem to find the solution.
Thanks for the help!!!
Adrian
Your style should just need to be used as UserControl.Style and have the correct TargetType, also default values you intend to change via trigger need to be moved into the style due to precedence:
<custom:MyUserControl.Style>
<Style TargetType="custom:MyUserControl">
<Setter Property="MyBackground" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsGoing" Value="True">
<Setter Property="MyBackground" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</custom:MyUserControl.Style>
Whether this actually does anything depends on how you use the properties in the control definition.

WPF Setting on a Control Template

General question. I have a ControlTemplate that is reasonably complex. Several TextBoxes etc.
I can't use TemplateBinding to bring all the properties to the surface so that I can set all the styles.
Is there a way for a Style to 'delv' into the controls within a control to set values?
Hope my question is clear without an example.
Thanks
The short answer is no. The ControlTemplate is essentially a black box, at least where XAML is concerned (there are ways to dig down the visual tree in code).
When you say you "can't use TemplateBinding", why not? If you just don't have enough available properties that can be fixed by creating some attached properties for the values you want to pass through. This is assuming you're templating a control that you can't change, otherwise you can just add new dependency properties.
Attached property:
public static class CustomProps
{
public static readonly DependencyProperty MyNewBrushProperty = DependencyProperty.RegisterAttached(
"MyNewBrush",
typeof(Brush),
typeof(CustomProps),
new UIPropertyMetadata(Brushes.Green));
public static Brush GetMyNewBrush(DependencyObject target)
{
return (Brush)target.GetValue(MyNewBrushProperty);
}
public static void SetMyNewBrush(DependencyObject target, Brush value)
{
target.SetValue(MyNewBrushProperty, value);
}
}
And usage in Style and Template:
<Style TargetType="{x:Type Button}">
<Setter Property="local:CustomProps.MyNewBrush" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:CustomProps.MyNewBrush)}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using this method also still allows overriding values on individual instances.

Resources