Dependency property callback does not work - wpf

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)));

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
}

DependencyProperty PropertyChangedCallback vs placing code directly into the setter

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.

Public vs Private AttachedProperties

Where does it make sense to have AttachedProperties as private vs public?
Usually it is define as (example):
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(Click),
new PropertyMetadata(OnSetCommandCallback));
But I have also seen examples where some properties are private static readonly...
What are the consequences if I change the above CommandProperty to private now? It seems to be still available in my XAML intellisense if I do that. What am I missing here?
The difference is that you won't be able to access the DependencyProperty from outside of the class. This can make sense if the static Get and Set methods are private as well, (in an attached behavior where you need to store some behavior local data for example) but not otherwise (and I don't think I've ever seen this with public Get and Set).
An example of when you want to use the DependencyProperty is DependencyPropertyDescriptor. With a public DependencyProperty you can do the following
DependencyPropertyDescriptor de =
DependencyPropertyDescriptor.FromProperty(Click.CommandProperty, typeof(Button));
de.AddValueChanged(button1, delegate(object sender, EventArgs e)
{
// Some logic..
});
But if the DependencyProperty is private, the above code won't work.
However, the following will work fine for both a public and private DependencyProperty (if the static Get and Set methods are public) since the owner class can access the private DependencyProperty. This also goes for Bindings and values set through Xaml where GetValue and SetValue are called directly.
Click.SetCommand(button, ApplicationCommands.Close);
ICommand command = Click.GetCommand(button);
If you look through the framework you will notice that all of the public attached properties have a public DependencyProperty, for example Grid.RowProperty and Storyboard.TargetNameProperty. So if the attached property is public, use a public DependencyProperty

How can I change the default value of an inherited dependency property?

How can I change the default value for an inherited dependency property? In our case, we've created a subclass of Control which by default has its Focusable set to 'true'. We want our subclass to have the default of 'false'.
What we've been doing is simply setting it to 'false' in the constructor, but if someone uses ClearValue, it goes back to the default, not the value set in the constructor.
Here's what I'm currently doing to achieve this (This is a test control with a DP of 'Foo' for an example.) I'm not a fan of the 'new' to hide the property although thanks to AddOwner, it does point to the same shared instance so I guess it's ok. It looks like it inherits all the other metadata values as well so that's good. Just wondering if this is correct?
public class TestControlBase : Control
{
public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
"Foo",
typeof(int),
typeof(TestControlBase),
new FrameworkPropertyMetadata(4) // Original default value
);
public int Foo
{
get { return (int)GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
}
public class TestControl : TestControlBase
{
public static readonly new DependencyProperty FooProperty = TestControlBase.FooProperty.AddOwner(
typeof(TestControl),
new FrameworkPropertyMetadata(67) // New default for this subclass
);
}
Mark
UPDATE...
I think this is even better as it eliminates the 'new' call. You still access it via the FooProperty on the base class since this uses AddOwner. As such, it's technically the same one.
public class TestControl : TestControlBase
{
// Note this is private
private static readonly DependencyProperty AltFooProperty = TestControlBase.FooProperty.AddOwner(
typeof(TestControl),
new FrameworkPropertyMetadata(67) // New default for this subclass
);
}
The correct way to override a base class's property is:
static TestControl() {
FooProperty.OverrideMetadata(
typeof(TestControl),
new FrameworkPropertyMetadata(67)
);
}
EDIT:
AddOwner is meant to share the same DependencyProperty across types that are not related (i.e. the TextProperty of TextBox and TextBlock).

Resources