Calendar Events System
Complete event lifecycle across 13 partial files — CRUD operations, mutation pipeline (EventMutating/Mutated), undo/redo history stack, conflict detection, and CalendarEventEditor dialog
Table of Contents
Overview
13 partial files under Calendar/BeepCalendar.EventOperations.*.cs handle the complete event lifecycle plus the standalone CalendarEventEditor class. Every event mutation (add, update, delete) follows a three-phase pipeline: Pre-mutation (EventMutating with cancel support) → Execution (data change) → Post-mutation (EventMutated + history recording). An integrated undo/redo system records mutations on two stacks, and the conflict detection system checks scheduling overlap using configurable CalendarConflictPolicyMode.
Partial File Map (13 Files)
| File | Responsibility | Key Members |
|---|---|---|
| EventOperations.cs | Mutation raising + conflict | RaiseMutating(), RaiseMutated(), GetConflicts(), CanApplyEventChange() |
| EventOperations.Public.cs | Public API surface | Events: EventSelected, DateSelected, CreateEventRequested, ConflictDetected, EventMutating, EventMutated. Properties: EventEditor, CanUndo, CanRedo |
| EventOperations.Crud.cs | CRUD coordinator stub | Empty partial — dispatches to .Add / .Remove / .Upsert |
| EventOperations.Crud.Add.cs | Add event logic | Adds CalendarEvent, validates, fires mutation events |
| EventOperations.Crud.Remove.cs | Remove event logic | Removes by reference or ID, validates, fires mutations |
| EventOperations.Crud.Upsert.cs | Upsert event logic | Insert-or-update — replaces if same ID exists |
| EventOperations.History.cs | Undo/Redo dispatcher | UndoLastMutation(), RedoLastMutation() |
| EventOperations.History.Internal.cs | Stack management | _undoStack, _redoStack, push/pop logic |
| EventOperations.History.Apply.cs | Reverse mutation application | ApplyMutationRecord(record, undo), reverse add=remove, reverse remove=add |
| EventOperations.Navigation.cs | Date navigation | Today(), Next(), Previous() |
| EventOperations.Queries.cs | Event queries | GetEventsForDate(), GetEventsForRange(), FindById() |
| EventOperations.Commands.cs | Command execution | Command helpers + duplicate detection |
| EventOperations.Editor.cs | Editor integration | Editor.Commit.cs — commits edited event back to model |
CRUD Operations
All event mutations follow a strict three-phase pipeline:
- Pre-mutation —
RaiseMutating(kind, source, proposed, original, isNew, out canceled)- Fires
EventMutatingevent withCalendarEventMutationEventArgs - Handlers may cancel by setting
e.Cancel = true(validation, permissions, business rules)
- Fires
- Execution — The actual data change: add to
Eventslist, update properties, or remove from list - Post-mutation —
RaiseMutated(kind, source, result, original, isNew, conflicts)- Records mutation in undo/redo history with full state snapshot
- Fires
EventMutatedevent for UI refresh - Updates view button states (undo/redo enabled)
Mutation Pipeline
// Cancel a delete based on business rules
cal.EventMutating += (s, e) =>
{
if (e.Kind == CalendarEventMutationKind.Delete
&& e.Original.Category == "Locked")
{
e.Cancel = true; // Prevent deletion of locked events
}
};
// React to completed mutations
cal.EventMutated += (s, e) =>
Console.WriteLine($"{e.Kind}: {e.Result.Title}");
// Create event request
cal.CreateEventRequested += (s, e) =>
{
e.CreatedEvent = CalendarEventEditor.ShowNewEventDialog(
e.RequestedDate, e.RequestedStart);
};Undo/Redo History
Two stacks manage the complete history:
- _undoStack — Stack of mutation records, each containing the full before/after state of the event
- _redoStack — Stack of undone mutations for redo capability
public bool UndoLastMutation()
{
// Pop _undoStack, apply record in reverse, push to _redoStack
// Returns false if stack is empty
}
public bool RedoLastMutation()
{
// Pop _redoStack, reapply forward, push back to _undoStack
}
// Convenience API
if (cal.CanUndo) cal.Undo();
if (cal.CanRedo) cal.Redo();
History is implemented across 5 files: History.cs (dispatcher), History.Internal.cs (stack management), History.Apply.cs / Apply.Upsert.cs / Apply.Remove.cs (reverse mutation logic). Each mutation record stores the complete event state before and after the change.
Conflict Detection
// Three policy modes
cal.ConflictPolicyMode = CalendarConflictPolicyMode.Strict;
// None: No conflict checking
// Warn: Detect and fire ConflictDetected, but don't block
// Strict: Detect and block overlapping events
cal.ConflictDetected += (s, e) =>
{
Console.WriteLine($"Conflict: {e.Description}");
foreach (var conflict in e.ConflictingEvents)
Console.WriteLine($" {conflict.Title} ({conflict.Start}-{conflict.End})");
};
The conflict detection system uses a CalendarConflictPolicy object (lazy-created from _conflictPolicyMode) to check scheduling overlap before commit. Overlapping events are returned as CalendarConflictEventArgs.
CalendarEventEditor
Implements: ICalendarEventEditor | Visibility: public sealed
public sealed class CalendarEventEditor : ICalendarEventEditor
{
public CalendarEventEditorMode Mode { get; set; }
// QuickEdit: compact 560x420 panel
// DialogEdit: full 720x620 dialog
public bool TryEdit(CalendarEventEditorRequest request,
out CalendarEvent editedEvent);
// Opens BeepDialogForm with CalendarEventEditorContent UserControl
// Fields: Title TextBox, Start DateTimePicker, End DateTimePicker,
// AllDay CheckBox, Location TextBox, Status ComboBox
// Advanced panel: Organizer TextBox, Description TextBox
}
CalendarEventEditorContent (internal sealed, extends UserControl) builds the editing UI with a TableLayoutPanel (4 rows, AutoScroll). Methods: LoadFromRequest() and BuildEditedEvent().
Categories & Resources
- Categories —
List<EventCategory>. Each category has Name, Color (for visual indicators), Icon path, and optional parent category for hierarchy. Events reference a category viaCalendarEvent.Category. - Resources —
List<CalendarResource>for room/equipment/person scheduling. Each resource has Name, Type, Capacity, Color, and optional Location. - PerformanceMetrics —
CalendarPerformanceMetricsobject (browsable=false) for tracking rendering time, interaction latency, and event operation timing.
Public Commands
5 command files expose the public command API: BeepCalendar.Commands.cs (main command definitions), Commands.Core.cs (execution engine), Commands.Helpers.cs (mutation context creation), Commands.DuplicateDetection.cs (dedup logic for command batching), Commands.MutationTracking.cs (mutation identity generation for auditing). Commands drive the public API indirectly — the calendar exposes user-facing methods that internally route through the command system for consistency.
Code Examples
Add Event with Validation
cal.EventMutating += (s, e) =>
{
if (e.Proposed.End - e.Proposed.Start > TimeSpan.FromHours(8))
e.Cancel = true; // Reject events longer than 8 hours
};
cal.Events.Add(new CalendarEvent
{
Title = "Design Review",
Start = DateTime.Today.AddHours(10),
End = DateTime.Today.AddHours(11),
Location = "Room 204",
Category = categories["Meeting"]
});
if (cal.CanUndo) cal.Undo(); // Revert if needed
Conflict Detection in Strict Mode
cal.ConflictPolicyMode = CalendarConflictPolicyMode.Strict;
cal.ConflictDetected += (s, e) =>
MessageBox.Show($"Cannot schedule: {e.Description}");
cal.CreateEventRequested += (s, e) =>
{
e.CreatedEvent = cal.EventEditor.TryEdit(
new CalendarEventEditorRequest
{
ProposedDate = e.RequestedDate,
Mode = CalendarEventEditorMode.QuickEdit
}, out var result) ? result : null;
};