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

时间:2016-05-23 04:59:16

标签: c# wpf data-binding custom-controls

注意:我在用户控件中没有像StackOverflow上的其他类似文章一样的问题,我正在使用用户控件本身的属性。我正在基于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属性更改通知的行。但是,不会调用User Control的属性的setter或getter。以下是调用OpenSlide时的输出(输出自程序开始以来):

enter image description here

Binding已经处于双向模式。我的页面中的其他控件(标签等)也会被通知并更改其内容,例如Page计数器,所以我想这也不是ViewModel问题。我在自定义控件类中遗漏了什么吗?如何使Binding工作?

1 个答案:

答案 0 :(得分:2)

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

  

因为当前的WPF实现了XAML处理器的行为   对于属性设置完全绕过包装器,你不应该   将任何其他逻辑放入包装器的set定义中   您的自定义依赖属性。如果你把这样的逻辑放在集合中   定义,然后在属性时不执行逻辑   在XAML而不是代码中设置。

为了对更改的属性值做出反应,您必须使用属性元数据注册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()));