Информация об изменениях

Сообщение Порядок вызова Callback в DependencyProperty от 30.12.2016 11:34

Изменено 30.12.2016 11:40 Cynic

Решил проверить порядок вызова Callback'ов для DependencyProperty следующим кодом:
    public class CustomControl : UserControl
    {
        public static readonly DependencyProperty CustomPropertyProperty;

        public static event EventHandler PropertyChangedCallbackIsRised;
        public static event EventHandler CoerceValueCallbackIsRised;
        public static event EventHandler ValidateValueCallbackIsRised;

        static CustomControl()
        {
            var metadata = new FrameworkPropertyMetadata(
                10, CustomPropertyChangedValuCallback, CustomPropertyCoerceValueCallback);

            CustomPropertyProperty = DependencyProperty.Register(
                "MyProperty", typeof(int), typeof(CustomControl), metadata, CustomPropertyValidateValueCallback);
        }

        private static void CustomPropertyChangedValuCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyChangedCallbackIsRised?.Invoke(null, null);
        }

        private static object CustomPropertyCoerceValueCallback(DependencyObject d, object baseValue)
        {
            CoerceValueCallbackIsRised?.Invoke(null, null);
            return baseValue;
        }

        private static bool CustomPropertyValidateValueCallback(object value)
        {
            ValidateValueCallbackIsRised?.Invoke(null, null);
            return true;
        }

        public int CustomProperty
        {
            get { return (int)GetValue(CustomPropertyProperty); }
            set { SetValue(CustomPropertyProperty, value); }
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            CustomControl.PropertyChangedCallbackIsRised += CustomControl_PropertyChangedCallbackIsRised;
            CustomControl.CoerceValueCallbackIsRised += CustomControl_CoerceValueCallbackIsRised;
            CustomControl.ValidateValueCallbackIsRised += CustomControl_ValidateValueCallbackIsRised;
        
        // В XAML разметке есть TextBox txtInfo в который выводится инфа
        
            var customControl = new CustomControl();
            txtInfo.Text = "Deafult value : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 100;
            txtInfo.Text += "New value1 : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 1000;
            txtInfo.Text += "New value2 : " + customControl.CustomProperty;
        }

        private void CustomControl_ValidateValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "ValidateValueCallback\r\n";
        }

        private void CustomControl_CoerceValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "CoerceValueCallback\r\n";
        }

        private void CustomControl_PropertyChangedCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "PropertyChangedCallback\r\n";
        }
    }

Получил вывод:

Deafult value : 10
Решил проверить порядок вызова Callback'ов для DependencyProperty следующим кодом:
    public class CustomControl : UserControl
    {
        public static readonly DependencyProperty CustomPropertyProperty;

        public static event EventHandler PropertyChangedCallbackIsRised;
        public static event EventHandler CoerceValueCallbackIsRised;
        public static event EventHandler ValidateValueCallbackIsRised;

        static CustomControl()
        {
            var metadata = new FrameworkPropertyMetadata(
                10, CustomPropertyChangedValuCallback, CustomPropertyCoerceValueCallback);

            CustomPropertyProperty = DependencyProperty.Register(
                "MyProperty", typeof(int), typeof(CustomControl), metadata, CustomPropertyValidateValueCallback);
        }

        private static void CustomPropertyChangedValuCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyChangedCallbackIsRised?.Invoke(null, null);
        }

        private static object CustomPropertyCoerceValueCallback(DependencyObject d, object baseValue)
        {
            CoerceValueCallbackIsRised?.Invoke(null, null);
            return baseValue;
        }

        private static bool CustomPropertyValidateValueCallback(object value)
        {
            ValidateValueCallbackIsRised?.Invoke(null, null);
            return true;
        }

        public int CustomProperty
        {
            get { return (int)GetValue(CustomPropertyProperty); }
            set { SetValue(CustomPropertyProperty, value); }
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            CustomControl.PropertyChangedCallbackIsRised += CustomControl_PropertyChangedCallbackIsRised;
            CustomControl.CoerceValueCallbackIsRised += CustomControl_CoerceValueCallbackIsRised;
            CustomControl.ValidateValueCallbackIsRised += CustomControl_ValidateValueCallbackIsRised;
        
        // В XAML разметке есть TextBox txtInfo в который выводится инфа
        
            var customControl = new CustomControl();
            txtInfo.Text = "Deafult value : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 100;
            txtInfo.Text += "New value1 : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 1000;
            txtInfo.Text += "New value2 : " + customControl.CustomProperty;
        }

        private void CustomControl_ValidateValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "ValidateValueCallback\r\n";
        }

        private void CustomControl_CoerceValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "CoerceValueCallback\r\n";
        }

        private void CustomControl_PropertyChangedCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "PropertyChangedCallback\r\n";
        }
    }

Получил вывод:

Deafult value : 10
ValidateValueCallback
CoerceValueCallback
PropertyChangedCallback
New value1 : 100
ValidateValueCallback
CoerceValueCallback
PropertyChangedCallback
New value2 : 1000

Вопрос почему? В книге "Pro WPF 4.5 in С#" Matthew MacDonald'а утверждается, что порядок должен быть таким:

CoerceValueCallback
ValidateValueCallback
PropertyChangedCallback

Отсюда два вопроса:
1) Почему между значениями 10 и 100 ValidateValueCallback вылетает два раза?
2) Почему между значениями 100 и 1000 порядок другой?
Порядок вызова Callback в DependencyProperty
Решил проверить порядок вызова Callback'ов для DependencyProperty следующим кодом:
    public class CustomControl : UserControl
    {
        public static readonly DependencyProperty CustomPropertyProperty;

        public static event EventHandler PropertyChangedCallbackIsRised;
        public static event EventHandler CoerceValueCallbackIsRised;
        public static event EventHandler ValidateValueCallbackIsRised;

        static CustomControl()
        {
            var metadata = new FrameworkPropertyMetadata(
                10, CustomPropertyChangedValuCallback, CustomPropertyCoerceValueCallback);

            CustomPropertyProperty = DependencyProperty.Register(
                "MyProperty", typeof(int), typeof(CustomControl), metadata, CustomPropertyValidateValueCallback);
        }

        private static void CustomPropertyChangedValuCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyChangedCallbackIsRised?.Invoke(null, null);
        }

        private static object CustomPropertyCoerceValueCallback(DependencyObject d, object baseValue)
        {
            CoerceValueCallbackIsRised?.Invoke(null, null);
            return baseValue;
        }

        private static bool CustomPropertyValidateValueCallback(object value)
        {
            ValidateValueCallbackIsRised?.Invoke(null, null);
            return true;
        }

        public int CustomProperty
        {
            get { return (int)GetValue(CustomPropertyProperty); }
            set { SetValue(CustomPropertyProperty, value); }
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            CustomControl.PropertyChangedCallbackIsRised += CustomControl_PropertyChangedCallbackIsRised;
            CustomControl.CoerceValueCallbackIsRised += CustomControl_CoerceValueCallbackIsRised;
            CustomControl.ValidateValueCallbackIsRised += CustomControl_ValidateValueCallbackIsRised;
        
        // В XAML разметке есть TextBox txtInfo в который выводится инфа
        
            var customControl = new CustomControl();
            txtInfo.Text = "Deafult value : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 100;
            txtInfo.Text += "New value1 : " + customControl.CustomProperty + "\r\n";
            customControl.CustomProperty = 1000;
            txtInfo.Text += "New value2 : " + customControl.CustomProperty;
        }

        private void CustomControl_ValidateValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "ValidateValueCallback\r\n";
        }

        private void CustomControl_CoerceValueCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "CoerceValueCallback\r\n";
        }

        private void CustomControl_PropertyChangedCallbackIsRised(object sender, EventArgs e)
        {
            txtInfo.Text += "PropertyChangedCallback\r\n";
        }
    }


Получил вывод:

Deafult value : 10
ValidateValueCallback
CoerceValueCallback
PropertyChangedCallback
New value1 : 100
ValidateValueCallback
CoerceValueCallback
PropertyChangedCallback
New value2 : 1000

При этом в книге "Pro WPF 4.5 in С#" Matthew MacDonald'а утверждается, что порядок должен быть таким:

CoerceValueCallback
ValidateValueCallback
PropertyChangedCallback

Отсюда два вопроса:
1) Почему между значениями 10 и 100 ValidateValueCallback вылетает два раза?
2) Почему между значениями 100 и 1000 порядок другой?