XDG0008 error occuring while implementing DependencyProperty into TemplateBinding [duplicate] - c#

This question already has answers here:
Template Binding with Attached Properties
(2 answers)
Closed 1 year ago.
To easily change the template-specific brushes of a button without directly changing the template, I decided to make a DependencyProperty that will bind to a template-specific brush. That way, I can change this brush just as easy as changing any other regular property. However, after implementing this DependencyProperty, I encountered an error: "Name "ExtensionClass" does not exist in namespace "clr-namespace:extensions"." What causes this error?
XAML:
<ResourceDictionary xmlns:ext="clr-namespace:Extensions"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ControlTemplate x:Key="ButtonBaseControlTemplate1" TargetType="{x:Type ButtonBase}">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="border" Value="{TemplateBinding Property=ext:ExtensionsClass.MouseOverBackground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>
C#:
namespace Extensions {
public class ExtensionsClass {
public static readonly DependencyProperty MouseOverBackgroundProperty = DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(Button));
public static void SetMouseOverBackground(UIElement element, Brush value) {
element.SetValue(MouseOverBackgroundProperty, value);
}
public static Brush GetMouseOverBackground(UIElement element) {
return (Brush)element.GetValue(MouseOverBackgroundProperty);
}
}
}

In addition to the problem with the Binding, which is covered in the answer to the duplicate question, you also have to be aware that you are declaring an attached property, which has to be registered with the RegisterAttached method.
Besides that, in both the Register and the RegisterAttached methods, the third argument has to be the type that declares the property, not the type of element where you intend to set the property, i.e. typeof(ExtensionsClass) here.
public static class ExtensionsClass
{
public static readonly DependencyProperty MouseOverBackgroundProperty =
DependencyProperty.RegisterAttached(
"MouseOverBackground",
typeof(Brush),
typeof(ExtensionsClass),
null);
public static void SetMouseOverBackground(UIElement element, Brush value)
{
element.SetValue(MouseOverBackgroundProperty, value);
}
public static Brush GetMouseOverBackground(UIElement element)
{
return (Brush)element.GetValue(MouseOverBackgroundProperty);
}
}
You bind to an attached property by means of a Binding Path with parentheses:
<Setter
Property="Background"
TargetName="border"
Value="{Binding Path=(ext:ExtensionsClass.MouseOverBackground),
RelativeSource={RelativeSource TemplatedParent}}"/>

Related

How to change the roundness of a button in a generic way

