简体   繁体   中英

Implementing two-way binding in WPF with a DateTime DependencyProperty

I've built a WPF user control that contains, among other controls, a DatePicker. My application is written with the MVVM design pattern. I want to be able to insert my user control into a view and bind the user control's DatePicker's SelectedDate property to a property of DateTime on my view's ViewModel. I want the default value shown to be the value stored on my view's viewmodel, and I want changes to the date through interaction with the DatePicker to update my view's viewmodel DateTime property.

I'm successfully seeing the proper bound value displayed in the DatePicker control, but when I change the date my view's viewmodel's DateTime property setter is not firing.

This is what I have for my user control's code-behind:

public partial class AgeDiscountUserControl : UserControl
{
    public AgeDiscountUserControl()
    {
        InitializeComponent();
    }

    public DateTime DateTime
    {
        get { return (DateTime)GetValue(DateTimeProperty); }
        set { SetValue(DateTimeProperty, value); }
    }

    public static readonly DependencyProperty DateTimeProperty =
        DependencyProperty.Register("DateTime", typeof(DateTime), typeof(AgeDiscountUserControl));
}

And in my user control's XAML, I have:

...
xmlns:my="clr-namespace:jkshay.UserControls"
...
<DatePicker Grid.Row="0" SelectedDate="{Binding DateTime, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType=my:AgeDiscountUserControl}}" DockPanel.Dock="Right"/>
...

In my view's viewmodel (which implements INotifyPropertyChanged), I've a DateTime property to which I'd like to bind my user control's DatePicker's SelectedDate property.

...
private DateTime birthdate;
public DateTime Birthdate
{
    get { return birthdate; }    
    set
    {
        if(birthdate != value)
        {  
            birthdate = value;
            NotifyPropertyChanged(() => Birthdate);
        }
    }
}
...

Finally, in my view's XAML, my AgeDiscountUserControl's binding is:

...
<uc:AgeDiscountUserControl DateTime="{Binding Birthdate}"/>
...

As stated previously, the proper value is initially displayed in the user control's DatePicker, but changes made by the DatePicker don't affect the bound property.

Am I missing something here, or do I simply have a complete misunderstanding of DependencyProperties?

I should mention that if I insert a DatePicker in my view and bind it to my viewmodel's Birthdate property, it works as expected.

As you have already figured out, the Binding must be two-way to ensure the source is updated when the target control's value changes. But you need not do that explicitly: you may want to make two-way binding the default for your DateTime property. You can do this by specifying a flag when you register your dependency property:

public static readonly DependencyProperty DateTimeProperty =
    DependencyProperty.Register(
        "DateTime",
        typeof(DateTime),
        typeof(AgeDiscountUserControl),
        new FrameworkPropertyMetadata(
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault // <--
        )
    );

On Sinatr's suggestion, I added a Mode=TwoWay setting to my view's XAML, and everything now works as expected.

Updated pertinent XAML is:

<uc:AgeDiscountUserControl DateTime="{Binding Birthdate, Mode=TwoWay}"/>

I now see my Birthdate setter firing upon interaction with the user control. Thanks for the suggestion, Sinatr.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM