Complete guide to building applications with BaseApplication class
BaseApplication is the foundation class for building applications in Fractal Platform. When you create your application, you inherit from BaseApplication and override its event handler methods to implement your business logic.
var power = GetPowerInfo(); Log($"Battery: {power.BatteryPercent}%"); Log($"Plugged in: {power.IsOnline}"); Log($"Time remaining: {power.BatteryLifeTime}");
| Property | Type | Description |
|---|---|---|
| SessionID | Guid | Unique identifier for current session |
| Client | FractalPlatformClient | Database client for queries |
| REST | RESTClient | HTTP client for REST APIs |
| AI | AIClient | AI service client |
| Context | Context | Current execution context |
| User | User | Current user information |
| Name | string | Application name (class name without "Application") |
| DatabaseName | string | Default database name |
| HasDatabase | bool | Whether database is configured |
Here's a complete example showing many features together:
public class TaskManagerApplication : BaseApplication { public override void OnStart() { // Check if user is logged in if (User.IsGuest) { // Show login form FirstDocOf("LoginForm") .OpenForm("Login"); } else { // Show main task list ShowTaskList(); } } private void ShowTaskList() { DocsWhere("Tasks", new { OwnerID = User.ID }) .OrderBy("{'DueDate':$}") .OpenForm("My Tasks"); } public override bool OnEventDimension(EventInfo info) { switch (info.Action) { case "AddTask": CreateNewDocFor("TaskTemplate", "Tasks") .OpenForm("New Task", result => { if (result.Result) { RefreshForm(); MessageBox("Task created!"); } }); return true; case "CompleteTask": ModifyDocsWhere("Tasks", info.DocID) .Update("{'Status':'Completed','CompletedDate':@Date}", GetNowDate()); RefreshForm(); return true; case "DeleteTask": MessageBox( "Delete this task?", MessageBoxButtonType.YesNo, result => { if (result.Result) { DelDoc("Tasks", info.DocID); CloseForm(); } }); return true; case "Logout": ResetToGuest(); CloseAllForms(); OnStart(); return true; } return false; } public override bool OnChangeDimension(ChangeInfo info) { // Auto-calculate priority based on due date if (info.ChangeName == "DueDate") { var dueDate = info.AfterValue.ToDateTime(); var daysUntilDue = (dueDate - GetNowDate()).Days; string priority = daysUntilDue < 1 ? "Urgent" : daysUntilDue < 7 ? "High" : "Normal"; ModifyDocsWhere("Tasks", info.DocID) .Update(DQL("{'Priority':@Priority}", priority)); RefreshForm(); return true; } return false; } public override object OnComputedDimension(ComputedInfo info) { if (info.Variable == "DaysRemaining") { var dueDate = info.FindFirstDateTimeValue("DueDate"); var days = (dueDate - GetNowDate()).Days; return days > 0 ? $"{days} days left" : "Overdue"; } return null; } public override bool OnSecurityDimension(SecurityInfo info) { // Users can only modify their own tasks if (info.OperationType == OperationType.Update || info.OperationType == OperationType.Delete) { var ownerId = info.FindFirstValue("OwnerID"); return ownerId == User.ID.ToString(); } return true; } public override bool OnTimerDimension(TimerInfo info) { // Check for overdue tasks every minute if (info.Action == "CheckOverdue") { var overdueTasks = DocsWhere("Tasks", "{'DueDate':Less(@Now),'Status':'Active'}", GetNowDate()) .Count(); if (overdueTasks > 0) { Log($"Warning: {overdueTasks} overdue tasks"); } return true; } return false; } }
This is the first method called when your application starts. Use it to initialize data, load settings, or show the initial form.
public override void OnStart() { // Load initial data var users = DocsOf("Users") .Select<User>(); // Show welcome message MessageBox("Welcome to the application!"); // Open main form FirstDocOf("MainMenu") .OpenForm("Main Menu"); }
Change the application language for the current user.
SetUserLanguage("en"); // English SetUserLanguage("uk"); // Ukrainian
Change the visual theme.
SetUserTheme(ThemeType.Dark); SetUserTheme(ThemeType.Light);
This is the most important event handler. It's called when users click buttons, select items, or trigger any action in your forms.
public override bool OnEventDimension(EventInfo info) { // Get the action name var action = info.Action; if (action == "SaveUser") { // Get values from the form var name = info.FindFirstValue("Name"); var age = info.FindFirstIntValue("Age"); // Save to database AddDoc("Users", new { Name = name, Age = age }); MessageBox("User saved successfully!"); return true; // Event was handled } if (action == "DeleteUser") { var userId = info.DocID; DelDoc("Users", userId); CloseForm(); return true; } return false; // Event not handled, continue to other handlers }
// Get single values string name = info.FindFirstValue("Name"); int age = info.FindFirstIntValue("Age"); bool isActive = info.FindFirstBoolValue("IsActive"); double price = info.FindFirstDoubleValue("Price"); DateTime date = info.FindFirstDateTimeValue("CreatedDate"); // Get multiple values at once var values = info.FindFirstValues("Name", "Email", "Phone");
Called when a field value changes in a form. Use this to implement validation, calculations, or dynamic behavior.
public override bool OnChangeDimension(ChangeInfo info) { // ChangeName is the field that changed if (info.ChangeName == "Quantity") { var quantity = info.AfterValue.ToInt(); var price = info.FindFirstDoubleValue("Price"); // Calculate total automatically var total = quantity * price; ModifyDocsWhere("Orders", info.DocID) .Update("{'Total':@Total}", total); RefreshForm(); return true; } if (info.ChangeName == "Email") { var email = info.AfterValue.ToString(); // Validate email format if (!email.Contains("@")) { MessageBox("Please enter a valid email address"); return false; // Reject the change } } return false; }
Called before a form opens. Use it to prepare data or prevent form opening.
public override bool OnOpenForm(FormInfo info) { // Check if user has permission if (info.Collection.Name == "AdminPanel") { if (!User.IsAdmin) { MessageBox("Access denied. Admin rights required."); return false; // Prevent opening } } // Load related data before showing form if (info.Collection.Name == "OrderDetails") { var orderId = info.DocID; // Prepare order items, customer info, etc. } return true; // Allow form to open }
Called before a form closes. Use it to save changes or confirm closing.
public override bool OnCloseForm(FormInfo info) { // Ask for confirmation before closing var hasChanges = /* check if form has unsaved changes */ true; if (hasChanges) { MessageBox( "You have unsaved changes. Close anyway?", MessageBoxButtonType.YesNo, result => { if (result.Result) { CloseForm(); } }); return false; // Wait for user confirmation } return true; // Allow closing }
Called when a computed field needs to be calculated. Use variables like @VariableName in your data.
public override object OnComputedDimension(ComputedInfo info) { if (info.Variable == "FullName") { var firstName = info.FindFirstValue("FirstName"); var lastName = info.FindFirstValue("LastName"); return $"{firstName} {lastName}"; } if (info.Variable == "Age") { var birthDate = info.FindFirstDateTimeValue("BirthDate"); return DateTime.Now.Year - birthDate.Year; } if (info.Variable == "OrderTotal") { var quantity = info.FindFirstIntValue("Quantity"); var price = info.FindFirstDoubleValue("Price"); return quantity * price; } return null; // Variable not found }
Control access to data based on user permissions.
public override bool OnSecurityDimension(SecurityInfo info) { // Only admins can delete if (info.OperationType == OperationType.Delete) { return User.IsAdmin; } // Users can only edit their own data if (info.OperationType == OperationType.Update) { var ownerId = info.FindFirstValue("OwnerID"); return ownerId == User.ID.ToString(); } return true; // Allow by default }
Provide options for dropdown lists dynamically.
public override List<string> OnEnumDimension(EnumInfo info) { if (info.Variable == "Countries") { return new List<string> { "USA", "Canada", "UK", "Germany" }; } if (info.Variable == "Categories") { // Load from database var categories = DocsOf("Categories") .Select<string>("{'Name':$}"); return categories.ToList(); } return new List<string>(); }
Called at regular intervals for background tasks.
public override bool OnTimerDimension(TimerInfo info) { if (info.Action == "RefreshData") { // Refresh data every 30 seconds var newData = DocsOf("RealTimeData") .Select<DataItem>(); RefreshForm(); return true; } return false; }
Handle file uploads from users.
public override bool OnUploadFiles(IEnumerable<UploadFileInfo> fileInfos) { foreach (var fileInfo in fileInfos) { Log($"Uploaded: {fileInfo.FileName}"); Log($"New name: {fileInfo.NewFileName}"); Log($"Size: {fileInfo.Length} bytes"); // Save file information to database AddDoc("Files", new { FileName = fileInfo.FileName, StoredName = fileInfo.NewFileName, Size = fileInfo.Length, UploadDate = GetNowDate() }); } return true; }
Receive messages from other applications.
public override bool OnReceiveMessage(MessageInfo info) { Log($"Message from {info.FromAppName}: {info.Message}"); // Handle message based on sender if (info.FromAppName == "NotificationService") { var notification = info.Message.ToString(); MessageBox(notification); } return true; }
SendMessage("OtherApp", myData)
Handle authentication events like login, logout, or registration.
public override bool OnAuthDimension(AuthInfo info) { if (info.Action == "Login") { var username = info.FindFirstValue("Username"); var password = info.FindFirstValue("Password"); // Validate credentials var user = DocsWhere("Users", new { Username = username, Password = password }) .Select<User>() .FirstOrDefault(); if (user != null) { // Set current user Context.User = user; MessageBox("Login successful!"); return true; } else { MessageBox("Invalid credentials"); return false; } } if (info.Action == "Register") { var username = info.FindFirstValue("Username"); var email = info.FindFirstValue("Email"); // Check if username exists var exists = DocsWhere("Users", new { Username = username }) .Any(); if (exists) { MessageBox("Username already taken"); return false; } // Create new user AddDoc("Users", info.Collection.ToJson(info.DocID)); MessageBox("Registration successful!"); return true; } return false; }
Called when a user confirms a code sent via SMS or email.
public override bool OnCodeConfirmed(CodeConfirmedInfo info) { if (info.EventType == "PhoneVerification") { // Mark phone as verified ModifyDocsWhere("Users", info.DocID) .Update("{'PhoneVerified':true}"); MessageBox("Phone number verified successfully!"); return true; } if (info.EventType == "EmailVerification") { // Mark email as verified ModifyDocsWhere("Users", info.DocID) .Update("{'EmailVerified':true}"); MessageBox("Email verified successfully!"); return true; } if (info.EventType == "TwoFactorAuth") { // Complete login process Log("Two-factor authentication successful"); ShowTaskList(); return true; } return false; }
Handle menu actions and customize menu behavior.
public override bool OnMenuDimension(MenuInfo info) { if (info.Action == "Export") { // Export current data var data = info.Collection.ToJson(info.DocID); WriteFileText("export.json", data); MessageBox("Data exported successfully!"); return true; } if (info.Action == "Print") { // Generate printable report Log("Generating print report..."); return true; } return false; }
Provide dynamic menu items based on context.
public override List<MenuItem> OnMenuItemsDimension(MenuInfo info) { var menuItems = new List<MenuItem>(); // Add different items based on user role if (User.IsAdmin) { menuItems.Add(new MenuItem { Name = "Admin Panel", Action = "OpenAdminPanel" }); menuItems.Add(new MenuItem { Name = "View Logs", Action = "ViewLogs" }); } // Add context-specific items if (info.Collection.Name == "Orders") { menuItems.Add(new MenuItem { Name = "Generate Invoice", Action = "GenerateInvoice" }); } return menuItems; }
Control pagination behavior when user navigates between pages.
public override bool OnMovePageDimension(MoveInfo info) { // Log page navigation Log($"Moving to page {info.CurrentPage}"); // Check if user can navigate if (info.IsNextPage) { // Validate before moving to next page var isValid = ValidateCurrentPage(info); if (!isValid) { MessageBox("Please complete all required fields"); return false; // Prevent navigation } } // Custom logic for specific pages if (info.CurrentPage == 3) { // Load additional data for page 3 LoadPageData(3); } return true; // Allow navigation }
false from event handlers to prevent the action. Return true to allow it or indicate the event was handled successfully.
BaseApplication provides convenient wrapper methods for database operations. All these methods are documented in the Query Guide, but here's a quick reference.
// Get all users var users = DocsOf("Users") .Select<User>(); // Count documents var count = DocsOf("Orders") .Count();
// Find by ID var user = DocsWhere("Users", 123) .Select<User>() .FirstOrDefault(); // Find by criteria var activeUsers = DocsWhere("Users", "{'IsActive':true}") .Select<User>(); // Find by object var admins = DocsWhere("Users", new { Role = "Admin" }) .Select<User>();
// Get first document from collection var settings = FirstDocOf("Settings") .Select<AppSettings>() .FirstOrDefault();
// Add with JSON uint newId = AddDoc("Users", "{'Name':'John','Age':30}"); // Add with object uint id = AddDoc("Users", new { Name = "Jane", Age = 25, Email = "jane@example.com" });
// Update specific document ModifyDocsWhere("Users", userId) .Update("{'Age':31}"); // Update multiple documents ModifyDocsWhere("Users", "{'Status':'Inactive'}") .Update("{'Status':'Archived'}"); // Add to array ModifyDocsWhere("Users", userId) .Update("{'Tags':[Add,'VIP']}");
DelDoc("Users", userId);
// Open form to create new user CreateNewDocFor("UserTemplate", "Users") .OpenForm("New User", result => { if (result.Result) { MessageBox("User created!"); } });
Display messages to the user with different button combinations.
// Simple message (Cancel button only) MessageBox("Operation completed successfully"); // OK button MessageBox("Welcome!", MessageBoxButtonType.Ok); // Yes/No buttons with callback MessageBox( "Delete this item?", MessageBoxButtonType.YesNo, result => { if (result.Result) { // User clicked Yes DelDoc("Items", itemId); } }); // With custom title MessageBox( "Changes saved", "Success", MessageBoxButtonType.Ok);
Prompt the user to enter text.
// Simple input InputBox( "Enter your name:", result => { if (result.Result) { var name = result.FindFirstValue("Enter your name:"); MessageBox($"Hello, {name}!"); } }); // With title and default value InputBox( "New filename:", "Rename File", result => { if (result.Result) { var newName = result.FindFirstValue("New filename:"); // Rename file... } }, "document.txt"); // default value
Close the currently active form.
CloseForm();
Save the current form (triggers save handlers).
SaveForm();
Refresh the current form to show updated data.
// After updating data ModifyDocsWhere("Users", userId) .Update("{'Status':'Active'}"); RefreshForm(); // Show the changes
Close all open forms.
CloseAllForms();
Conditionally close or save a specific form if it's open.
// Close user form if it's open CloseIfOpenedForm("UserDetails"); // Save settings form if it's open SaveIfOpenedForm("Settings");
Protect actions with password verification.
UsePassword("secret123", () => { // This code only runs if password is correct FirstDocOf("AdminPanel") .OpenForm("Admin Panel"); });
Read text content from a file.
var content = ReadFileText("config.json"); var data = JsonConvert.Deserialize(content);
Read binary content from a file.
byte[] imageData = ReadFileBytes("logo.png");
Write text content to a file.
var settings = new { Theme = "Dark", Language = "en" }; var json = JsonConvert.Serialize(settings); WriteFileText("settings.json", json);
Write binary content to a file.
WriteFileBytes("backup.dat", dataBytes);
Get the base path where application files are stored.
var basePath = GetFilesPath(); Log($"Files stored in: {basePath}");
Get full path to a specific file.
var fullPath = GetFilePath("data.csv");
Get URL to access a file through the web interface.
var imageUrl = GetFileUrl("profile.jpg"); // Returns: https://yourserver/files/YourApp/profile.jpg
Log messages for debugging and monitoring.
// Simple message Log("Application started"); // Multiple values Log("User:", userName, "Age:", userAge); // With formatting Log("Processing {0} items", count);
Get the current date and time in the server's timezone.
var now = GetNowDate(); AddDoc("Events", new { Name = "UserLogin", Timestamp = now });
Cache expensive operations to improve performance.
// Cache for 15 minutes (default) var categories = UseCache(() => { return DocsOf("Categories") .Select<Category>() .ToList(); }); // Cache for custom duration var stats = UseCache( () => CalculateStatistics(), TimeSpan.FromHours(1) ); // Named cache var data = UseCache( () => LoadData(), TimeSpan.FromMinutes(30), "MyDataCache" ); // Force refresh var freshData = UseCache( () => LoadData(), TimeSpan.FromMinutes(30), "MyDataCache", isForceUpdate: true );
Build dynamic query language strings with parameters.
var name = "John"; var age = 30; // Create JSON with parameters var json = DQL("{'Name':@Name,'Age':@Age}", name, age); // Use in queries AddDoc("Users", DQL("{'Name':@Name}", name));
Reset the user session to guest user (logout).
// Logout and remove auto-login ResetToGuest(true); // Logout but keep auto-login ResetToGuest(false);
Verify SMS or email confirmation codes.
var isValid = ConfirmCode("Users", userId, code); if (isValid) { MessageBox("Code confirmed!"); } else { MessageBox("Invalid code"); }
Get localized text based on current user language.
var welcomeText = GetLocalizedValue("Welcome"); // Returns "Welcome" for English, "ΠΠ°ΡΠΊΠ°Π²ΠΎ ΠΏΡΠΎΡΠΈΠΌΠΎ" for Ukrainian
Execute multiple database operations atomically.
try { BeginTran(TranType.ReadCommited); // Multiple operations var orderId = AddDoc("Orders", orderData); AddDoc("OrderItems", itemsData); ModifyDocsWhere("Products", productId) .Update("{'Stock':Sub(1)}"); CommitTran(); // All succeed together } catch { RollbackTran(); // Undo all changes throw; }
Execute code while preserving the current context state.
var result = ExecuteWithSaveContext(Context, () => { // Change collection or context here Client.SetDefaultCollection("TempData"); // Do operations... var data = DocsOf("TempData").Select(); return data; // Context automatically restored after this block });
Quick way to show "Not Implemented" message.
public override bool OnEventDimension(EventInfo info) { if (info.Action == "NewFeature") { NotImplementedMessageBox(); return true; } return false; }
Override this to customize form rendering.
public override BaseRenderForm CreateRenderForm(DOMForm form) { // Return custom render form implementation return new CustomRenderForm(this, form); }
Set the current user context (internal use).
SetUserContext();
Apply global dimensions to a collection.
var collection = new Collection(documentStorage); // Set all global dimensions SetGlobalDimensions(collection); // Set specific dimensions only SetGlobalDimensions(collection, dimType => dimType != DimensionType.Security);
Make HTTP requests to external APIs.
// GET request var response = REST.Get("https://api.example.com/users"); // POST request var result = REST.Post( "https://api.example.com/users", new { Name = "John", Email = "john@example.com" } ); // With headers REST.AddHeader("Authorization", "Bearer token123"); var data = REST.Get("https://api.example.com/protected");
Integrate AI capabilities into your application.
// Generate text var prompt = "Write a welcome message for new users"; var message = AI.Generate(prompt); MessageBox(message);
Send messages to other applications.
// Send to specific app SendMessage("NotificationService", new { Type = "Alert", Message = "New order received" }); // Notify all open instances SendMessage( "Dashboard", new { Action = "Refresh" }, isNotifyAllOpenApps: true ); // Auto-open app if not running SendMessage( "ReportGenerator", reportData, isOpenAppToNotify: true );
Get battery and power status on Windows devices.
var power = GetPowerInfo(); Log($"Battery: {power.BatteryPercent}%"); Log($"Plugged in: {power.IsOnline}"); Log($"Time remaining: {power.BatteryLifeTime}");
| Property | Type | Description |
|---|---|---|
| SessionID | Guid | Unique identifier for current session |
| Client | FractalPlatformClient | Database client for queries |
| REST | RESTClient | HTTP client for REST APIs |
| AI | AIClient | AI service client |
| Context | Context | Current execution context |
| InternalContext | Context | Internal context (private) |
| User | User | Current user information |
| Name | string | Application name (class name without "Application") |
| DatabaseName | string | Default database name |
| HasDatabase | bool | Whether database is configured |
| Instance | FractalPlatformInstance | Platform instance reference |
| Logs | List |
Application log messages |
Here's a complete example showing many features together:
public class TaskManagerApplication : BaseApplication { public override void OnStart() { // Check if user is logged in if (User.IsGuest) { // Show login form FirstDocOf("LoginForm") .OpenForm("Login"); } else { // Show main task list ShowTaskList(); } } private void ShowTaskList() { DocsWhere("Tasks", new { OwnerID = User.ID }) .OrderBy("{'DueDate':$}") .OpenForm("My Tasks"); } public override bool OnEventDimension(EventInfo info) { switch (info.Action) { case "AddTask": CreateNewDocFor("TaskTemplate", "Tasks") .OpenForm("New Task", result => { if (result.Result) { RefreshForm(); MessageBox("Task created!"); } }); return true; case "CompleteTask": ModifyDocsWhere("Tasks", info.DocID) .Update("{'Status':'Completed','CompletedDate':@Date}", GetNowDate()); RefreshForm(); return true; case "DeleteTask": MessageBox( "Delete this task?", MessageBoxButtonType.YesNo, result => { if (result.Result) { DelDoc("Tasks", info.DocID); CloseForm(); } }); return true; case "Logout": ResetToGuest(); CloseAllForms(); OnStart(); return true; } return false; } public override bool OnChangeDimension(ChangeInfo info) { // Auto-calculate priority based on due date if (info.ChangeName == "DueDate") { var dueDate = info.AfterValue.ToDateTime(); var daysUntilDue = (dueDate - GetNowDate()).Days; string priority = daysUntilDue < 1 ? "Urgent" : daysUntilDue < 7 ? "High" : "Normal"; ModifyDocsWhere("Tasks", info.DocID) .Update(DQL("{'Priority':@Priority}", priority)); RefreshForm(); return true; } return false; } public override object OnComputedDimension(ComputedInfo info) { if (info.Variable == "DaysRemaining") { var dueDate = info.FindFirstDateTimeValue("DueDate"); var days = (dueDate - GetNowDate()).Days; return days > 0 ? $"{days} days left" : "Overdue"; } return null; } public override bool OnSecurityDimension(SecurityInfo info) { // Users can only modify their own tasks if (info.OperationType == OperationType.Update || info.OperationType == OperationType.Delete) { var ownerId = info.FindFirstValue("OwnerID"); return ownerId == User.ID.ToString(); } return true; } public override bool OnTimerDimension(TimerInfo info) { // Check for overdue tasks every minute if (info.Action == "CheckOverdue") { var overdueTasks = DocsWhere("Tasks", "{'DueDate':Less(@Now),'Status':'Active'}", GetNowDate()) .Count(); if (overdueTasks > 0) { Log($"Warning: {overdueTasks} overdue tasks"); } return true; } return false; } public override bool OnAuthDimension(AuthInfo info) { if (info.Action == "Login") { var username = info.FindFirstValue("Username"); var password = info.FindFirstValue("Password"); // Validate and login user return ValidateAndLogin(username, password); } return false; } }
| Event Handler | Return true | Return false |
|---|---|---|
| OnEventDimension | Event handled | Event not handled, continue propagation |
| OnChangeDimension | Change accepted | Change rejected/not handled |
| OnOpenForm | Allow form to open | Prevent form from opening |
| OnCloseForm | Allow form to close | Prevent form from closing |
| OnSecurityDimension | Allow access | Deny access |
| OnTimerDimension | Timer event handled | Timer event not handled |
| OnAuthDimension | Authentication successful | Authentication failed |
| OnMovePageDimension | Allow navigation | Prevent navigation |
| OnMenuDimension | Menu action handled | Menu action not handled |
| OnUploadFiles | Upload processed | Upload rejected |
| OnReceiveMessage | Message handled | Message not handled |
| OnCodeConfirmed | Code confirmation handled | Code confirmation not handled |