DependencyProperty PropertyChangedCallback vs placing code directly into the setter - wpf

Was just wondering about DependencyProperties.
Usually I'm seeing this kind of coding standard when executing some code after a DependencyProperty has changed.
public int SomeProperty
{
get { return (int)GetValue(SomePropertyProperty); }
set { SetValue(SomePropertyProperty, value); }
}
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(int), typeof(MainWindow), new UIPropertyMetadata(new DependencyPropertyChangedEventHandler(OnSomePropertyChanged)));
private static void OnSomePropertyChanged(object obj, DependencyPropertyChangedEventArgs e)
{
//Some logic in here
}
But I don't think I've never seen this kind of implementation -
public int SomeProperty
{
get { return (int)GetValue(SomePropertyProperty); }
set
{
SetValue(SomePropertyProperty, value);
//Execute code in here
}
}
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));
Is this considered a bad practice?
Thanks!

This isn't just bad practice, this will actually result in incorrect behavior. When binding to dependency properties in XAML, the SetValue method will be called directly, not the setter. Basically, you can't guarantee that code there will even be executed.
Source: http://www.switchonthecode.com/tutorials/wpf-tutorial-introduction-to-dependency-properties
A little bit of a side note here - don't ever put anything but the
GetValue and SetValue calls inside the property wrapper. This is
because you never know if someone will set the property through the
wrapper, or straight through a SetValue call - so you don't want to
put any extra logic in the property wrapper. For example, when you set
the value of a dependency property in XAML, it will not use the
property wrapper - it will hit the SetValue call directly, bypassing
anything that you happened to put in the property wrapper.

Related

Dependency property value is not inherited

I declared a dependency property with FrameworkPropertyMetadataOptions.Inherits:
public static class DesignerItemStyles {
public static readonly DependencyProperty HeaderBackgroundProperty =
DependencyProperty.RegisterAttached(
"HeaderBackground", typeof(Brush), typeof(DesignerItemStyles),
new FrameworkPropertyMetadata(
Brushes.DesignerViewElementHeaderBackground,
FrameworkPropertyMetadataOptions.Inherits));
/* Below are Get & Set as usual */
}
It kind of works, but somehow not throughout the visual tree. Here is a screenshot showing ContentPresenter that inherit value from HeaderedDesignerItemChrome:
And now, a screenshot showing content of the ContentPresenter, and it does not inherit the value. Nor it is set to something else - it is a default value:
Any idea why?
Using this is not that straight forward since there are some rules that need to be followed to implement property with inheritable values. Here they are:
On parent, dependency property must be defined as attached property. You can still declare property getter/setter, but property must be attached. Here is simple declaration:
public static readonly DependencyProperty InheritedValueProperty =
DependencyProperty.RegisterAttached("InheritedValue",
typeof(int), typeof(MyClass), new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
public static int GetInheritedValue(DependencyObject target)
{
return (int)target.GetValue(InheritedValueProperty);
}
public static void SetInheritedValue(DependencyObject target, int value)
{
target.SetValue(InheritedValueProperty, value);
}
public int InheritedValue
{
get
{
return GetTimeSlotDuration(this);
}
set
{
SetTimeSlotDuration(this, value);
}
}
Child objects would define their instance of the property with inherited value using AddOwner. Following is the code that goes into say MyChildClass sample class:
public static readonly DependencyProperty InheritedValueProperty;
public int InheritedValue
{
get
{
return (int)GetValue(InheritedValueProperty);
}
set
{
SetValue(InheritedValueProperty, value);
}
}
static MyChildClass()
{
InheritedValueProperty =
MyClass.InheritedValueProperty.AddOwner(typeof(MyChildClass),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
}
The global default value is preserved and inheritance still works, if the single argument overload is used...
MyClass.InheritedValueProperty.AddOwner(typeof(MyChildClass));
Note that property is in child class declared as standard dependency property and that it specifies Inherit in meta-data options.
With setup like this now when MyChildClass in parented to MyClass visually or logically they will share the same property value automatically.
So technically, what you see in the Visual Tree is doing what you told it to do. It set the default value that you told it to and the inherited controls inherit from the value of the parent which is your ContentPresenter
Eliminating one of the two ContentPresenters (visible on both screenshots just above DesignerItemsPresenter) worked for me. I am inclined to believe that was a bug in WPF framework itself.

Custom object as a DependencyProperty

I have a custom class, MyPerson. All (relevant) properties implement INotifyPropertyChanged.
I created a UserControl to display it, and it all worked fine. Binding to properties like MyPerson.FirstName (a string) all work - they display and update (two way binding) as expected.
Now I want to do more complex stuff in the codebehind, so I wanted to create a DependencyProperty with a PropertyType of MyPerson, but I'm not sure how to construct the DependencyProperty, in particular the PropertyChangedCallback part.
Can this be done? How so?
Read on this article - Custom Dependency Properties
Something like -
public static readonly DependencyProperty MyPersonValueProperty =
DependencyProperty.Register( "MyPersonValue", typeof(MyPerson),
typeof(MyPersonControl), new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnPersonChanged) ) );
public MyPerson ThePerson
{
get { return (MyPerson)GetValue(MyPersonValueProperty); }
set { SetValue(MyPersonValueProperty, value); }
}
private static void OnPersonChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
// Property change code here
}

