Files
duplicati/Duplicati/GUI/Duplicati.GUI.TrayIcon/ErrorDialog.cs
T

166 lines
5.8 KiB
C#

// Copyright (C) 2026, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media;
using Avalonia.Layout;
using Avalonia.Themes.Fluent;
using ScrollBarVisibility = Avalonia.Controls.Primitives.ScrollBarVisibility;
namespace Duplicati.GUI.TrayIcon
{
/// <summary>
/// Helper class for showing error dialogs
/// </summary>
public static class ErrorDialog
{
/// <summary>
/// Shows an error dialog for the crash, being very defensive to avoid additional crashes.
/// This should only be called when the hosted server instance is crashing.
/// </summary>
/// <param name="ex">The exception to show</param>
public static void ShowErrorDialog(Exception ex)
{
try
{
// Build a simple Avalonia application to show the dialog
var builder = AppBuilder
.Configure<Application>()
.UsePlatformDetect();
var app = builder.SetupWithoutStarting().Instance;
if (app == null)
return;
// Apply a simple theme so controls look native
app.Styles.Add(new FluentTheme());
// Create the message box window
var window = new Window
{
Title = "Duplicati TrayIcon Error",
Width = 600,
Height = 400,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
CanResize = true,
ShowInTaskbar = false,
Topmost = true
};
// Create the content
var stackPanel = new StackPanel
{
Margin = new Thickness(20),
Spacing = 10
};
var titleBlock = new TextBlock
{
Text = "A fatal error occurred",
FontSize = 16,
FontWeight = FontWeight.Bold,
TextWrapping = TextWrapping.Wrap
};
var messageBlock = new TextBlock
{
Text = ex.Message,
TextWrapping = TextWrapping.Wrap
};
// Details label
var detailsLabel = new TextBlock
{
Text = "Details:",
FontWeight = FontWeight.Bold,
Margin = new Thickness(0, 10, 0, 0)
};
// Scrollable text area with full exception details
var exceptionText = new TextBox
{
Text = ex.ToString(),
IsReadOnly = true,
TextWrapping = TextWrapping.NoWrap,
AcceptsReturn = true,
FontFamily = new FontFamily("Consolas, Monaco, Courier New, monospace"),
FontSize = 10
};
// Explicitly wrap in a ScrollViewer for proper scrolling
var scrollViewer = new ScrollViewer
{
Content = exceptionText,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Height = 200
};
var buttonPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 20, 0, 0)
};
var okButton = new Button
{
Content = "OK",
Width = 100,
HorizontalContentAlignment = HorizontalAlignment.Center
};
okButton.Click += (_, _) =>
{
try
{
window.Close();
}
catch
{
// Ignore errors closing window
}
};
buttonPanel.Children.Add(okButton);
stackPanel.Children.Add(titleBlock);
stackPanel.Children.Add(messageBlock);
stackPanel.Children.Add(detailsLabel);
stackPanel.Children.Add(scrollViewer);
stackPanel.Children.Add(buttonPanel);
window.Content = stackPanel;
app.Run(window);
}
catch
{
}
}
}
}