I am currently working on making my WPF application a little bit more generic.
Up to this point, for each button I wanted to create, I used a different style to modify roundness (and it creates a lot of useless code).
Using the following code I've managed to create a variable I can change from the XAML file, but I cannot link it to the roundness itself.
Could anyone please tell me what I am doing wrong? I already have checked on so many forums but no one seems to have the answer other than "don't do it in a generical way".
I can precise that everything is compiling and the style is otherwise correctly applied to the button (there is no xaml linking problem).
The style I am using:
<Style x:Key="AwakeButton" TargetType="{x:Type customcontrols:AwakeButton}" BasedOn="{StaticResource {x:Type Button}}"
xmlns:extensions="Awake.Services.Properties:Extensions">
<Setter Property="customcontrols:AwakeButton.BorderRoundness" Value="4.0"/>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="{Binding Path=BorderRoundness}" />
<!--<Setter Property="CornerRadius" Value="10" />-->
</Style>
</Style.Resources>
</Style>
The overload of the button I created to do so:
public class AwakeButton : Button
{
public AwakeButton()
{
}
public static DependencyProperty BorderRoundnessProperty =
DependencyProperty.RegisterAttached("BorderRoundness", typeof(double), typeof(AwakeButton));
public static void SetBorderRoundness(UIElement element, double value)
{
element.SetValue(BorderRoundnessProperty, value);
}
public static double GetBorderRoundness(UIElement element)
{
return (double)element.GetValue(BorderRoundnessProperty);
}
}
How I am using it in the page:
<customcontrols:AwakeButton Style="{StaticResource AwakeButton}" Margin="142,115,0,0" Width="136" Height="167" BorderRoundness="5">
You have to bind the BorderRoundness to the parent AwakeButton, otherwise it is resolved using the current DataContext, which does not contain this property. Furthermore, if you derive from Button, you do not have to make the dependency property attached, you could just register a normal one using the Register(...) method. Also make DPs static and readonly.
<Setter Property="CornerRadius" Value="{Binding BorderRoundness, RelativeSource={RelativeSource AncestorType={x:Type local:AwakeButton}}}" />
If you do not change anything special about the button, you could also create attached properties instead of a dedicated sub type just for exposing a BorderRoundness property.
public static class ButtonProperties
{
public static readonly DependencyProperty BorderRoundnessProperty =
DependencyProperty.RegisterAttached("BorderRoundness", typeof(double), typeof(ButtonProperties));
public static void SetBorderRoundness(UIElement element, double value)
{
element.SetValue(BorderRoundnessProperty, value);
}
public static double GetBorderRoundness(UIElement element)
{
return (double)element.GetValue(BorderRoundnessProperty);
}
}
You can refer to the BorderRoundness using attached property binding syntax (parentheses).
<Style x:Key="AwakeButton" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="local:ButtonProperties.BorderRoundness" Value="4.0"/>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="{Binding (local:ButtonProperties.BorderRoundness), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
</Style>
</Style.Resources>
</Style>
You use regular button now with the newly created attached border roundness property.
<Button Grid.Row="0" Style="{StaticResource AwakeButton}" Margin="142,115,0,0" Width="136" Height="167" local:ButtonProperties.BorderRoundness="5"/>
The roundness is applied as CornerRadius to the Border of the Button. The Border is defined in the ControlTemplate of the Button. The ControlTemplate defines the appearance of a control.
In other words, you need to delegate the property values to the related elements in the ControlTemplate.
To delegate the values to the ControlTemplate, you have to override this template and bind the templated parents properties to the template elements:
In your AwakeButton define the BorderRoundness property as simple DependencyProperty (not attached) and the override the default style definition, so that the AwakeButton will use its own default Style. This way the Button is reusable withgout having to redefine the Style each time you weant to use it, which is especially important wjen you publish your project as library:
AwakeButton.cs
public class AwakeButton : Button
{
public static readonly DependencyProperty BorderRoundnessProperty = DependencyProperty.Register(
"BorderRoundness",
typeof(Thickness),
typeof(AwakeButton),
new PropertyMetadata(default(Thickness)));
public Thickness DestinationPath
{
get => (Thickness) GetValue(AwakeButton.BorderRoundnessProperty);
set => SetValue(AwakeButton.BorderRoundnessProperty, value);
}
static AwakeButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AwakeButton), new FrameworkPropertyMetadata(typeof(AwakeButton)));
}
}
Generic.xaml.cs
This file is located in the Themes folder and contains all default styles. WPF will automatically check this file for a default style and apply it if no other Style override was found.
<Style TargetType="AwakeButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="AwakeButton">
<Border BorderBrush={TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="{TemplateBinding BorderRoundness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Stayle>
Example
<Grid>
<AwakeButton BorderRoundness="8" />
</Grid>
But if you want to make it really general, using an attached property, you have to make a attached behavior. The following code works with every DependencyObject that contains a Border as child in its visual tree:
class Element : DependencyObject
{
#region CornerRoundness attached property
public static readonly DependencyProperty CornerRoundnessProperty = DependencyProperty.RegisterAttached(
"CornerRoundness",
typeof(CornerRadius),
typeof(Element),
new PropertyMetadata(default(CornerRadius), Element.OnCornerRoundnessChanged));
public static void SetCornerRoundness(DependencyObject attachingElement, CornerRadius value) =>
attachingElement.SetValue(Element.CornerRoundnessProperty, value);
public static CornerRadius GetCornerRoundness(DependencyObject attachingElement) =>
(CornerRadius) attachingElement.GetValue(Element.CornerRoundnessProperty);
#endregion CornerRoundness attached property
private static void OnCornerRoundnessChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
{
if (Element.TryFindVisualChildElement(attachingElement, out Border elementBorder))
{
elementBorder.CornerRadius = (CornerRadius) e.NewValue;
}
}
public static bool TryFindVisualChildElement<TChild>(DependencyObject parent, out TChild resultElement)
where TChild : DependencyObject
{
resultElement = null;
if (parent is Popup popup)
{
parent = popup.Child;
if (parent == null)
{
return false;
}
}
for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
{
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
if (childElement is TChild child)
{
resultElement = child;
return true;
}
if (Element.TryFindVisualChildElement(childElement, out resultElement))
{
return true;
}
}
return false;
}
}
Example
<StackPanel>
<Button Element.CornerRoundness="8" />
<ToggleButton Element.CornerRoundness="8" />
</StackPanel>

Setting A dependency property by this

I've created a type of Border called SelectableBorder that has an additional property called "IsSelected". I'm using this property in some triggers e.g.:
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" />
</MultiTrigger.Setters>
However in the code in the background there is no way for me to set IsSelected, How do I go about creating a property that can be used in xaml triggers and in code in the background?
This is the current SelectableBorder code
public class SelectableBorder : Border
{
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached("IsSelected", typeof(bool), typeof(Border), new PropertyMetadata(false));
public static void SetIsSelected(UIElement element, bool value)
{
element.SetValue(IsSelectedProperty, value);
}
public static bool GetIsSelected(UIElement element)
{
return (bool)element.GetValue(IsSelectedProperty);
}
}
I am also seeing:
'SelectableBorder' initialization failed: The type initializer for 'SelectableBorder' threw an exception.
Which suggests to me that I'm not doing that well at my first attempt. Could you please guide me in the correct direction to solving these problems?
Since you're adding property to DependencyObject so you can use normal DependencyProperty instead of attached one. You can use attached if you want but then trigger should change. Also owner type of your property should be SelectableBorder instead of Border. You can also add IsSelected CLR wrapper do make it easier to set/get value in code behind.
public class SelectableBorder : Border
{
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(SelectableBorder), new PropertyMetadata(false));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
}

XAML How to own StaticResource in derived UserControl class

I have the following problem. I have a class which derives from UserControl, here is the code:
public partial class MyUC : UserControl
{
[...]
public bool IsFlying { get { return true; } }
[...]
}
I want to use a style, which is created for the class MyUC, below is the style code. It is located in App.Xaml :
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dc="clr-namespace:MyNamespace"
<Application.Resources>
<Style x:Key="mystyle" TargetType="dc:MyUC ">
<Style.Triggers>
<Trigger Property="IsFlying" Value="true">
<Setter Property = "Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
As you can see I want to use a property which I declared in MyUC.
The problem is that when I am trying to add a style to my control, an error occurres.
<UserControl x:Class="MyNamespace.MyUC"
[...]
Style="{StaticResource mystyle}">
<UserControl.Resources>
</UserControl.Resources>
</UserControl>
The error is: 'MyUC' TargetType does not match type of element 'UserControl'.
As far as I understand, the compiler do not recognize class MyUC to be derived from UserControl. How to fix it?
Thanks in advance!
Error might be at design time only, it should work fine at runtime. Run your app and see if it works for you.
Moreover your trigger won't work for normal CLR property, you need to make it a Dependency Property -
public bool IsFlying
{
get { return (bool)GetValue(IsFlyingProperty); }
set { SetValue(IsFlyingProperty, value); }
}
public static readonly DependencyProperty IsFlyingProperty =
DependencyProperty.Register("IsFlying", typeof(bool),
typeof(SampleUserControl), new UIPropertyMetadata(true));
Also, you can remove the x:Key="mystyle" from your style declaration. It will automatically gets applied to your UserControl.
That way you won't have to explicitly set style on your UserControl. This line won't be required then - Style="{StaticResource mystyle}"

Unable to set style-trigger property to custom dependency property

I got another problem while working with my usercontrol's xaml file -.-'
I tried to implement an IsChecked property to my custom button in order to set a different background colour if the button is checked.
So I created a DependencyProperty like this:
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(LeftMenuBtn));
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
Then I setup a new style trigger to handle this property:
<Style x:Key="ButtonEnableStates" TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="{DynamicResource CheckedStateGradient}" />
</Trigger>
</Style.Triggers>
</Style>
Expression Blend now underlines Property="IsChecked" and says:
The member "IsChecked" is not recognized or is not accessible.
How can I solve this problem?
Well, the Style's TargetType is Grid and the property is defined for LeftMenuBtn, not going to work like that.

Updating Custom Attached Property in Style Trigger with Setter

I was trying out attached properties and style triggers hoping to learn more about it.
I wrote a very simple WPF windows app with an attached property:
public static readonly DependencyProperty SomethingProperty =
DependencyProperty.RegisterAttached(
"Something",
typeof(int),
typeof(Window1),
new UIPropertyMetadata(0));
public int GetSomethingProperty(DependencyObject d)
{
return (int)d.GetValue(SomethingProperty);
}
public void SetSomethingProperty(DependencyObject d, int value)
{
d.SetValue(SomethingProperty, value);
}
And I was trying to update the 'Something' attached property with a property trigger defined in the button style section:
<Window x:Class="TestStyleTrigger.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestStyleTrigger;assembly=TestStyleTrigger"
Title="Window1" Height="210" Width="190">
<Window.Resources>
<Style x:Key="buttonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="local:Window1.Something" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Button Style="{StaticResource buttonStyle}"></Button>
</Window>
However, I kept getting following compilation error:
error MC4003: Cannot resolve the Style Property 'Something'. Verify that the owning type is the Style's TargetType, or use Class.Property syntax to specify the Property. Line 10 Position 29.
I can't understand why it gives me this error because I did use the 'Class.Property' syntax in the tag of the section. Can any one tell me how can I fix this compilation error?
Your backing methods for the dependency property are named incorrectly and must be static:
public static int GetSomething(DependencyObject d)
{
return (int)d.GetValue(SomethingProperty);
}
public static void SetSomething(DependencyObject d, int value)
{
d.SetValue(SomethingProperty, value);
}
Also, you shouldn't specify the assembly in the local XML NS mapping in the XAML because the namespace is in the current assembly. Do this instead:
xmlns:local="clr-namespace:TestStyleTrigger"

Categories