/// <summary> /// Calculate the appointment bounds. This works out the size and /// shape that the appointments will take up on the screen, /// and handles any other calculations such as overlaps. This method /// is called from OnPaint only when the property BoundsValidAppointment /// is set to false (this property is set to false in cases such as /// when the control has been resized or the appointment list has /// changed). /// </summary> protected override void CalculateAppointmentBounds(Graphics graphics) { //int appSeparator = 3; //recalculate the appointments foreach (DayWithHourRegion day in this.DayRegions) { //clear day and hour appointment regions day.Appointments.Clear(); foreach (HourRegion hour in day.Hours) { hour.Appointments.Clear(); } //TODO: cater for overlapping multiple timeslots foreach (Appointment app in Appointments) { if (app.DateStart.Year == day.Date.Year && app.DateStart.DayOfYear == day.Date.DayOfYear) { AppointmentWithHourRegion appRegion = new AppointmentWithHourRegion(app); day.Appointments.Add(appRegion); foreach (HourRegion hour in day.Hours) { if (app.DateStart.Hour == hour.Hour) // && app.DateStart.Hour <= hour.Hour) { hour.Appointments.Add(appRegion); } } } } foreach (AppointmentWithHourRegion appt1 in day.Appointments) { foreach (AppointmentWithHourRegion appt2 in day.Appointments) { if (appt1 != appt2 && appt1.Appointment.DateEnd != appt2.Appointment.DateStart && appt1.Appointment.DateStart != appt2.Appointment.DateEnd && ( appt2.Appointment.DateStart == appt1.Appointment.DateStart || appt2.Appointment.DateEnd == appt1.Appointment.DateEnd || (appt2.Appointment.DateStart < appt1.Appointment.DateEnd && appt2.Appointment.DateStart > appt1.Appointment.DateStart) || (appt2.Appointment.DateEnd < appt1.Appointment.DateEnd && appt2.Appointment.DateEnd > appt1.Appointment.DateStart) || (appt1.Appointment.DateEnd < appt2.Appointment.DateEnd && appt1.Appointment.DateEnd > appt2.Appointment.DateStart) ) ) { appt1.HasOverlaps = true; appt2.HasOverlaps = true; if (!appt1.OverlappingAppointments.Contains(appt2)) { appt1.OverlappingAppointments.Add(appt2); } if (!appt2.OverlappingAppointments.Contains(appt1)) { appt2.OverlappingAppointments.Add(appt1); } } } } //roughly work out appt bounds and starting slot for (int i = 0; i < day.Hours.Count; i++) { HourRegion hour = day.Hours[i]; int apptY = hour.Bounds.Y; if (hour.Appointments.Count > 0) { int hourX = hour.Bounds.X; int apptX = hourX; for (int j = 0; j < hour.Appointments.Count; j++) { AppointmentWithHourRegion app = hour.Appointments[j]; //work out starting slot int startMinute = app.Appointment.DateStart.Minute; if (startMinute >= 0 && startMinute < 15) { app.StartingSlot = 0; } else if (startMinute >= 15 && startMinute < 30) { app.StartingSlot = 1; } else if (startMinute >= 30 && startMinute < 45) { app.StartingSlot = 2; } else if (startMinute >= 45 && startMinute < 60) { app.StartingSlot = 3; } //work out duration and therefore height TimeSpan span = app.Appointment.DateEnd.Subtract(app.Appointment.DateStart); int tempApptHeight = ((span.Hours * 60 + span.Minutes) / 15) * hour.Bounds00.Height; int tempApptY = apptY + (app.StartingSlot * hour.Bounds00.Height); int apptWidth = hour.Bounds.Width; //if we have overlaps, things get nasty if (app.HasOverlaps) { int hourAppWidth = apptWidth / hour.Appointments.Count; apptWidth = hourAppWidth; } app.Bounds = new Rectangle(apptX, tempApptY, apptWidth, tempApptHeight); //set the bounds apptX += apptWidth; } } } //go over again a few times to resolve overlaps for (int i = 0; i < 5; i++) { handleOverlaps(day); } } BoundsValidAppointment = true; }
/// <summary> /// Calculate the time slot bounds. This works out the size and /// shape that days will take up on the screen. This method /// is called from OnPaint only when the property BoundsValidTimeSlot /// is set to false (this property is set to false in cases such as /// when the control has been resized or the date shown has /// changed). /// </summary> protected override void CalculateTimeSlotBounds(Graphics g) { if (Width == 0 || Height == 0) { return; } DayRegions.Clear(); hourHeaders.Clear(); int numberOfDays = 1; //set up the start day if (!SingleDay) { //five days numberOfDays = 5; if (Date.DayOfWeek != DayOfWeek.Monday) { //handle sunday being set - go forwards in that case if (Date.DayOfWeek == DayOfWeek.Sunday) { Date = Date.AddDays(1); } else if (Date.DayOfWeek == DayOfWeek.Saturday) { Date = Date.AddDays(2); } else { DateTime temp = Date; while (temp.DayOfWeek != DayOfWeek.Monday) { temp = temp.AddDays(-1); } Date = temp; } } } //set up days for (int i = 0; i < numberOfDays; i++) { DayWithHourRegion day = new DayWithHourRegion(); day.Date = Date.AddDays(i); day.IsInCurrentMonth = true; this.DayRegions.Add(day); } int hourCount = 0; //set up hours for (int j = 0; j < DayRegions.Count; j++) { DayWithHourRegion day = DayRegions[j] as DayWithHourRegion; if (day != null) { for (int i = 0; i < 24; i++) { if (RenderWorkingHoursOnly) { if (i >= (WorkStartHour - 1) && i < (WorkEndHour + 1)) { HourRegion hour = new HourRegion(); hour.IsWorkingHour = (i >= WorkStartHour && i < WorkEndHour); hour.Hour = i; day.Hours.Add(hour); } } else { HourRegion hour = new HourRegion(); hour.IsWorkingHour = (i >= WorkStartHour && i < WorkEndHour); hour.Hour = i; day.Hours.Add(hour); } } if (j == 0) { hourCount = day.Hours.Count; } } } int timeMarkerWidth = 0; if (this.Width > 120) { timeMarkerWidth = 80; } int dayHeaderHeight = (int)(RendererCache.Current.Header.GetTextInfo(g, this.Font).Height * 1.2); int hourHeight = ((Height - 1 - dayHeaderHeight) / hourCount); int xCurrent = timeMarkerWidth; int hourWidth = (Width - timeMarkerWidth - 1) / numberOfDays; int bounds15height = (hourHeight) / 4; //set up day and hour bounds for (int j = 0; j < DayRegions.Count; j++) { DayWithHourRegion day = DayRegions[j] as DayWithHourRegion; if (day != null) { int yCurrent = dayHeaderHeight; //set up day bounds day.Bounds = new Rectangle(xCurrent, 0, hourWidth, hourHeight * day.Hours.Count); day.Name = day.Date.DayOfWeek.ToString(); day.TitleBounds = new Rectangle(xCurrent, 0, hourWidth, dayHeaderHeight); day.BodyBounds = new Rectangle(day.Bounds.X, day.Bounds.Y, day.Bounds.Width, day.Bounds.Height); foreach (HourRegion hour in day.Hours) { //work out the hour bounds hour.Bounds = new Rectangle(xCurrent, yCurrent, hourWidth, hourHeight); //work out the 15 minute divisions hour.Bounds00 = new Rectangle(xCurrent, yCurrent, hourWidth, bounds15height); hour.Bounds15 = new Rectangle(xCurrent, yCurrent + hour.Bounds00.Height, hourWidth, bounds15height); hour.Bounds30 = new Rectangle(xCurrent, yCurrent + hour.Bounds00.Height + hour.Bounds15.Height, hourWidth, bounds15height); hour.Bounds45 = new Rectangle(xCurrent, yCurrent + hour.Bounds00.Height + hour.Bounds15.Height + hour.Bounds30.Height, hourWidth, bounds15height); if (j == 0) { HeaderRegion header = new HeaderRegion(); header.Bounds = new Rectangle(0, yCurrent, timeMarkerWidth, hourHeight); header.Name = string.Format("{0}:00", hour.Hour); hourHeaders.Add(header); } yCurrent += hourHeight; } } xCurrent += hourWidth; } BoundsValidTimeSlot = true; }