Skip to content

Instantly share code, notes, and snippets.

@bytesandwich
Created March 20, 2025 00:55
Show Gist options
  • Save bytesandwich/a5eed3d5eceb7b04af328ea27a68642c to your computer and use it in GitHub Desktop.
Save bytesandwich/a5eed3d5eceb7b04af328ea27a68642c to your computer and use it in GitHub Desktop.
.NET Maui simple hierarchical debug view window
using System.Timers;
public partial class App : Application
{
private Label? debugLabel;
private System.Timers.Timer? debugUpdateTimer;
private Window? debugWindow;
public App()
{
InitializeComponent();
MainPage = new AppShell();
StartDebugUpdateTimer();
}
private void StartDebugUpdateTimer()
{
// Set up the timer to update every 2 seconds
debugUpdateTimer = new(2000); // 2000ms = 2 seconds
debugUpdateTimer.Elapsed += (sender, args) =>
{
// Get the UI hierarchy string
if (debugWindow == null)
{
MainThread.BeginInvokeOnMainThread(() =>
{
// Create a Label to display the debug text
debugLabel = new Label
{
Text = "Loading debug info...",
FontSize = 14,
LineBreakMode = LineBreakMode.WordWrap,
Padding = new Thickness(10)
};
// Create a ScrollView to contain the Label
var scrollView = new ScrollView
{
Content = debugLabel
};
debugWindow = new Window(
new ContentPage
{
Title = "Debug UI Hierarchy",
Content = scrollView
})
{
Width = 700,
Height = 500,
X = 100,
Y = 100
};
Application.Current?.OpenWindow(debugWindow);
Application.Current?.ActivateWindow(debugWindow);
});
return;
}
string debugText = DebugLogger.GetVisualTreeAsString();
// Update the Label on the UI thread
MainThread.BeginInvokeOnMainThread(() =>
{
debugLabel.Text = debugText;
});
};
// Start the timer
debugUpdateTimer.Start();
}
protected override void OnStart()
{
base.OnStart();
// Ensure the timer starts when the app starts
debugUpdateTimer?.Start();
}
protected override void OnSleep()
{
base.OnSleep();
// Stop the timer when the app goes to sleep
debugUpdateTimer?.Stop();
}
protected override void OnResume()
{
base.OnResume();
// Resume the timer when the app resumes
debugUpdateTimer?.Start();
}
}
using System.Text;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
public static class DebugLogger
{
public static string GetVisualTreeAsString()
{
if (Application.Current == null)
{
return "No current application instance found.";
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("========== UI Hierarchy ==========");
foreach (var window in Application.Current.Windows)
{
stringBuilder.AppendLine($"Window: {window.Title}");
if (window.Page != null)
{
AppendElementHierarchy(stringBuilder, window.Page, 1);
}
}
stringBuilder.AppendLine("==================================");
return stringBuilder.ToString();
}
private static void AppendElementHierarchy(StringBuilder stringBuilder, IView element, int indentLevel)
{
string indent = new string(' ', indentLevel * 2);
stringBuilder.AppendLine($"{indent}- {element.GetType().Name}");
if (element is Layout layout) // Handles StackLayout, Grid, FlexLayout, etc.
{
foreach (var child in layout.Children)
{
AppendElementHierarchy(stringBuilder, child, indentLevel + 1);
}
}
else if (element is ContentView contentView && contentView.Content != null) // Handles ContentView
{
AppendElementHierarchy(stringBuilder, contentView.Content, indentLevel + 1);
}
else if (element is ScrollView scrollView && scrollView.Content != null) // Handles ScrollView
{
AppendElementHierarchy(stringBuilder, scrollView.Content, indentLevel + 1);
}
else if (element is Frame frame && frame.Content != null) // Handles Frame
{
AppendElementHierarchy(stringBuilder, frame.Content, indentLevel + 1);
}
else if (element is Border border && border.Content != null) // Handles Border
{
AppendElementHierarchy(stringBuilder, border.Content, indentLevel + 1);
}
else if (element is Shell shell) // Handles Shell Navigation
{
foreach (var item in shell.Items)
{
AppendShellItemHierarchy(stringBuilder, item, indentLevel + 1);
}
}
}
private static void AppendShellItemHierarchy(StringBuilder stringBuilder, ShellItem item, int indentLevel)
{
string indent = new string(' ', indentLevel * 2);
stringBuilder.AppendLine($"{indent}- ShellItem: {item.Title}");
foreach (var section in item.Items)
{
stringBuilder.AppendLine($"{indent} * Section: {section.Title}");
foreach (var content in section.Items)
{
if (content is ShellContent shellContent && shellContent.Content is Page page)
{
AppendElementHierarchy(stringBuilder, page, indentLevel + 2);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment