BeepCalendar
A full-featured calendar control with 7 view modes, event management (CRUD, undo/redo, conflict detection), mouse/touch interaction, theme-aware painting pipeline, and sidebar mini-calendar — decomposed across 90+ partial files
Table of Contents
Overview
Namespace: TheTechIdea.Beep.Winform.Controls.Calendar
Assembly: TheTechIdea.Beep.Winform.Controls.dll
Inheritance: BaseControl
BeepCalendar is the most decomposed control in the Beep library, split across 90+ partial files organized into 8 subsystems. It provides a complete calendar experience with 7 view modes (Month, Week, WorkWeek, Day, Timeline, Agenda, List), full event lifecycle management (add, update, delete, undo/redo), conflict detection, mouse/touch interaction with drag-to-create and drag-to-move, and a theme-aware painting pipeline. An optional sidebar mini-calendar provides quick date navigation.
The control uses a centralized Paint() dispatcher that routes to view-mode-specific renderers, a style-based painter system, and a legacy renderer fallback. During design time in Visual Studio, it renders a simplified preview.
Key Features
- 7 View Modes — Month (7-column grid, up to 5 events per cell with overflow), Week/WorkWeek (7-day grid with hourly time slots), Day (single day with 24 hourly slots), Timeline (horizontal time spans), Agenda/List (chronological event list). Configurable density: Compact, Standard, Comfortable.
- Event CRUD — Add, update, delete events with a three-phase mutation pipeline (EventMutating → execution → EventMutated + history recording). Cancelable pre-mutation.
- Undo/Redo — Two-stack history system (_undoStack + _redoStack). Each mutation records full before/after state. Convenience API: CanUndo, CanRedo, Undo(), Redo().
- Conflict Detection — Three policy modes: None, Warn, Strict. Checks scheduling overlap before commit. Events fire ConflictDetected with conflicting event details.
- Drag-to-Create — Click-drag on date cells or empty surfaces to create new events. Visual ghost preview during drag. Ctrl+drag copies existing events.
- Time Snapping — Configurable snap interval (default 15 min). Events snap to nearest N-minute boundary during drag operations.
- CalendarEventEditor — QuickEdit (560x420) and DialogEdit (720x620) modes. Fields: Title, Start/End DateTimePickers, AllDay CheckBox, Location, Status, Organizer, Description.
- Categories & Resources — Color-coded categories with hierarchy. Room/equipment resource scheduling.
- Sidebar — Compact mini-calendar with month navigation, dot indicators for days with events, today highlight.
- Theme-Aware — 9-file LayoutTheme subsystem maps IBeepTheme tokens to calendar colors (cells, headers, today, selected, events, weekends).
View Modes
| Mode | Enum Value | Layout | Best For |
|---|---|---|---|
| Month | CalendarViewMode.Month | 7-column grid (Sun-Sat), 5-6 rows. Up to maxEventsPerCell event blocks per day. "+N more" overflow indicator. | Monthly overview, event browsing |
| Week | CalendarViewMode.Week | 7-day columns with hourly time slots on left. Event bars span their time range. Current time line (red). | Weekly planning, time-based scheduling |
| WorkWeek | CalendarViewMode.WorkWeek | Same as Week but 5 columns (Mon-Fri only). | Business-week scheduling |
| Day | CalendarViewMode.Day | Single day with 24 one-hour slots (~60px each). All-day events in header band. Event blocks show title, time, location. | Daily agenda, time management |
| Timeline | CalendarViewMode.Timeline | Horizontal time axis with event spans. Configurable time scale. Supports resource grouping on Y axis. | Project timelines, resource scheduling |
| Agenda | CalendarViewMode.Agenda | Chronological event list with date headers. Each row: time, title, location, category badge. | Event list for date ranges |
| List | CalendarViewMode.List | Simple scrollable event list. Multi-day events span multiple entries with "continues" indicator. | Quick event browse |
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| ViewMode | CalendarViewMode | Month | Active view mode: Month, Week, WorkWeek, Day, Timeline, Agenda, List |
| CurrentDate | DateTime | DateTime.Today | The date currently displayed/navigated to |
| SelectedDate | DateTime? | null | Currently selected date (click or navigate) |
| Events | ObservableCollection<CalendarEvent> | Empty | Collection of calendar events. Supports mutation pipeline. |
| Categories | List<EventCategory> | Empty | Event categories with Name, Color, Icon, optional parent |
| Resources | List<CalendarResource> | Empty | Resources for room/equipment scheduling |
| DensityMode | CalendarDensityMode | Standard | Compact (small fonts, tight spacing), Standard, Comfortable (large fonts, spacing) |
| ShowSidebar | bool | false | Show compact mini-calendar in sidebar panel |
| ShowWeekNumbers | bool | false | Show ISO week numbers in Month/Week views |
| ShowTodayButton | bool | true | Show "Today" navigation button |
| InteractionSnapIntervalMinutes | int | 15 | Snap interval for event time adjustment during drag |
| ConflictPolicyMode | CalendarConflictPolicyMode | None | None (no checking), Warn (fire event, don't block), Strict (fire event, block overlaps) |
| CanUndo / CanRedo | bool (read-only) | - | Whether undo/redo operations are available |
| EventEditor | ICalendarEventEditor | CalendarEventEditor | Editor instance for editing event details |
| MaxEventsPerCell | int | 5 | Max visible event blocks in month cell before "+N more" |
| FirstDayOfWeek | DayOfWeek | Sunday | First day of the week column |
| MinimumDate / MaximumDate | DateTime? | null | Date range constraints for navigation and event creation |
Events
| Event | Type | Description |
|---|---|---|
| DateSelected | EventHandler<EventArgs> | A date was clicked/navigated to |
| EventSelected | EventHandler<CalendarEvent> | An event was clicked/selected |
| CreateEventRequested | EventHandler<CreateEventRequestEventArgs> | Drag-to-create completed. Set e.CreatedEvent to provide the new event. |
| EventMutating | EventHandler<CalendarEventMutationEventArgs> | Pre-mutation. Cancelable via e.Cancel=true. |
| EventMutated | EventHandler<CalendarEventMutationEventArgs> | Post-mutation. Event has been added/updated/deleted. |
| ConflictDetected | EventHandler<CalendarConflictEventArgs> | Scheduling conflict detected. e.ConflictingEvents lists conflicts. |
| InteractionStarted | EventHandler<CalendarInteractionEventArgs> | Mouse drag interaction started (move, resize, create) |
| InteractionUpdated | EventHandler<CalendarInteractionEventArgs> | Mouse drag position updated |
| InteractionCompleted | EventHandler<CalendarInteractionEventArgs> | Mouse drag interaction completed |
| InteractionCancelled | EventHandler<CalendarInteractionEventArgs> | Mouse drag interaction cancelled (Escape key) |
Methods
| Method | Return | Description |
|---|---|---|
| NavigateToDate(DateTime) | void | Navigate to a specific date |
| Today() | void | Navigate to today's date |
| Next() / Previous() | void | Navigate to next/previous period based on ViewMode |
| Undo() / Redo() | bool | Undo or redo the last event mutation |
| GetEventsForDate(DateTime) | IEnumerable<CalendarEvent> | Get all events for a specific date |
| GetEventsForRange(DateTime, DateTime) | IEnumerable<CalendarEvent> | Get events within a date range |
| FindById(string) | CalendarEvent? | Find event by unique ID |
| ResetViewButtonStates() | void | Refresh undo/redo button enabled states |
Interaction System
16 partial files handle all pointer/touch/mouse events with view-mode-specific hit testing:
- Drag Modes — ResolveDragMode() returns CalendarInteractionMode: MoveEvent (event block in Day/Week/WorkWeek/Timeline), ResizeStart/End (event block edges), SelectEvent (Month/Agenda/List), RangeSelect (Day/Week date cells), CreateEvent (empty surface).
- Hit Testing — 8 files: HitTesting.cs dispatches to view-specific resolvers (MonthView, WeekView, DayView, TimelineView, TimedView, Agenda). Returns CalendarInteractionHitTestResult with TargetKind (EventBlock/DateCell/EmptySurface), RequestedMode, Location, and Date.
- Timing Snap — InteractionSnapIntervalMinutes (default 15). Events snap to nearest interval. Timing.cs, Snap.cs, Snap.Location.cs handle the snapping logic.
- Ctrl+Drag Copy — IsCopyModifierDown() checks for Ctrl. When active, dragging an existing event creates a copy at the drop location.
- Commitment — Commit.cs dispatches to NewEvent.cs (create from proposal), Copy.cs (Ctrl+drag copy), or ExistingEvent.cs (mutation). All route through EventMutating → CRUD → EventMutated → History.
Event Management
Event Model
CalendarEvent: Id, Title, Start, End, IsAllDay, Location, Description, Status, Category (references EventCategory), Organizer, CreatedDate, ModifiedDate.
Mutation Pipeline
- Pre-mutation — RaiseMutating fires EventMutating. Cancelable.
- Execution — Data change occurs (add/update/delete).
- Post-mutation — RaiseMutated fires EventMutated. History record created. View button states updated.
CalendarEventEditor
Implements ICalendarEventEditor. Two modes: QuickEdit (560x420 compact) and DialogEdit (720x620 full form). TryEdit(request, out editedEvent) opens a BeepDialogForm with CalendarEventEditorContent (UserControl) containing Title, Start/End DateTimePickers, AllDay CheckBox, Location, Status ComboBox, and optional Advanced panel (Organizer, Description).
Conflict Detection
CalendarConflictPolicyMode: None (disabled), Warn (fire ConflictDetected but allow), Strict (fire and block). Conflict resolution checks scheduling overlap. GetConflicts(event) returns conflicting events.
Theming
The LayoutTheme subsystem (9 files) handles theme token resolution:
| File | Role |
|---|---|
| LayoutTheme.cs | Main coordinator, initializes all theme-related visuals |
| LayoutTheme.ApplyTheme.cs | Maps IBeepTheme tokens to calendar colors, fonts, borders, padding |
| LayoutTheme.Layout.cs | Cell positions, margins, grid spacing calculations |
| LayoutTheme.MonthCells.cs | Month view cell colors: today, selected, hovered, weekend, other-month, events |
| LayoutTheme.HeaderText.cs | Header text formatting, day-of-week labels, month/year title |
| LayoutTheme.ResponsiveLabels.cs | Text truncation for narrow cells |
| LayoutTheme.Controls.cs | Navigation buttons, view selector, density toggle visual properties |
| LayoutTheme.Controls.Categories.cs | Event category color mapping from theme |
Architecture
Partial File Organization (90+ files)
| Subsystem | Files | Key Contents |
|---|---|---|
| Core | 6 | Constructor, public API, lifecycle, style, appearance |
| Commands | 5 | Public commands, execution core, helpers, duplicate detection |
| Event Operations | 13 | CRUD (Add/Remove/Upsert), History (Undo/Redo), Navigation, Queries, Editor |
| Interactions | 16 | Pointer events, timing snap, proposals, hit testing (7 views), commitment |
| Painting | 11 | Pipeline, Month/Week/Day/List/Timeline views, design-time, sidebar, header formatting |
| LayoutTheme | 9 | Theme application, cell colors, header text, responsive labels, categories |
| Visual Updates | 3 | Flush (full redraw), Scope (partial invalidation), Strategy (dirty-rect) |
| Types & Fields | 5 | Enums, EventArgs, command types, event model |
Paint Dispatcher
private void Paint(Graphics g, Rectangle bounds)
{
if (IsDesignModeSafe)
DrawDesignTimePreview(g, bounds); // VS designer
else if (_usePainterSystem && _stylePainter != null)
DrawWithPainter(g, contentRect); // New system
else
DrawWithLegacyRenderer(g, contentRect); // Fallback
}Code Examples
Basic Calendar Setup
var cal = new BeepCalendar();
cal.ViewMode = CalendarViewMode.Month;
cal.CurrentDate = DateTime.Today;
cal.DensityMode = CalendarDensityMode.Standard;
cal.ShowSidebar = true;
cal.ShowWeekNumbers = true;
// Add sample events
cal.Events.Add(new CalendarEvent
{
Title = "Team Standup",
Start = DateTime.Today.AddHours(9),
End = DateTime.Today.AddHours(9.5),
Category = categories["Meeting"]
});
cal.Events.Add(new CalendarEvent
{
Title = "Lunch with Client",
Start = DateTime.Today.AddHours(12),
End = DateTime.Today.AddHours(13),
Location = "Bistro 45"
});
Event Management with Validation
cal.EventMutating += (s, e) =>
{
if (e.Kind == CalendarEventMutationKind.Delete
&& e.Original.Category == "Locked")
e.Cancel = true;
};
cal.EventMutated += (s, e) =>
Console.WriteLine($"{e.Kind}: {e.Result.Title}");
// Conflict detection
cal.ConflictPolicyMode = CalendarConflictPolicyMode.Strict;
cal.ConflictDetected += (s, e) =>
MessageBox.Show($"Cannot schedule: {e.Description}");
Interaction Events
cal.InteractionStarted += (s, e) =>
Console.WriteLine($"Started: {e.Mode}");
cal.CreateEventRequested += (s, e) =>
{
e.CreatedEvent = new CalendarEvent
{
Title = "New Event",
Start = e.RequestedStart,
End = e.RequestedStart.AddHours(1)
};
};
cal.InteractionSnapIntervalMinutes = 30; // Snap to :00/:30
Meeting Scheduler Example
public class MeetingScheduler
{
private BeepCalendar cal;
private List<DateTime> bookedSlots = new();
private void Calendar_InteractionCompleted(object s, CalendarInteractionEventArgs e)
{
var selectedTime = e.Date;
if (IsTimeSlotAvailable(selectedTime))
ScheduleMeeting(selectedTime);
else
MessageBox.Show("This slot is already booked");
}
private bool IsTimeSlotAvailable(DateTime dt)
=> !bookedSlots.Any(s => Math.Abs((s - dt).TotalMinutes) < 60);
private void ScheduleMeeting(DateTime dt)
{
bookedSlots.Add(dt);
cal.Events.Add(new CalendarEvent
{
Title = "New Meeting",
Start = dt,
End = dt.AddHours(1)
});
}
}