数据绑定不适用于具有依赖性 属性 的用户控件的自定义 属性(输出 window 是干净的)

Data Binding not working for User Control's custom Property with Dependency Property (Output window is clean)

注意:与 Whosebug 上的其他类似文章一样,我对用户控件内部的控件没有问题,我对用户控件本身的 属性 有疑问。我正在制作一个基于 Canvas 的自定义控件,具有依赖性 属性(使用 propdb 模板):

public sealed partial class PresentationViewer : Canvas
{

    #region Properties

    public ISlide PresentationSlide
    {
        get
        {
            Debug.WriteLine("Get PresentationSlide");
            return (ISlide)GetValue(PresentationSlideProperty);
        }
        set
        {
            Debug.WriteLine("Set PresentationSlide");
            SetValue(PresentationSlideProperty, value);
            this.ShowSlideContent();
        }
    }

    // Using a DependencyProperty as the backing store for PresentationSlide.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PresentationSlideProperty =
        DependencyProperty.Register(nameof(PresentationSlide), typeof(ISlide), typeof(PresentationViewer), new PropertyMetadata(null));

    #endregion

    // Other codes...
}

在我的页面中,我使用控件并绑定 属性:

    <views:PresentationViewer x:Name="PresentationViewer" PresentationSlide="{Binding CurrentSlide, Mode=TwoWay}" />

我是这样设置主页的 DataContext:

    public MainPage()
    {
        this.InitializeComponent();

        this.ViewModel = new MainPageViewModel();
        this.DataContext = this.ViewModel;
    }

这是我的代码 MainPageViewModel:

public class MainPageViewModel : INotifyPropertyChanged
{

    // Other codes...

    public ISlide CurrentSlide
    {
        get
        {
            return this.CurrentPresentation?.Slides[this.CurrentSlideIndex];
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OpenSlide(int index)
    {
        if (index < 0)
        {
            index = 0;
        }

        if (index > this.TotalSlides - 1)
        {
            index = this.TotalSlides - 1;
        }

        this.currentSlideIndexField = index;

        this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.CurrentSlideIndex)));

        System.Diagnostics.Debug.WriteLine("Current Slide notified");
        this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.CurrentSlide)));

        this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.PageCounter)));
    }

}

请注意我打印 CurrentSlide 属性 更改通知的行。但是,没有调用用户控件 属性 的 setter 或 getter。这是调用 OpenSlide 时的输出(输出自程序开始):

绑定已经处于双向模式。我页面中的其他控件(标签等)也收到通知并更改了它们的内容,例如页面计数器,所以我想这也不是 ViewModel 的问题。我的自定义控件 class 中是否缺少某些内容?我怎样才能使绑定工作?

XAML Loading and Dependency Properties 中所述,依赖项 属性 的 CLR 包装器可能不会被调用,因此您的断点未命中且 ShowSlideContent 方法未执行。相反,框架直接调用依赖项 属性 的 GetValueSetValue 方法。

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.

为了对更改后的 属性 值做出反应,您必须使用 属性 元数据注册 PropertyChangedCallback

public ISlide PresentationSlide
{
    get { return (ISlide)GetValue(PresentationSlideProperty); }
    set { SetValue(PresentationSlideProperty, value); }
}

public static readonly DependencyProperty PresentationSlideProperty =
    DependencyProperty.Register(
        nameof(PresentationSlide),
        typeof(ISlide),
        typeof(PresentationViewer),
        new PropertyMetadata(null, PresentationSlidePropertyChanged));

private static void PresentationSlidePropertyChanged(
    DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    ((PresentationViewer)o).ShowSlideContent();
}

或者,使用 lambda 表达式:

public static readonly DependencyProperty PresentationSlideProperty =
    DependencyProperty.Register(
        nameof(PresentationSlide),
        typeof(ISlide),
        typeof(PresentationViewer),
        new PropertyMetadata(null,
            (o, e) => ((PresentationViewer)o).ShowSlideContent()));