Обнаружил очень странно поведение DP.
В чём суть.
Есть некий контрол в котором есть 2 свойства,
для примера X и X2.
При изменении свойства X вызывается d.CoerceValue(X2Property), после чего вызывается метод удержания свойства на нужном значении X2. То есть по сути, X2 всегда будет зависить от X1, и не важно какое мы значение ему задаем. Вроде всё просто.
Но когда я так сделал, обнаружил интересно поведение.
Если привязать поле X2 к обычному (не DP) свойству, то связывание работать не будет и источник значения не будет обновляется после изменения X2;
При этом связывание с другим DP работает отлично, после "принуждения" значения X2, оно обновится и в свойстве источника.
Вот небольшой пример:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
<Window x:Class="WpfApplication40.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication40="clr-namespace:WpfApplication40"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainModel}">
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<wpfApplication40:MyControl x:Name="MyControl" Grid.Row="1"
X2="{Binding X, Mode=TwoWay}"
Y2="{Binding Y, Mode=TwoWay}"
Background="Transparent"/>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<StackPanel Background="LightCoral">
<TextBlock Text="Element Binding"/>
<TextBlock Text="{Binding X,ElementName=MyControl}"/>
<TextBlock Text="{Binding Y,ElementName=MyControl}"/>
</StackPanel>
<StackPanel Background="LightGreen">
<TextBlock Text="DataContext Binding"/>
<TextBlock Text="{Binding X}"/>
<TextBlock Text="{Binding Y}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication40
{
public class MyControl : Control
{
protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
var position = e.GetPosition(this);
X = position.X;
Y = position.Y;
}
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(MyControl), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender, OnXChanged));
private static void OnXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(X2Property);
}
public double X2
{
get { return (double)GetValue(X2Property); }
set { SetValue(X2Property, value); }
}
public static readonly DependencyProperty X2Property =
DependencyProperty.Register("X2", typeof(double), typeof(MyControl), new PropertyMetadata(0.0, null, OnX2Coerce));
private static object OnX2Coerce(DependencyObject d, object basevalue)
{
var newValue = (double)d.GetValue(XProperty);
return newValue + 1;
}
public double Y
{
get { return (double)GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
public static readonly DependencyProperty YProperty =
DependencyProperty.Register("Y", typeof(double), typeof(MyControl), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender, OnYChanged));
private static void OnYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(Y2Property);
}
public double Y2
{
get { return (double)GetValue(Y2Property); }
set { SetValue(Y2Property, value); }
}
public static readonly DependencyProperty Y2Property =
DependencyProperty.Register("Y2", typeof(double), typeof(MyControl), new PropertyMetadata(0.0, null, OnY2Coerce));
private static object OnY2Coerce(DependencyObject d, object basevalue)
{
var value = (double)basevalue;
return value + 1;
}
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawRectangle(Background, null, new Rect(new Point(0, 0), RenderSize));
var pen = new Pen(Brushes.Red, 1);
drawingContext.DrawEllipse(Brushes.Blue, pen, new Point(X, Y), 10, 10);
base.OnRender(drawingContext);
}
}
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using WpfApplication40.Annotations;
namespace WpfApplication40
{
public class MainModel : INotifyPropertyChanged
{
private double _x;
private double _y;
public double X
{
set
{
if (value.Equals(_x)) return;
_x = value;
OnPropertyChanged("X");
}
get { return _x; }
}
public double Y
{
set
{
if (value.Equals(_y)) return;
_y = value;
OnPropertyChanged("Y");
}
get { return _y; }
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
согласен, что такой подход, не очень хороший. Особенно он плох удобочитаемостью, так как для стороннего разработчика будет совершенно не ясно, почему это свойство меняет свое значение и не принимает другие, при этом не являясь readonly.
Но мне просто интересно, с чем это связано?