I have the following in a UWP app. Notice that my Element descends from DependencyObject, not from a Control, Panel, etc. But when I call the test method MessWithBindings(), the bindings are not working. The PropertyChanged method does fire, but the PropertyChangedEventHandler is null, so it doesn't do anything. Am I supposed to directly setup the event handler somehow? Or is there another call I'm missing that creates it?
Incidentally, I have FrameworkElement versions of the BindWidth/BindHeight methods, and those work just fine.
class WindowsElement: DependencyObject, INotifyPropertyChanged
{
public WindowsElement()
{
}
private double _Width { get; set; }
private double _Height { get; set; }
public double Width
{
get
{
return _Width;
}
set
{
if (_Width != value)
{
_Width = value;
OnPropertyChanged("Width");
}
}
}
public double Height
{
get
{
return _Height;
}
set
{
if (_Height != value)
{
_Height = value;
OnPropertyChanged("Height");
}
}
}
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(
"Width",
typeof(double),
typeof(WindowsElement),
null);
public static readonly DependencyProperty HeightProperty = DependencyProperty.Register(
"Height",
typeof(double),
typeof(WindowsElement),
null);
private double _ActualWidth { get; set; }
public double ActualWidth {
get
{
return _ActualWidth;
}
set
{
if (_ActualWidth != value)
{
_ActualWidth = value;
OnPropertyChanged("ActualWidth");
}
}
}
private double _ActualHeight { get; set; }
public double ActualHeight {
get
{
return _ActualHeight;
}
set
{
if (_ActualHeight != value)
{
_ActualHeight = value;
OnPropertyChanged("ActualHeight");
}
}
}
public static readonly DependencyProperty ActualWidthProperty = DependencyProperty.Register(
"ActualWidth",
typeof(double),
typeof(WindowsElement),
null);
public static readonly DependencyProperty ActualHeightProperty = DependencyProperty.Register(
"ActualHeight",
typeof(double),
typeof(WindowsElement),
null);
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
internal void SetBinding(DependencyProperty property, Binding b)
{
BindingOperations.SetBinding(this, property, b);
}
public static void MessWithBindings()
{
WindowsElement we1 = new WindowsElement();
WindowsElement we2 = new WindowsElement();
we1.BindWidth(we2);
we1.BindHeight(we2);
we2.Width = 12;
we2.ActualWidth = 13;
we2.ActualHeight = 666;
CommonDebug.LogLine(we1, we1.Width, we1.Height, we1.ActualWidth, we1.ActualHeight, we2, we2.ActualWidth, we2.ActualHeight);
CommonDebug.DoNothing();
}
}
internal static class WindowsElementAdditions
{
public static void BindWidth(this WindowsElement bindMe, WindowsElement toMe)
{
Binding b = new Binding();
b.Mode = BindingMode.OneWay;
b.Source = toMe.ActualWidth;
bindMe.SetBinding(WindowsElement.WidthProperty, b);
}
public static void BindHeight(this WindowsElement bindMe, WindowsElement toMe)
{
Binding b = new Binding();
b.Mode = BindingMode.OneWay;
b.Source = toMe.ActualHeight;
bindMe.SetBinding(WindowsElement.HeightProperty, b);
}
}
The property wrapper of a dependency property must call the DependencyObject's GetValue
and SetValue
methods, e.g.
class WindowsElement : DependencyObject
{
public static readonly DependencyProperty WidthProperty =
DependencyProperty.Register(
"Width", typeof(double), typeof(WindowsElement), null);
public double Width
{
get { return (double)GetValue(WidthProperty); }
set { SetValue(WidthProperty, value); }
}
}
and it must not call anything else than that. When the property is set by a Binding (or Style Setter, an a few other sources), the framework does not call the property wrapper, but directly calls SetValue
.
In order to get internally notified about a changed property value, the class should register a PropertyChangedCallback
via PropertyMetadata
public static readonly DependencyProperty WidthProperty =
DependencyProperty.Register(
"Width", typeof(double), typeof(WindowsElement),
new PropertyMetadata((double)0, WidthPropertyChanged));
private static void WidthPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WindowsElement element = (WindowsElement)o;
double width = (double)e.NewValue;
// update element here if necessary
}
Dependency properties already implement a mechanism that notifies listeners (e.g. Bindings) about changed property values, hence there is no need to implement INotifyPropertyChanged
here.