Распространение силы по принуждению

tl; dr: Принудительные значения не распространяются по привязкам данных. Как я могу форсировать обновление через привязку данных, когда код позадине знаю другую сторону переплета?

м, используяCoerceValueCallback на свойство зависимости WPF и яЯ застрял в вопросе о том, что принуждать ценности неТ распространяется через привязки.

Window1.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace CoerceValueTest
{
    public class SomeControl : UserControl
    {
        public SomeControl()
        {
            StackPanel sp = new StackPanel();

            Button bUp = new Button();
            bUp.Content = "+";
            bUp.Click += delegate(object sender, RoutedEventArgs e) {
                Value += 2;
            };

            Button bDown = new Button();
            bDown.Content = "-";
            bDown.Click += delegate(object sender, RoutedEventArgs e) {
                Value -= 2;
            };

            TextBlock tbValue = new TextBlock();
            tbValue.SetBinding(TextBlock.TextProperty,
                               new Binding("Value") {
                                Source = this
                               });

            sp.Children.Add(bUp);
            sp.Children.Add(tbValue);
            sp.Children.Add(bDown);

            this.Content = sp;
        }

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
                                                                                              typeof(int),
                                                                                              typeof(SomeControl),
                                                                                              new PropertyMetadata(0, ProcessValueChanged, CoerceValue));

        private static object CoerceValue(DependencyObject d, object baseValue)
        {
            if ((int)baseValue % 2 == 0) {
                return baseValue;
            } else {
                return DependencyProperty.UnsetValue;
            }
        }

        private static void ProcessValueChanged(object source, DependencyPropertyChangedEventArgs e)
        {
            ((SomeControl)source).ProcessValueChanged(e);
        }

        private void ProcessValueChanged(DependencyPropertyChangedEventArgs e)
        {
            OnValueChanged(EventArgs.Empty);
        }

        protected virtual void OnValueChanged(EventArgs e)
        {
            if (e == null) {
                throw new ArgumentNullException("e");
            }

            if (ValueChanged != null) {
                ValueChanged(this, e);
            }
        }

        public event EventHandler ValueChanged;

        public int Value {
            get {
                return (int)GetValue(ValueProperty);
            }
            set {
                SetValue(ValueProperty, value);
            }
        }
    }

    public class SomeBiggerControl : UserControl
    {
        public SomeBiggerControl()
        {
            Border parent = new Border();
            parent.BorderThickness = new Thickness(2);
            parent.Margin = new Thickness(2);
            parent.Padding = new Thickness(3);
            parent.BorderBrush = Brushes.DarkRed;

            SomeControl ctl = new SomeControl();
            ctl.SetBinding(SomeControl.ValueProperty,
                           new Binding("Value") {
                            Source = this,
                            Mode = BindingMode.TwoWay
                           });
            parent.Child = ctl;

            this.Content = parent;
        }

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
                                                                                              typeof(int),
                                                                                              typeof(SomeBiggerControl),
                                                                                              new PropertyMetadata(0));

        public int Value {
            get {
                return (int)GetValue(ValueProperty);
            }
            set {
                SetValue(ValueProperty, value);
            }
        }
    }

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }
}

Window1.xaml


    
        
        
        
    

то есть два пользовательских элемента управления, один вложенный в другой, а внешний - в окне. Внутренний пользовательский элемент управления имеетValue свойство зависимости, которое связано сValue свойство зависимости внешнего управления. В окнеTextBox.Text собственность связана сValue свойство внешнего контроля.

Внутренний контроль имеетCoerceValueCallback зарегистрирован с егоValue свойство, эффект которого заключается в том, что этоValue Свойству могут быть назначены только четные числа.

Обратите внимание, что этот код упрощен для демонстрационных целей. Реальная версия неинициализировать что-либо в конструкторе; два элемента управления на самом деле имеют шаблоны элементов управления, которые делают все, чтосделано в соответствующих конструкторах здесь. То есть в реальном коде внешний контроль нене знаю внутреннего контроля.

При записи четного числа в текстовое поле и изменении фокуса (например, путем фокусировки фиктивной кнопки под текстовым полем), обаValue свойства должным образом обновляются. При записи нечетного числа в текстовое поле, однако,Value свойство внутреннего контроля нет, в то время какValue свойство внешнего контроля, а такжеTextBox.Text свойство, показать нечетное число.

Мой вопрос:Как я могу принудительно обновить в текстовом поле (и в идеале также во внешнем элементе управленияValue собственность, пока мына это)?

я нашелТАК вопрос по той же проблеме, но не делаетЯ действительно не могу найти решение. Это намекает на использование обработчика события, измененного свойством, для сброса значения, но, насколько я вижу, это означало бы дублирование кода оценки на внешний элемент управления ... что на самом деле не жизнеспособно, поскольку мой действительный код оценки полагается на некоторые Информация в основном только известна (без особых усилий) для внутреннего контроля.

Более того,этот пост предлагает вызватьUpdateTarget на переплет вTextBox.Text вCoerceValueCallback, но, во-первых, как подразумевается выше, мой внутренний контроль не может иметь никаких знаний о текстовом поле, а во-вторых, мне, вероятно, придется позвонитьUpdateSource сначала на привязкуValue свойство внутреннего контроля. Я нене вижу, где это сделать, как в рамкахCoerceValue метод, приведенное значение еще не установлено (поэтомуслишком рано, чтобы обновить привязку), в то время как в случае, если значение сбрасываетсяCoerceValueзначение свойства останется таким, каким оно было, следовательно, обратный вызов, измененный свойством, не будет вызван (как также подразумевается вэто обсуждение).

Одним из возможных обходных путей, о котором я подумал, была замена свойства зависимости вSomeControl с обычной собственностью иINotifyPropertyChanged реализация (так что я могу вручную вызватьPropertyChanged событие, даже если значение было приведено). Однако это будет означать, что я больше не могу объявлять привязку к этому свойству, поэтомуне очень полезное решение.

Ответы на вопрос(1)

Ваш ответ на вопрос