Hi,
I am creating a control that resembles the 'month view calendar' in Outlook. It has one 'block' for each day and those blocks can contain appointments. The 'blocks' are called DayControls, and the appointments are instances of an Appointment class, whose important properties are the Subject and StartTime (DateTime) of the appointment.
The Calendar control hosts a collection of DayControls via the DayControls property:
csharp Code:
private readonly ObservableCollection<DayControl> _InternalDayControls;
private readonly DayControlCollection _DayControls;
public DayControlCollection DayControls { get { return _DayControls; } }
The DayControlCollection inherits ReadOnlyObservableCollection<DayControl> , such that DayControls cannot be added/edited from the outside of the calendar. When I add/remove DayControls I do that from the _InternalDayControls variable, which the _DayControls read-only collection wraps.
Furthermore, the Calendar hosts a property Appointments, with a similar system;
csharp Code:
private readonly ObservableCollection<Appointment> _InternalAppointments;
private readonly AppointmentCollection _Appointments;
public AppointmentCollection Appointments { get { return _Appointments; } }
Appointments can only belong to one single Day (not true in Outlook, but that is the case in my application) which is determined by their StartTime property.
To add/remove appointments, I created a public AddAppointment and RemoveAppointment method. The Appointments property is a ReadOnlyObservableCollection again, because I don't want users adding new appointments to the Appointments collection directly. The reason will become clear in a moment.
csharp Code:
public void AddAppointment(Appointment appointment)
{
if (appointment == null) return;
_InternalAppointments.Add(appointment);
this.OnPropertyChanged(() => this.Appointments);
}
The DayControls show the Appointments specific to that day in a ListBox. Since I want to data-bind that ListBox to a collection of Appointments, I needed an Appointments property in each DayControl as well. I made that into a read-only property that returns a new ObservableCollection<Appointment>, to which I add all the appointments that belong to this specific day:
csharp Code:
public ObservableCollection<Appointment> Appointments
{
get
{
var appointments =
new ObservableCollection<Appointment>
();
foreach (var appointment in this.Calendar.Appointments.FromDate(this.Date.Date))
{
appointments.Add(appointment);
}
return appointments;
}
}
As you can see, the DayControl has a reference to its parent Calendar, from which I request all Appointments that have their StartTime set to the date of this current DayControl.
This seems to work fine, except for one crucial issue: the ListBox does not know when to update its data!
Back in the Calendar control I had a method AddAppointment, where I add an Appointment to the Calendar._InternalAppointments method (which adds it to the read-only Appointments property). But the DayControl.Appointments property (to which the ListBox is bound) is not updated, it will only return the new data when its getter is called... In other words; the DayControl.Appointments property does not notify the ListBox of changes (and it can't, because it is created 'on-the-fly' when required).
For this reason, I can add a new appointment, but the DayControl in question does not display it because its ListBox does not know that the Appointments property has changed.
One 'dirty' workaround I've come up with is just to raise the PropertyChanged event (with property "Appointments"), on the DayControl in question. So the AddAppointment method now looks like this:
csharp Code:
public void AddAppointment(Appointment appointment)
{
if (appointment == null) return;
_InternalAppointments.Add(appointment);
this.OnPropertyChanged(() => this.Appointments);
// Also notify the DayControl to update its data
var day = this.DayControls.FromAppointment(appointment);
if (day != null) day.OnPropertyChanged(() => day.Appointments);
}
This works, but it seems like a dirty workaround... The OnPropertyChanged method shouldn't be called from the outside (I had to make it public just to try this).
Am I right, is this a 'hack', or is this the way it should be done? Perhaps instead of making the OnPropertyChanged public I should make a public method specifically to call the OnPropertyChanged:
csharp Code:
public void UpdateAppointments()
{
this.OnPropertyChanged(() => this.Appointments);
}
That's better, but still doesn't seem 'correct'.
What should I be doing instead?