Dependency property callback does not work

I have the following code:
private static readonly DependencyProperty IDProperty = DependencyProperty.Register(
"ID", typeof(int), typeof(DetailDataControl), new PropertyMetadata(-1, new PropertyChangedCallback(IDChanged)));
public int ID
{
get { return (int)GetValue(IDProperty); }
set { SetValue(IDProperty, value); }
}
private static void IDChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Do something here!
}
I can see that when I change ID, the line SetValue(IPproperty is called), but it doesn't call the IDChanged.
Why?
Your code is correct, however PropertyChanged callback will not be called until it has changed. Try changing the property to two different values in consecutive lines of code and have a break point you can see that it's been hit. I believe it's set to -1 and hence it isn't called.
Make the DP public static readonly. When setting the value in XAML, the wrapper is not used, the DP is used directly. So, it has to be public.
But...apparently you are setting it from within code? In that case, i don't know what's wrong...but you can always try.
I don't know if this was ever solved or not but if you are setting the value in the XAML file that uses it, there are certain circumstances where the proceedural code default value will take precedent and it will never fire from being set in the XAML initially. So remove the default value of -1 so
private static readonly DependencyProperty IDProperty = DependencyProperty.Register(
"ID", typeof(int), typeof(DetailDataControl), new PropertyMetadata(-1, new PropertyChangedCallback(IDChanged)));
becomes
private static readonly DependencyProperty IDProperty = DependencyProperty.Register(
"ID", typeof(int), typeof(DetailDataControl), new PropertyMetadata( new PropertyChangedCallback(IDChanged)));

Attached Property and Binding

I'm creating an attached behavior in order to set a regular property of a class:
public class LookupHelper
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource", typeof(object), typeof(LookupHelper), new UIPropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MyControl;
if(control == null)
return;
control.ItemsSource = (IEnumerable)e.NewValue;
}
public static object GetItemsSource(GridColumn column)
{
return column.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(GridColumn column, object value)
{
column.SetValue(ItemsSourceProperty, value);
}
}
Here, ItemsSource property on MyControl is a regular property, so I can not bind it in Xaml, hence this attached behavior.
Now, when I use this attached property using string or objects it works and breakpoint I set is hit, but when I set it with Binding markup, it never runs. Why isn't this working?
<MyControl ctrl:LookupHelper.ItemsSource="DataSource"/>; //It works
<MyControl ctrl:LookupHelper.ItemsSource="{Binding Path=MyDataSource}"/>; //Does not work
What I need to do is to set the ItemsSource property to the value specified by the Binding.
In your Get and Set methods, you're defining the receiving object as GridColumn where it should be DependencyObject.
You might also want to change the type of your DP from object to IEnumerable since your casting to that in your change handler.
Can you please post the markup you are using? Also, If the actual property exists on an object and makes sense there then I think you should be using a regular dependency property on that object instead of an attached property on a helper class.
Edit
From MSDN:
The signature for the GetPropertyName accessor must be:
public static object GetPropertyName(object target)
and the signature for the SetPropertyName accessor must be:
public static void SetPropertyName(object target, object value)
In your case, is GridColumn the correct target type?

wpf how to tell when databinding has finished?

I've got a custom control which has a DependencyProperty MyAnimal - I'm binding an Animal Property on my ViewModel to the MyAnimal DependencyProperty.
I've stuck a TextBox on the Control so I can trigger an Event - whenever I trigger the event the MyAnimal property has been set - however if I put a break point on the Setter of the MyAnimal property it never gets fired!
I guess I'm missing something fundamental about WPF Dependency Properties/Binding?!
And so my question is, if I can't use the Setter how can I find out when its been set? If I put if I put a break point after InitializeComponent() its null and I had a look to see if theres an Event a can hook up to - DatabindingFinished or similar? but can't see what it would be ...
Can anyone assist please?
Thanks,
Andy
public partial class ControlStrip
{
public ControlStrip()
{
InitializeComponent();
}
public Animal MyAnimal
{
get
{
return (Animal)GetValue(MyAnimalProperty);
}
set
{
SetValue(MyAnimalProperty, value);
}
}
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip));
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var myAnimal = MyAnimal;
MessageBox.Show(myAnimal.Name);
}
}
The setter methods are never called by the runtime. They go directly to the DependencyProperty. You will need to add an additional argument to your call to RegisterAttached(). There you can add a PropertyChangedCallback.
Here is some sample code:
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip), new PropertyMetadata(AnimalChanged));
private static void AnimalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Do work here
}
The setter is only there for your use - you actually can leave the property off entirely, since DataBinding uses the actual DependencyProperty itself, not the CLR property.
If you need to see when the property changes, you will need to specify PropertyMetadata on your dependency property, and provide a PropertyChangedCallback.
For details, I recommend reading Dependency Property Metadata.

Resources