当依赖 属性 接收到 xaml 变化时的回调

Callback when dependency property recieves xaml change

当我在运行期间设置IsClosed的值时,OnIsClosedChanged()被称为正常。 但是,Designer 设置了 属性 的值但没有调用 OnIsClosedChanged()

public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));

public bool IsClosed {
    get {
        return (bool)this.GetValue(IsClosedProperty);
    }
    set {
        if ((bool)this.GetValue(IsClosedProperty) == value)
            return;

        this.SetValue(IsClosedProperty, value);
        OnIsClosedChanged();
    }
}



private void OnIsClosedChanged() {
    _rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}

显然 IsClosed 未被设计者修改,只有 IsClosedProperty 收到 xaml 更改。
我的问题是:如何在Designer中修改值后运行IsClosed。或者至少为非 运行 时间更改添加一些逻辑。

现在自己找到了答案。 ValidateValueCallback 非常接近! (正如 Alex K 所指出的那样)但它是一个静态方法,我没有得到任何对已更改实例的引用。关键是在 FrameworkPropertyMetadata 中使用 PropertyChangedCallback,它也是传递给 Property.Register 方法的参数。
参见:

public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnIsClosedChangedPCC)));

        public bool IsClosed {
            get {
                return (bool)this.GetValue(IsClosedProperty);
            }
            set {
                this.SetValue(IsClosedProperty, value);
                OnIsClosedChanged();
            }
        }



        private static void OnIsClosedChangedPCC(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            GroupBox current = (GroupBox)d;
            current.IsClosed = current.IsClosed;
        }



        private void OnIsClosedChanged() {
            _rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
        }

现在将触发 OnIsClosedChangedIsClosedValue 重新设置为 运行。
谢谢你们的帮助!

您必须使用 属性 元数据注册 PropertyChangedCallback

原因是 XAML 中或通过绑定或其他一些来源设置的依赖属性不会调用 CLR 包装器(setter 方法)。 MSDN 上的 XAML Loading and Dependency Properties 文章解释了原因:

For implementation reasons, it is computationally less expensive to identify a property as a dependency property and access the property system SetValue method to set it, rather than using the property wrapper and its setter.

...

Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.

您的代码应如下所示:

public static readonly DependencyProperty IsClosedProperty =
    DependencyProperty.Register(
        "IsClosed", typeof(bool), typeof(GroupBox),
        new FrameworkPropertyMetadata(false,
            FrameworkPropertyMetadataOptions.AffectsRender,
            (o, e) => ((GroupBox)o).OnIsClosedChanged()));

public bool IsClosed
{
    get { return (bool)GetValue(IsClosedProperty); }
    set { SetValue(IsClosedProperty, value); }
}

private void OnIsClosedChanged()
{
    _rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}