Data Binding not working for User Control's cu

2020-02-16 03:50发布

Note: I am not having problem with controls inside User Control like other similar articles on StackOverflow, I am having with a Property of the User Control itself. I am making a Custom Control based on Canvas, with a Dependency Property (using propdb template):

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...
}

In my Page, I use the control and bind that property:

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

This is how I set the Page's DataContext:

    public MainPage()
    {
        this.InitializeComponent();

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

And this is the code of my 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)));
    }

}

Notice the line where I print the notification of CurrentSlide property change. However, no setter or getter of the User Control's property is called. Here is the output when OpenSlide is called (the output is since the beginning of the program):

enter image description here

The Binding is already in two-way mode. And other control in my page (Label, etc...) are also notified and changed their content, such as the Page counter, so I guess it is not the ViewModel problem too. Am I missing something in my Custom Control class? How can I make the Binding work?

1条回答
\"骚年 ilove
2楼-- · 2020-02-16 04:36

As explained in XAML Loading and Dependency Properties, the CLR wrapper of a dependency property may not be called, so your breakpoints aren't hit and the ShowSlideContent method isn't executed. Instead, the framework directly calls the dependency property's GetValue and SetValue methods.

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.

In order to react on changed property values, you'll have to register a PropertyChangedCallback with property metadata:

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();
}

Or, with a lambda expression:

public static readonly DependencyProperty PresentationSlideProperty =
    DependencyProperty.Register(
        nameof(PresentationSlide),
        typeof(ISlide),
        typeof(PresentationViewer),
        new PropertyMetadata(null,
            (o, e) => ((PresentationViewer)o).ShowSlideContent()));
查看更多
登录 后发表回答