Search code examples
c#wpfuser-interfacemvvmuser-controls

DependencyProperty Set and Callback in my UserControl not happening


I am trying to create a simple UserControl in WPF for reusing in my other applications. It is a simple DateRangePicker. Some of the control's properties are bound to child UI elements and hence I implement INotifyPropertyChanged. My control.xaml.cs looks like below (only relevant portions)

public partial class DateRangePicker : UserControl, INotifyPropertyChanged
   {
      public DateRangePicker()
      {
         InitializeComponent();

         DataContext = this;
      }

      public event PropertyChangedEventHandler PropertyChanged;

      protected void RaisePropertyChangedEvent(string propertyName)
      {
         if (PropertyChanged != null)
         {
            var e = new PropertyChangedEventArgs(propertyName);
            PropertyChanged(this, e);
         }
      }


      public static readonly DependencyProperty InitialFromDateProperty =
         DependencyProperty.Register("InitialFromDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime),
      new PropertyChangedCallback(OnInitialDateRangeChanged)));

      public static readonly DependencyProperty InitialToDateProperty =
         DependencyProperty.Register("InitialToDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime),
      new PropertyChangedCallback(OnInitialDateRangeChanged)));

      public static readonly DependencyProperty SelectedFromDateProperty =
         DependencyProperty.Register("SelectedFromDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime), null));

      public static readonly DependencyProperty SelectedToDateProperty =
         DependencyProperty.Register("SelectedToDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime), null));


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

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

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

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

      private static void OnInitialDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
         DateRangePicker control = (DateRangePicker)d;
         control.RefreshLists();
      }

My Test application xaml file which hosts the control looks like below (some lines including namespaces have been removed):


<Window x:Class="WpfApp3.MainWindow"
        xmlns:ControlDateRangePicker ="clr-namespace:SurfServer.Apps.Common.UI"
        xmlns:local="clr-namespace:WpfApp3"
        Title="MainWindow" Height="414.831" Width="565.808">
    <Grid>
        <ControlDateRangePicker:DateRangePicker
                        InitialFromDate="{Binding InitialFromDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                        InitialToDate="{Binding InitialToDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                        SelectedFromDate ="{Binding SelectedFromDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                        SelectedToDate ="{Binding SelectedToDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                        HorizontalAlignment="Left" Margin="136,78,0,0" VerticalAlignment="Top" Width="347"/>
    </Grid>
</Window>

And my Test Application xaml.cs looks like this


public partial class MainWindow : Window
   {
      public MainWindow()
      {
         InitializeComponent();
         try
         {
            var viewModel = new SampleViewModel();
            DataContext = viewModel;
            viewModel.Initialize();
         }
         catch (Exception)
         {
            Close();
         }
      }
   }

My Test Application's ViewModel looks like below :

class SampleViewModel : ViewModelBase
   {

      private DateTime m_dtInitialFrom;
      public DateTime InitialFromDateVM
      {
         get => m_dtInitialFrom;
         set
         {
            m_dtInitialFrom = value;
            RaisePropertyChangedEvent(nameof(InitialFromDateVM));
         }
      }

      private DateTime m_dtInitialTo;
      public DateTime InitialToDateVM
      {
         get => m_dtInitialTo;
         set
         {
            m_dtInitialTo = value;
            RaisePropertyChangedEvent(nameof(InitialToDateVM));
         }
      }

      public DateTime SelectedFromDateVM
      {
         get;
         set;
      }

      public DateTime SelectedToDateVM
      {
         get;
         set;
      }

      public void Initialize()
      {
         InitialFromDateVM = new DateTime(DateTime.Now.Year - 1, DateTime.Now.Month, DateTime.Now.Day);
         InitialToDateVM = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
      }

As you can see, some of the properties in the test application's View model are bound to my control's (DateRangePicker) dependency properites. Now the problem I am facing is, though I am trying to set the Initial values in my Test Application's ViewModel (in the Initialize method), it looks like the binding does not work and I am not getting a callback in my control (in fact, I am unable to hit even the 'set' of the dependency property itself). What am I trying to do wrong here ?


Solution

  • You have to remove setting DataContext from DateRangePicker constructor. So the DataContext for your UserControl was not your ViewModel SampleViewModel but the UserControl itself.

    public partial class DateRangePicker : UserControl, INotifyPropertyChanged
    {
          public DateRangePicker()
          {
             InitializeComponent();
    
             //DataContext = this; REMOVE
          }
    }