LogViewerControl
Scrollable control that displays log entries from the library's LogService, updating automatically as new entries arrive.
Overview
LogViewerControl renders the entries held by an ILogService as a live, scrolling list. It subscribes to the service's LogAdded and LogsCleared events, so the view updates automatically whenever the application or the library writes a log entry. Each entry is rendered through LogEntry.ToMarkup(), which color-codes the level and dims the timestamp and category.
Internally the control wraps a ScrollablePanelControl (with AutoScroll enabled), so it inherits smooth scrolling, an optional scrollbar, and mouse-wheel support. New entries appended at the bottom automatically scroll into view unless the user has scrolled away. The control honors the service's MaxBufferSize, trimming the oldest displayed entries when the buffer overflows.
LogViewerControl is thread-safe: log events may be raised from any thread. Incoming entries are placed on a concurrent queue and applied on the UI thread during the next paint, so background logging never races with rendering. The control supports an optional Title line and can filter what it shows by minimum log level and by category.
See also: ListControl, MarkupControl
Quick Start
var logViewer = new LogViewerControl(windowSystem.LogService)
{
Title = "Library Logs"
};
var window = new WindowBuilder(windowSystem)
.WithTitle("Log Viewer")
.WithSize(80, 25)
.Centered()
.AddControl(logViewer)
.BuildAndShow();
There is no fluent builder or
Controls.LogViewer()factory for this control. Construct it directly with its constructor, passing anILogService(typicallywindowSystem.LogService).
Construction
LogViewerControl has a single constructor that binds it to a log service:
public LogViewerControl(ILogService logService)
| Parameter | Type | Description |
|---|---|---|
logService |
ILogService |
The log service whose entries are displayed. Must not be null (throws ArgumentNullException). Existing entries are loaded immediately; new entries appear as they are logged. |
// Bind to the window system's built-in log service
var viewer = new LogViewerControl(windowSystem.LogService);
// Configure via object initializer
var viewer = new LogViewerControl(windowSystem.LogService)
{
Title = "Diagnostics",
AutoScroll = true,
FilterLevel = LogLevel.Information,
Name = "logViewer"
};
Properties
| Property | Type | Default | Description |
|---|---|---|---|
AutoScroll |
bool |
true |
Whether to automatically scroll to show new entries. Delegates to the internal ScrollablePanelControl. |
FilterLevel |
LogLevel |
LogLevel.Trace |
Minimum log level to display; entries below this level are filtered out. Changing it refreshes the view. |
FilterCategory |
string? |
null |
Category filter. When null, all categories are shown; otherwise only entries whose Category matches exactly. Changing it refreshes the view. |
Title |
string? |
null |
Optional title rendered on a single line above the entries (markup-formatted). When focused, the title is shown in cyan; otherwise grey. |
IsEnabled |
bool |
true |
Enables or disables interaction (keyboard handling and focus). |
HasFocus |
bool |
(computed) | Whether the control currently has keyboard focus. |
CanReceiveFocus |
bool |
(computed) | True when the control is visible and enabled. |
WantsMouseEvents |
bool |
true |
Indicates the control consumes mouse events. |
Standard BaseControl members such as Name, Width, Visible, Margin, and HorizontalAlignment also apply.
Events
| Event | Arguments | Description |
|---|---|---|
MouseClick |
EventHandler<MouseEventArgs> |
Declared for interface compatibility; not raised by this control. |
MouseDoubleClick |
EventHandler<MouseEventArgs> |
Declared for interface compatibility; not raised by this control. |
MouseRightClick |
EventHandler<MouseEventArgs> |
Fired when the control is right-clicked (mouse button 3). |
MouseEnter |
EventHandler<MouseEventArgs> |
Declared for interface compatibility; not raised by this control. |
MouseLeave |
EventHandler<MouseEventArgs> |
Declared for interface compatibility; not raised by this control. |
MouseMove |
EventHandler<MouseEventArgs> |
Declared for interface compatibility; not raised by this control. |
The log content itself comes from the bound
ILogService. To react to new log entries programmatically, subscribe toILogService.LogAddedrather than to this control's events.
Keyboard Support
| Key | Action |
|---|---|
| Up Arrow | Scroll up one line |
| Down Arrow | Scroll down one line |
| Page Up | Scroll up by one viewport height |
| Page Down | Scroll down by one viewport height |
| Home | Scroll to the top |
| End | Scroll to the bottom |
| Delete | Clear all logs (calls LogService.ClearLogs()) |
| Ctrl+C | Clear all logs (calls LogService.ClearLogs()) |
Keyboard input is only processed when the control is enabled and focused.
Mouse Support
| Action | Result |
|---|---|
| Right Click | Fires MouseRightClick |
| Scroll Wheel | Scrolls the log up/down (handled by the internal scroll panel) |
| Other mouse events | Delegated to the internal ScrollablePanelControl |
Examples
Basic Log Viewer Window
var logViewer = new LogViewerControl(ws.LogService)
{
Title = "Library Logs",
Name = "logViewer"
};
var window = new WindowBuilder(ws)
.WithTitle("Log Viewer")
.WithSize(80, 25)
.Centered()
.AddControl(logViewer)
.OnKeyPressed((s, e) =>
{
if (e.KeyInfo.Key == ConsoleKey.Escape)
{
ws.CloseWindow((Window)s!);
e.Handled = true;
}
})
.BuildAndShow();
Filtering by Level
// Only show warnings and above
var viewer = new LogViewerControl(windowSystem.LogService)
{
Title = "Warnings & Errors",
FilterLevel = LogLevel.Warning
};
window.AddControl(viewer);
Filtering by Category
// Only show entries logged under the "Window" category
var viewer = new LogViewerControl(windowSystem.LogService)
{
Title = "Window Events",
FilterCategory = "Window"
};
window.AddControl(viewer);
Writing to the Log
The control reflects whatever is written to its bound service. Use the ILogService convenience methods:
var log = windowSystem.LogService;
log.LogInfo("Application started", category: "System");
log.LogDebug("Loaded 42 items");
log.LogWarning("Cache nearly full", category: "Cache");
try
{
DoWork();
}
catch (Exception ex)
{
log.LogError("Work failed", ex, category: "Worker");
}
Never use
Console.WriteLinein a SharpConsoleUI application — it corrupts the UI. Route diagnostics throughLogServiceand surface them with a LogViewerControl instead.
Logging From a Background Thread
The control is thread-safe, so it is fine to log from background work:
var viewer = new LogViewerControl(windowSystem.LogService) { Title = "Activity" };
window.AddControl(viewer);
_ = Task.Run(async () =>
{
for (int i = 0; i < 100; i++)
{
windowSystem.LogService.LogInfo($"Processed batch {i}", category: "Worker");
await Task.Delay(200);
}
});
Entries logged from the background thread are queued and rendered on the next paint; with AutoScroll enabled the view follows the newest entry.
Adjusting the Retained Buffer
The number of entries retained (and therefore trimmed in the viewer) is governed by the service's MaxBufferSize:
windowSystem.LogService.MaxBufferSize = 1000; // keep the last 1000 entries
var viewer = new LogViewerControl(windowSystem.LogService) { Title = "Logs" };
window.AddControl(viewer);
Best Practices
- Bind to the system log service: Pass
windowSystem.LogServiceso the viewer shows the library's own diagnostics alongside your messages. - Filter for focus: Set
FilterLevel(and optionallyFilterCategory) to keep noisy logs readable. - Use categories consistently: Logging with a
categorymakesFilterCategoryand visual grouping useful. - Keep AutoScroll on for live tails: Leave
AutoScroll = trueso new entries stay visible; users scrolling up temporarily pauses the follow behavior. - Tune MaxBufferSize: Adjust the service's
MaxBufferSizeto balance history depth against memory. - Never write to the console: Route all diagnostics through
LogService; console output corrupts the UI.
See Also
- ListControl - For scrollable selectable lists
- MarkupControl - For static markup-formatted text (used internally per entry)