Search code examples
wpfdata-bindingmahapps.metro

WPF: Data binding to common property across views


I have a WPF application, where I want to keep a centralized date selection. I want to allow the date to set through one screen, and update it on others. Below is the common service,

public interface IDateService
{   
    public DateTime ScheduledDate { get; set; }
}

public sealed class DateService :  ObservableObject, IDateService
{
    private DateTime _scheduledDate = DateTime.Now.AddDays(1);

    public DateTime ScheduledDate
    {
        get => _scheduledDate;
        set
        {
            SetProperty(ref _scheduledDate, value);
        }
    }
}

I inject this though the constrictor of the view models of each screen.

   public DateSetViewModel( IDateService dateService, IDialogCoordinator dialogCoordinator)
    {
        _dateService = dateService;

    }

    public DateTime ScheduledDate
    {
        get => _dateService.ScheduledDate;
        set
        {
            _dateService.ScheduledDate = value;
        }
    }

and on read only views


 public class DateReadViewModel : ObservableObject
 {
    private readonly IDateService _dateService;


  public DateReadViewModel( IDateService dateService, IDialogCoordinator dialogCoordinator)
    {
        _dateService = dateService;

    }

    public DateTime ScheduledDate
    {
        get => _dateService.ScheduledDate;
    }

...
}

Now, when loading, all screen shows initial date (now +1 day). Any update made through DateSetViewModel is reflected on that page UI. But, when switch to other views, it always shows initial date, not the updated value from IDateService. I tried to directly bind to dateService.ScheduledDate in other views, but it didn't work. I use MahApps.Metro to define the views if that matters.

The bindings on DateSetView

<DatePicker Width="100"
                            Margin="{StaticResource ControlMargin}"
                            SelectedDate="{Binding ScheduledDate}" />

and other views, I tried few, but similar to

   <DatePicker Width="100"
                    Margin="5"
                    mah:TextBoxHelper.AutoWatermark="True"
                    SelectedDate="{Binding ScheduledDate, Mode=OneWay}" />
                <TextBlock 
                    Margin="5"
                    VerticalAlignment="Center"
                    Text="{Binding ScheduledDate}" 
                />

Solution

  • DateService is the only object that raises the PropertyChanged event when its ScheduledDate property is set.

    Given your current implementation, you need to subscribe to this event in the view models and raise the event for each data-bound source property:

    public class DateReadViewModel : ObservableObject
    {
        private readonly IDateService _dateService;
    
        public DateReadViewModel(IDateService dateService, IDialogCoordinator dialogCoordinator)
        {
            _dateService = dateService;
            _dateService.PropertyChanged += OnDateServicePropertyChanged;
        }
    
        private void OnDateServicePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(IDateService.ScheduledDate))
                OnPropertyChanged(nameof(ScheduledDate));
        }
    
        public DateTime ScheduledDate
        {
            get => _dateService.ScheduledDate;
        }
    
        ...
    }
    
    public interface IDateService : INotifyPropertyChanged
    {
        public DateTime ScheduledDate { get; set; }
    }
    

    The other option is to bind directly to the property of the service.