I need to create a custom attached property that will be able to be empty as shown below:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<l:SimpleAttach.MyProperty></l:SimpleAttach.MyProperty>
</Grid>
I tried doing it this way:
public static class SimpleAttach
{
public static object GetMyProperty(DependencyObject obj)
{
return obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, object value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(object), typeof(SimpleAttach), new PropertyMetadata(null));
}
But that just gives an error:
The property "MyProperty" cannot be empty.
By contrast, MS doesn't seem to have any problem creating empty attached properties. Here is an example that does not give an error about being empty:
<TextBlock Name="VatAmount" Text="hello world" TextAlignment="Right" Margin="0,0,20,111" HorizontalAlignment="Right" Width="120" Height="16" VerticalAlignment="Bottom">
<i:Interaction.Behaviors>
</i:Interaction.Behaviors>
</TextBlock>
So what do I have to do to tell XAML that this property is allowed to be empty?
Okay, so I finally figured out my issues. Sometimes it helps to read the official documentation: XAML Syntax In Detail
In particular I seemed to be referring to "Content properties".
XAML content syntax is a syntax that is only enabled on classes that specify the ContentPropertyAttribute as part of their class declaration. The ContentPropertyAttribute references the property name that is the content property for that type of element (including derived classes). When processed by a XAML processor, any child elements or inner text that are found between the opening and closing tags of the object element will be assigned to be the value of the XAML content property for that object.
Second of all, in the example of Microsofts Interactivity.Behaviors, that is a collection and collections are allowed to have 0 items in them.
Furthermore, if you go down the road of implementing an attached behavior that is a collection, then this post will guide you through the quirks: Attached property of type list
Related
Can we implement dependency property in a class that does not inherit from DependencyObject? If yes what is the difference?
Yes you can. This is a variant of an Attached Property. From How to Create an Attached Property:
If your class is defining the attached property strictly for use on other types, then the class does not have to derive from DependencyObject. But you do need to derive from DependencyObject if you follow the overall WPF model of having your attached property also be a dependency property.
The difference begins in how you define an attached property. Instead of Register, you have to use the RegisterAttached method and you have to define the get and set accessors as static methods with the following naming convention, where PropertyName is the name of the attached property.
public static object GetPropertyName(object target)
public static void SetPropertyName(object target, object value)
Let's look at a simple example. Suppose you want create a button that shows an image and a text side-by side. Unfortunately, a Button only has a single Content. As you do not want to create a custom control right now, you try to solve the issue by creating a content template and an attached property for the path of an image to display.
public static class IconButtonProperties
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached(
"Source", typeof(string), typeof(IconButtonProperties));
public static void SetSource(UIElement element, string value)
{
element.SetValue(SourceProperty, value);
}
public static string GetSource(UIElement element)
{
return (string) element.GetValue(SourceProperty);
}
}
Now you can attach this property to a button in your view to define an image path. Here the attached property differs in that you define it on a different type (Button) using its owner type IconButtonProperties.
<Button ContentTemplate="{StaticResource ImageTextContentTemplate}"
local:IconButtonProperties.Source="Resources/MyImage.png"
Content="Click me!"/>
The last big difference is shown in the data template that uses the attached property with a Binding. When binding to an attached property, you have to put the property in parentheses.
<DataTemplate x:Key="ImageTextContentTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding (local:IconButtonProperties.Source), RelativeSource={RelativeSource AncestorType={x:Type Button}}}"/>
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Margin="5, 0, 0, 0"
Text="{Binding}"/>
</Grid>
</DataTemplate>
As you can see, attached properties are invaluable for extensibility and binding in WPF. For more information on attached properties in general, you can refer to the documentation:
Attached Properties Overview
How to: Register an Attached Property
Binding Path Syntax
DependencyProperty.RegisterAttached Method
DependencyProperty.RegisterAttachedReadOnly Method
I'm trying to understand dependency property and learn how to use it. I'm going through articles and in this article https://www.c-sharpcorner.com/UploadFile/6d590d/wpf-dependency-property/ there's this example:
MainWindow.xaml:
<Window x:Class="WpfApplication1.DependencyPropertyDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="DependencyPropertyDemo" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<local:CarDependencyClass x:Key="carDependencyClass"></local:CarDependencyClass>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Enter Car:" Grid.Row="0" VerticalAlignment="Center" />
<TextBox Text="{Binding Path=MyCar, Source={StaticResource carDependencyClass }}" Name="MyTextCar" Height="25" Width="150" />
<Button Name="MyButton" Content="Click Me!" Height="25" Click="MyButton_Click" Width="150" Grid.Row="1" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for DependencyPropertyDemo.xaml
/// </summary>
public partial class DependencyPropertyDemo : Window {
public DependencyPropertyDemo() {
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e) {
CarDependencyClass dpSample = TryFindResource("carDependencyClass") as CarDependencyClass;
MessageBox.Show(dpSample.MyCar);
}
}
public class CarDependencyClass : DependencyObject {
//Register Dependency Property
public static readonly DependencyProperty CarDependencyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(CarDependencyClass));
public string MyCar {
get {
return (string)GetValue(CarDependencyProperty);
}
set {
SetValue(CarDependencyProperty, value);
}
}
}
}
It works. I noticed that they registered dependency property with the name "MyProperty" and that it isn't used anywhere in the program. Only normal CLR property MyCar is used in xaml.
But then there's another article https://www.c-sharpcorner.com/article/simplest-wpf-dependency-property-for-beginners-on-background-color/. And they provide other example:
MainWindow.xaml:
<Window x:Class="DependencyPropertyTutorial.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:views="clr-namespace:DependencyPropertyTutorial" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DependencyPropertyTutorial" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="BG" Color="Green" />
</Window.Resources>
<Grid>
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
</Grid>
</Window>
CustomButtonControl.xaml:
<UserControl x:Class="DependencyPropertyTutorial.CustomButtonControl"
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"
xmlns:local="clr-namespace:DependencyPropertyTutorial"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button x:Name="btnCustom" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Height="52" Click="btnCustom_Click" />
</Grid>
</UserControl>
CustomButtonControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DependencyPropertyTutorial {
/// <summary>
/// Interaction logic for CustomButtonControl.xaml
/// </summary>
public partial class CustomButtonControl : UserControl {
public CustomButtonControl() {
InitializeComponent();
}
public static readonly DependencyProperty btnDependencyProperty = DependencyProperty.Register("SetBackground", typeof(SolidColorBrush), typeof(CustomButtonControl), new PropertyMetadata(new SolidColorBrush(Colors.HotPink), new PropertyChangedCallback(OnSetColorChanged)));
public SolidColorBrush SetBackground {
set {
SetValue(btnDependencyProperty, value);
}
get {
return (SolidColorBrush)GetValue(btnDependencyProperty);
}
}
private void btnCustom_Click(object sender, RoutedEventArgs e) {
this.SetBackground = new SolidColorBrush(Colors.IndianRed);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
CustomButtonControl mycontrol = d as CustomButtonControl;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e) {
btnCustom.Background = (SolidColorBrush)e.NewValue;
}
}
}
And here they register dependency property with the name "SetBackground" which is exactly the same as the name of CLR property - SetBackground. And if I change dependency property(the one I registered with Register method) "SetBackground" to something else, like "SetBackgroundDependencyProperty", then I get a XAML exception when trying to run the application. But "SetBackground" dependency property isn't even referenced in xaml anywhere. Only CLR property SetBackground is referenced in XAML at the line
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
I also get an error in Visual Studio with this example:
But when I try to build and run the application, it works.
So my questions are: why in the first example they didn't have to name registered dependency property the same as CLR property, but in the second example, I have to name registered dependency property the same as CLR property. Is there a way to name registered dependency property differently to the CLR property in the second example? Why and how xaml even uses dependency properties, considering xaml references only CLR properties anyway? I checked it and in both projects only CLR properties are referenced from XAML, according to VS IntelliSense. Why do I have to register dependency property with the same name as CLR property - "SetBackground" when in xaml only CLR property is referenced and it returns SolidColorBrush from the dependency property, anyway:
return (SolidColorBrush)GetValue(btnDependencyProperty);
Here's the solution with both examples:
https://github.com/KulaGGin/DependencyProperty
First example is a bit dirty, I wouldn't code it such way. There is a good convention to avoid a confusion - to name the DP as the CLR property + 'Property'(but it's not mandatory!) and register it as name of CLR property(if you want to use it as DP in XAML).
First to your questions:
First example does work, because of everywhere, where the property MyCar being used, it is used as CLR property. If you will try to bind to the MyCar, it will fail, because of there is no such a dependency property. To implement the functionality in this example would be enough just to declare a CLR property:
public string MyCar { get; set; }
instead of all this confusion with dependency property.
In second example CLR property as well as dependency property SetBackground are defined (the name btnDependencyProperty for the field is not convenient, but OK). Missunderstanding on your side, is what being used in XAML.
If you use in XAML Binding or DynamicResource the dependency property as well as CLR property are necessary! Therefore they need to have the same name. If there aren't, then you will get an error.
If you set the property to the StaticResource or directly to the value or even do not use it in XAML, then you will be able to run the application.
DependencyProperty implementation is slightly weird.
The XAML compiler depends on the CLR property wrapper in order to compile, but at runtime bindings ignore it completely and just call GetValue/SetValue on the DP. Therefore the names should match.
There was a typo in the first example. The registered dependency property name needs to be the same as the CLR-backing property.
Declaring a DependencyProperty is a two-stage process:
Register the property so that the WPF DependencyProperty system can track it and notify when properties change, etc.
Setup a CLR property that gives developers an API to get and set values.
I hope this helps.
I am trying to populate a custom string array in XAML, but am receiving an error. I subclassed a ComboBox and added a string [] that I want to fill with custom values:
public class MyComboBox : ComboBox
{
public string[] MyProperty { get { return (string[])GetValue(MyPropertyProperty); } set { SetValue(MyPropertyProperty, value); } }
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(string[]), typeof(MyComboBox));
}
My XAML is as follows:
<Window x:Class="Samples.CustomArrayProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Samples"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="CustomArrayProperty" Height="300" Width="300">
<Grid>
<local:MyComboBox Height="20">
<local:MyComboBox.MyProperty>
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:MyComboBox.MyProperty>
</local:MyComboBox>
</Grid>
</Window>
When I run this, I get the error: "'Monday' is not a valid value for property 'MyProperty'.".
What am I doing wrong?
You can create arrays in XAML using x:Array, you still need to use it as a resource like Liz's answer.
<Window.Resources>
<x:Array Type="sys:String" x:Key="days">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</x:Array>
</Window.Resources>
<local:MyComboBox Height="23" MyProperty="{StaticResource days}" />
Is there any other reason you are subclassing the ComboBox beyond what you are doing here?
Because if the idea is to just provide a list of strings to the combobox then take a look at "Add collection or array to wpf resource dictionary"
As much as possible it would be better to let the combobox take care of itself - by using the ItemsSource property. So what we are doing in the linked example is providing a 'resource' containing your list of strings, and then passing this resource to the ItemsSource as follows:
<ComboBox ItemsSource="{StaticResource stringList}" />
Define a type like this:
public class StringList : List<string> { }
And then create a static resource
<Window.Resources>
<local:StringList x:Key="stringList">
<sys:String>Monday</sys:String>
<sys:String>Wednesday</sys:String>
<sys:String>Friday</sys:String>
</local:StringList >
</Window.Resources>
I hope that helps.
Edit: You could also change your DependencyProperty to use StringList instead of String[], then your dependency property will work as well.
<local:MyComboBox MyProperty="{StaticResource stringList}" Height="23" />
And then the dependencyProperty:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty",typeof(StringList),typeof(MyComboBox),new FrameworkPropertyMetadata(null, listChangedCallBack));
static void listChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
ComboBox combo = (ComboBox)property;
combo.ItemsSource= (IEnumerable)args.NewValue;
}
Then this would effectively do the same thing as binding directly to the ItemsSource. But the main thing if I understand you correctly, is just to get the Dependency property to work.
And, an extra example with enum:
namespace MyNamespace
{
public enum OrganizationType
{
Office365,
OnPremisses
}
}
xmlns:local="clr-namespace:MyNamespace"
<x:Array Type="local:OrganizationType" x:Key="OrganizationTypeArray">
<local:OrganizationType>Office365</local:OrganizationType>
<local:OrganizationType>OnPremisses</local:OrganizationType>
</x:Array>
I am exploring the Silverlight attached behaviors mechanism in order to use the Model-View-ViewModel pattern within my Silverlight applications. To start with, I am trying to get a simple Hello World working, but I am completely stuck in an error for which I'm not able to find a solution.
What I have right now is a page that just contains a button which should display a message when clicked. The click event is handled by using a class derived from Behavior, and the message is specified as a dependency property of the behavior itself. The problem comes when trying to bind the message property to a property on a viewmodel class used as the data context: I get an exeption in the call to InitializeComponent in the view.
Here is all the code I'm using, as you can see it is rather simple. First the markup of the main page and the view it contains:
MyPage
<UserControl x:Class="MyExample.MyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyExample"
>
<Grid x:Name="LayoutRoot">
<local:MyView/>
</Grid>
</UserControl>
MyView (the TextBlock is there just to check that the binding syntax is correct)
<UserControl x:Class="MyExample.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyExample"
Width="400" Height="300">
<StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="White">
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
</StackPanel.Resources>
<TextBlock Text="This button will display the following message:"/>
<TextBlock Text="{Binding MyMessage, Source={StaticResource MyResource}}" FontStyle="Italic"/>
<Button x:Name="MyButton" Content="Click me!">
<i:Interaction.Behaviors>
<local:MyBehavior Message="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</i:Interaction.Behaviors>
</Button>
</StackPanel>
</UserControl>
Now the code, there are two classes: one for the behavior and another one for the viewmodel:
MyViewmodel
public class MyViewmodel
{
public string MyMessage
{
get { return "Hello, world!"; }
}
}
MyBehavior
public class MyBehavior : Behavior<Button>
{
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message",
typeof(string), typeof(MyBehavior),
new PropertyMetadata("(no message)"));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += new RoutedEventHandler(AssociatedObject_Click);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Click -= new RoutedEventHandler(AssociatedObject_Click);
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Message);
}
}
Simple enough, but this code throws an AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43] (right at the start of the value being set for the Message property) exception when ran. I'm sure that I'm missing something, but what?
Additional information: if I remove the binding from the Message property on MyBehavior (that is, if I set its value to any static string), it works fine. Also, I'm targeting silverlight 3 RTW.
Thanks a lot!
UPDATE
It seems that unlike WPF, Silverlight does not support data binding on any object deriving from DependencyObject, but only on objects deriving from FrameworkElement. This is no the case for Behavior, hence binding does not work.
I have found a workaround here, in the form of something named surrogate binders. Basically you specify the element and property to be binded, as well as the value, as attributes of the FrameworkElement containing the non-FrameworkElement object.
UPDATE 2
The surrogate binder does not work when the FrameworkElement contains an Interaction.Behaviors sub-element.
I have found another solution here, and this one seems to work. This time, the trick used is a DeepSetter. You define one of such setters as a static resource on the containing StackPanel, and then reference the resource from the behavior. So in my example, we should expand the StackPanel resources section as follows:
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
<local:DeepSetter
x:Key="MyBehaviorSetter"
TargetProperty="Message"
BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>
...and modify the button's behavior declaration as follows:
<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>
UPDATE 3
Good news: data binding for any DependecyObject will be available on Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind
To get the DataBinding support the class should inherit from FrameworkElement.Hoping MSFT will give support in Silverlight 4
I have a class like this:
public class Stretcher : Panel {
public static readonly DependencyProperty StretchAmountProp = DependencyProperty.RegisterAttached("StretchAmount", typeof(double), typeof(Stretcher), null);
public static void SetStretchAmount(DependencyObject obj, double amount)
{
FrameworkElement elem = obj as FrameworkElement;
elem.Width *= amount;
obj.SetValue(StretchAmountProp, amount);
}
}
I can set the stretch amount property in XAML using the attribute syntax:
<UserControl x:Class="ManagedAttachedProps.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:map="clr-namespace:ManagedAttachedProps"
Width="400" Height="300">
<Rectangle Fill="Aqua" Width="100" Height="100" map:Stretch.StretchAmount="100" />
</UserControl>
and my rectangle is stretched, but I can't use property element syntax like this:
<UserControl x:Class="ManagedAttachedProps.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:map="clr-namespace:ManagedAttachedProps"
Width="400" Height="300">
<Rectangle Fill="Aqua" Width="100" Height="100">
<map:Stretcher.StretchAmount>100</map:Stretcher.StretchAmount>
</Rectangle>
</UserControl>
with the property element syntax my set block seems to be totally ignored (I can even put invalid double values in there), and the SetStretchAmount method is never called.
I know it's possible to do something like this, because VisualStateManager does it. I've tried using types other than double and nothing seems to work.
I think I figured this out, although I'm not totally sure I understand the reason why it works.
In order to get your example to work I had to create a custom type called Stretch with a property called StretchAmount. Once I did that and put that inside the property element tags it worked. Otherwise it wasn't called.
public class Stretch
{
public double StretchAmount { get; set; }
}
And the property changed to..
public static readonly DependencyProperty StretchAmountProp = DependencyProperty.RegisterAttached("StretchAmount", typeof(Stretch), typeof(Stretcher), null);
public static void SetStretchAmount(DependencyObject obj, Stretch amount)
{
FrameworkElement elem = obj as FrameworkElement;
elem.Width *= amount.StretchAmount;
obj.SetValue(StretchAmountProp, amount);
}
To get this to work in the scenario where you aren't using a property element you would need to create a custom type converter to allow this to work.
Hope this helps, even though it doesn't explain the why which I'm still trying to understand.
BTW - for a real brain teaser, take a look at the VisualStateManager in reflector. The dependency property and the setter for VisualStateGroups are both internal.
So Bryant's solution works, it does require a slight modification to the XAML:
<Rectangle Fill="Aqua" Width="100" Height="100" x:Name="the_rect">
<map:Stretcher.StretchAmount>
<map:Stretch StretchAmount="100" />
</map:Stretcher.StretchAmount>
</Rectangle>