fix.启动器一定要能够启动

This commit is contained in:
lincube
2026-04-16 19:28:58 +08:00
parent e9ff590d79
commit 59c4824425
22 changed files with 2991 additions and 115 deletions

View File

@@ -0,0 +1,106 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views"
mc:Ignorable="d"
d:DesignWidth="420"
d:DesignHeight="320"
x:Class="LanMountainDesktop.Launcher.Views.ErrorDebugWindow"
x:DataType="views:ErrorDebugWindow"
Title="调试模式"
Width="420"
Height="320"
CanResize="False"
WindowStartupLocation="CenterOwner"
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
TransparencyLevelHint="None">
<Design.DataContext>
<views:ErrorDebugWindow />
</Design.DataContext>
<Grid Margin="24" RowDefinitions="Auto,*,Auto">
<!-- 标题 -->
<TextBlock Grid.Row="0"
Text="调试设置"
FontSize="20"
FontWeight="SemiBold"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Margin="0,0,0,16" />
<!-- 设置内容 -->
<StackPanel Grid.Row="1" Spacing="16">
<!-- 开发模式开关 -->
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Padding="16,12">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="开发模式"
FontSize="14"
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<TextBlock Text="启用后自动扫描开发目录"
FontSize="12"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
Margin="0,2,0,0" />
</StackPanel>
<ToggleSwitch x:Name="DevModeToggle"
Grid.Column="1"
OnContent="开"
OffContent="关" />
</Grid>
</Border>
<!-- 应用路径选择 -->
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Padding="16,12">
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto">
<TextBlock Grid.Row="0" Grid.Column="0"
Text="应用路径"
FontSize="14"
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<TextBlock x:Name="PathTextBlock"
Grid.Row="1" Grid.Column="0"
Text="未选择"
FontSize="12"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
TextTrimming="CharacterEllipsis"
Margin="0,4,12,0" />
<Button x:Name="BrowseButton"
Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
Content="浏览..."
VerticalAlignment="Center" />
</Grid>
</Border>
<!-- 提示信息 -->
<Border Background="{DynamicResource SystemFillColorCautionBackgroundBrush}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Padding="12,10"
IsVisible="True">
<TextBlock Text="此功能仅供开发人员使用"
FontSize="12"
Foreground="{DynamicResource SystemFillColorCautionBrush}"
TextWrapping="Wrap" />
</Border>
</StackPanel>
<!-- 按钮区域 -->
<StackPanel Grid.Row="2"
Orientation="Horizontal"
HorizontalAlignment="Right"
Spacing="12"
Margin="0,16,0,0">
<Button x:Name="CancelButton"
Content="取消"
Width="80"
Height="32" />
<Button x:Name="OkButton"
Content="确定"
Width="80"
Height="32"
Theme="{DynamicResource AccentButtonTheme}" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,128 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
namespace LanMountainDesktop.Launcher.Views;
/// <summary>
/// 错误调试窗口 - 开发人员专用调试设置
/// </summary>
public partial class ErrorDebugWindow : Window
{
private string? _selectedHostPath;
/// <summary>
/// 是否启用了开发模式
/// </summary>
public bool IsDevModeEnabled { get; private set; }
/// <summary>
/// 选择的主程序路径
/// </summary>
public string? SelectedHostPath => _selectedHostPath;
public ErrorDebugWindow()
{
AvaloniaXamlLoader.Load(this);
InitializeComponents();
}
public ErrorDebugWindow(bool devModeEnabled, string? initialPath) : this()
{
IsDevModeEnabled = devModeEnabled;
_selectedHostPath = initialPath;
// 设置初始值
var devModeToggle = this.FindControl<ToggleSwitch>("DevModeToggle");
if (devModeToggle is not null)
{
devModeToggle.IsChecked = devModeEnabled;
}
UpdatePathDisplay(initialPath);
}
private void InitializeComponents()
{
// 开发模式开关
var devModeToggle = this.FindControl<ToggleSwitch>("DevModeToggle");
if (devModeToggle is not null)
{
devModeToggle.IsCheckedChanged += (s, e) =>
{
IsDevModeEnabled = devModeToggle.IsChecked ?? false;
};
}
// 浏览按钮
var browseButton = this.FindControl<Button>("BrowseButton");
if (browseButton is not null)
{
browseButton.Click += OnBrowseClick;
}
// 确定按钮
var okButton = this.FindControl<Button>("OkButton");
if (okButton is not null)
{
okButton.Click += (s, e) => Close();
}
// 取消按钮
var cancelButton = this.FindControl<Button>("CancelButton");
if (cancelButton is not null)
{
cancelButton.Click += (s, e) =>
{
// 取消时恢复原始状态
IsDevModeEnabled = false;
_selectedHostPath = null;
Close();
};
}
}
/// <summary>
/// 浏览按钮点击
/// </summary>
private async void OnBrowseClick(object? sender, RoutedEventArgs e)
{
var storageProvider = StorageProvider;
if (storageProvider is null) return;
var options = new FilePickerOpenOptions
{
Title = "选择阑山桌面主程序",
AllowMultiple = false,
FileTypeFilter = new[]
{
new FilePickerFileType("可执行文件")
{
Patterns = OperatingSystem.IsWindows()
? new[] { "*.exe" }
: new[] { "*" }
}
}
};
var result = await storageProvider.OpenFilePickerAsync(options);
if (result.Count > 0)
{
_selectedHostPath = result[0].Path.LocalPath;
UpdatePathDisplay(_selectedHostPath);
}
}
/// <summary>
/// 更新路径显示
/// </summary>
private void UpdatePathDisplay(string? path)
{
var pathTextBlock = this.FindControl<TextBlock>("PathTextBlock");
if (pathTextBlock is not null)
{
pathTextBlock.Text = string.IsNullOrEmpty(path) ? "未选择" : path;
}
}
}

View File

@@ -0,0 +1,84 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views"
mc:Ignorable="d"
d:DesignWidth="480"
d:DesignHeight="320"
x:Class="LanMountainDesktop.Launcher.Views.ErrorWindow"
x:DataType="views:ErrorWindow"
Title="阑山桌面 - 启动失败"
Width="480"
Height="320"
CanResize="False"
WindowStartupLocation="CenterScreen"
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
TransparencyLevelHint="None">
<Design.DataContext>
<views:ErrorWindow />
</Design.DataContext>
<Grid Margin="40" RowDefinitions="Auto,*,Auto">
<!-- 错误图标和标题 -->
<StackPanel Grid.Row="0" HorizontalAlignment="Center">
<!-- 错误图标 - 可点击进入调试模式(隐藏功能,无提示) -->
<Border x:Name="ErrorIconBorder"
Width="64"
Height="64"
Background="{DynamicResource SystemFillColorCriticalBackgroundBrush}"
CornerRadius="32"
HorizontalAlignment="Center">
<TextBlock x:Name="ErrorIconText"
Text="!"
FontSize="36"
FontWeight="Bold"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<TextBlock Text="启动失败"
FontSize="24"
FontWeight="Medium"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
HorizontalAlignment="Center"
Margin="0,20,0,0" />
</StackPanel>
<!-- 错误信息 -->
<StackPanel Grid.Row="1" VerticalAlignment="Center" Margin="0,20">
<TextBlock x:Name="ErrorMessageText"
Text="找不到阑山桌面应用程序。"
FontSize="14"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
TextWrapping="Wrap"
TextAlignment="Center"
LineHeight="22" />
<TextBlock Text="请确保应用程序已正确安装,或尝试重新安装。"
FontSize="13"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap"
TextAlignment="Center"
Margin="0,12,0,0"
LineHeight="20" />
</StackPanel>
<!-- 按钮区域 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Spacing="12">
<Button x:Name="RetryButton"
Content="重试"
Width="100"
Height="36"
FontSize="14"
Theme="{DynamicResource AccentButtonTheme}" />
<Button x:Name="ExitButton"
Content="退出"
Width="100"
Height="36"
FontSize="14" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,239 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
namespace LanMountainDesktop.Launcher.Views;
/// <summary>
/// 错误窗口 - 显示启动失败信息,支持调试模式(隐藏入口)
/// </summary>
public partial class ErrorWindow : Window
{
private readonly TaskCompletionSource<ErrorWindowResult> _completionSource = new();
private int _iconClickCount = 0;
private const int DebugModeClickThreshold = 5;
private bool _isDebugMode = false;
private string? _customHostPath;
private bool _devModeEnabled;
public ErrorWindow()
{
AvaloniaXamlLoader.Load(this);
// 先加载保存的状态
_devModeEnabled = LoadDevModeStateInternal();
InitializeComponents();
}
private void InitializeComponents()
{
// 错误图标点击事件(进入调试模式 - 隐藏功能)
var errorIconBorder = this.FindControl<Border>("ErrorIconBorder");
if (errorIconBorder is not null)
{
errorIconBorder.PointerPressed += OnErrorIconClick;
}
// 按钮事件
var retryButton = this.FindControl<Button>("RetryButton");
var exitButton = this.FindControl<Button>("ExitButton");
if (retryButton is not null)
{
retryButton.Click += OnRetryClick;
}
if (exitButton is not null)
{
exitButton.Click += OnExitClick;
}
}
/// <summary>
/// 设置错误消息
/// </summary>
public void SetErrorMessage(string message)
{
var errorText = this.FindControl<TextBlock>("ErrorMessageText");
if (errorText is not null)
{
errorText.Text = message;
}
}
/// <summary>
/// 获取用户选择的主程序路径
/// </summary>
public string? GetCustomHostPath() => _customHostPath;
/// <summary>
/// 是否启用了开发模式
/// </summary>
public bool IsDevModeEnabled() => _devModeEnabled;
/// <summary>
/// 等待用户选择
/// </summary>
public Task<ErrorWindowResult> WaitForChoiceAsync()
{
return _completionSource.Task;
}
/// <summary>
/// 错误图标点击事件 - 连续点击 5 次进入调试模式(隐藏功能)
/// </summary>
private void OnErrorIconClick(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{
_iconClickCount++;
if (_iconClickCount >= DebugModeClickThreshold && !_isDebugMode)
{
EnterDebugMode();
}
}
/// <summary>
/// 进入调试模式 - 显示调试窗口
/// </summary>
private async void EnterDebugMode()
{
_isDebugMode = true;
// 创建并显示调试窗口
var debugWindow = new ErrorDebugWindow(_devModeEnabled, _customHostPath)
{
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
// 订阅调试窗口关闭事件
debugWindow.Closed += (s, e) =>
{
// 更新状态
_devModeEnabled = debugWindow.IsDevModeEnabled;
_customHostPath = debugWindow.SelectedHostPath;
// 保存开发模式状态
SaveDevModeStateInternal(_devModeEnabled);
// 如果启用了开发模式且没有选择路径,自动扫描
if (_devModeEnabled && string.IsNullOrEmpty(_customHostPath))
{
ScanDevPaths();
}
};
await debugWindow.ShowDialog(this);
}
/// <summary>
/// 扫描开发路径
/// </summary>
private void ScanDevPaths()
{
var executable = OperatingSystem.IsWindows() ? "LanMountainDesktop.exe" : "LanMountainDesktop";
var possiblePaths = new[]
{
Path.Combine(AppContext.BaseDirectory, "..", "..", "LanMountainDesktop", "bin", "Debug", "net10.0", executable),
Path.Combine(AppContext.BaseDirectory, "..", "..", "LanMountainDesktop", "bin", "Release", "net10.0", executable),
Path.Combine(AppContext.BaseDirectory, "..", "LanMountainDesktop", "bin", "Debug", "net10.0", executable),
Path.Combine(AppContext.BaseDirectory, "..", "LanMountainDesktop", "bin", "Release", "net10.0", executable),
Path.Combine(AppContext.BaseDirectory, "..", "dev-test", "app-1.0.0-dev", executable),
};
foreach (var path in possiblePaths.Select(Path.GetFullPath).Distinct())
{
if (File.Exists(path))
{
_customHostPath = path;
break;
}
}
}
/// <summary>
/// 保存开发模式状态(内部方法)
/// </summary>
private static void SaveDevModeStateInternal(bool enabled)
{
try
{
var devModeFile = GetDevModeFilePath();
var dir = Path.GetDirectoryName(devModeFile);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllText(devModeFile, enabled ? "1" : "0");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Failed to save dev mode state: {ex.Message}");
}
}
/// <summary>
/// 加载开发模式状态(内部方法)
/// </summary>
private static bool LoadDevModeStateInternal()
{
try
{
var devModeFile = GetDevModeFilePath();
if (File.Exists(devModeFile))
{
var content = File.ReadAllText(devModeFile).Trim();
return content == "1";
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Failed to load dev mode state: {ex.Message}");
}
return false;
}
/// <summary>
/// 获取开发模式状态文件路径
/// </summary>
private static string GetDevModeFilePath()
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(appData, "LanMountainDesktop", ".launcher", "devmode.config");
}
/// <summary>
/// 检查是否启用了开发模式(静态方法,启动时调用)
/// </summary>
public static bool CheckDevModeEnabled()
{
return LoadDevModeStateInternal();
}
private void OnRetryClick(object? sender, RoutedEventArgs e)
{
_completionSource.TrySetResult(ErrorWindowResult.Retry);
}
private void OnExitClick(object? sender, RoutedEventArgs e)
{
_completionSource.TrySetResult(ErrorWindowResult.Exit);
}
}
/// <summary>
/// 错误窗口用户选择结果
/// </summary>
public enum ErrorWindowResult
{
/// <summary>
/// 重试
/// </summary>
Retry,
/// <summary>
/// 退出
/// </summary>
Exit
}

View File

@@ -1,22 +1,55 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views"
xmlns:ui="using:FluentAvalonia.UI.Controls"
mc:Ignorable="d"
d:DesignWidth="420"
d:DesignHeight="260"
x:Class="LanMountainDesktop.Launcher.Views.OobeWindow"
Title="阑山桌面"
x:DataType="views:OobeWindow"
Title="欢迎使用阑山桌面"
Width="420"
Height="260"
CanResize="False"
WindowStartupLocation="CenterScreen">
<Grid Margin="24" RowDefinitions="*,Auto">
<TextBlock Text="欢迎使用阑山桌面"
FontSize="26"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
WindowStartupLocation="CenterScreen"
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
TransparencyLevelHint="None">
<Design.DataContext>
<views:OobeWindow />
</Design.DataContext>
<Grid Margin="32" RowDefinitions="*,Auto">
<!-- 欢迎文本 -->
<StackPanel Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="欢迎使用"
FontSize="18"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
HorizontalAlignment="Center" />
<TextBlock Text="阑山桌面"
FontSize="32"
FontWeight="Light"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
HorizontalAlignment="Center"
Margin="0,8,0,0" />
<TextBlock Text="您的智能桌面助手"
FontSize="14"
Foreground="{DynamicResource TextFillColorTertiaryBrush}"
HorizontalAlignment="Center"
Margin="0,16,0,0" />
</StackPanel>
<!-- 进入按钮 -->
<Button Grid.Row="1"
x:Name="EnterButton"
HorizontalAlignment="Right"
Width="64"
Height="40"
Content=""
FontSize="18" />
Width="80"
Height="36"
Content="开始使用"
FontSize="14"
Background="{DynamicResource AccentFillColorDefaultBrush}"
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
CornerRadius="4" />
</Grid>
</Window>

View File

@@ -4,13 +4,17 @@ using Avalonia.Markup.Xaml;
namespace LanMountainDesktop.Launcher.Views;
internal partial class OobeWindow : Window
/// <summary>
/// OOBE首次使用体验窗口
/// </summary>
public partial class OobeWindow : Window
{
private readonly TaskCompletionSource<bool> _completionSource = new();
public OobeWindow()
{
AvaloniaXamlLoader.Load(this);
var enterButton = this.FindControl<Button>("EnterButton");
if (enterButton is not null)
{
@@ -18,6 +22,9 @@ internal partial class OobeWindow : Window
}
}
/// <summary>
/// 等待用户点击开始按钮
/// </summary>
public Task WaitForEnterAsync() => _completionSource.Task;
private void OnEnterClick(object? sender, RoutedEventArgs e)

View File

@@ -1,40 +1,57 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views"
xmlns:ui="using:FluentAvalonia.UI.Controls"
mc:Ignorable="d"
d:DesignWidth="400"
d:DesignHeight="200"
x:Class="LanMountainDesktop.Launcher.Views.SplashWindow"
x:DataType="views:SplashWindow"
Title="阑山桌面"
Width="420"
Height="240"
Width="400"
Height="200"
CanResize="False"
WindowStartupLocation="CenterScreen"
SystemDecorations="None">
<Grid Margin="24" RowDefinitions="*,Auto,Auto,Auto">
SystemDecorations="None"
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
TransparencyLevelHint="None">
<Design.DataContext>
<views:SplashWindow />
</Design.DataContext>
<Grid RowDefinitions="*,Auto,Auto">
<!-- 应用名称 -->
<TextBlock x:Name="AppNameText"
Text="阑山桌面"
FontSize="34"
FontSize="36"
FontWeight="Light"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.Row="0" />
Grid.Row="0"
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<!-- 进度条 -->
<ProgressBar x:Name="ProgressIndicator"
Grid.Row="1"
Minimum="0"
Maximum="100"
Value="0"
Height="4"
Margin="0,12,0,0"
IsIndeterminate="True" />
<TextBlock x:Name="StageText"
Height="3"
Width="200"
Margin="0,20,0,0"
IsIndeterminate="True"
Foreground="{DynamicResource AccentFillColorDefaultBrush}"
Background="{DynamicResource ControlStrokeColorDefaultBrush}" />
<!-- 状态文本 -->
<TextBlock x:Name="StatusText"
Grid.Row="2"
FontSize="12"
Foreground="#999999"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
HorizontalAlignment="Center"
Margin="0,8,0,0"
Text="" />
<TextBlock x:Name="DetailText"
Grid.Row="3"
FontSize="11"
Foreground="#BBBBBB"
HorizontalAlignment="Center"
Margin="0,2,0,0"
Text="" />
Margin="0,12,0,24"
Text="正在启动..." />
</Grid>
</Window>

View File

@@ -1,48 +1,92 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Views;
internal partial class SplashWindow : Window, ISplashStageReporter
/// <summary>
/// 启动画面窗口 - 简洁设计
/// </summary>
public partial class SplashWindow : Window, ISplashStageReporter
{
private static readonly (string Stage, string Label, double Progress)[] StageMap =
[
("bootstrap", "正在初始化...", 10),
("silentUpdate", "正在应用更新...", 35),
("pluginTasks", "正在处理插件...", 65),
("launchHost", "正在启动...", 90),
];
public SplashWindow()
{
AvaloniaXamlLoader.Load(this);
}
/// <summary>
/// 更新进度和状态
/// </summary>
public void Report(string stage, string message)
{
var (label, progress) = ResolveStageInfo(stage);
Dispatcher.UIThread.Post(() =>
{
var statusText = this.GetControl<TextBlock>("StatusText");
var progressIndicator = this.GetControl<ProgressBar>("ProgressIndicator");
var stageText = this.GetControl<TextBlock>("StageText");
var detailText = this.GetControl<TextBlock>("DetailText");
var progressIndicator = this.GetControl<ProgressBar>("ProgressIndicator");
// 更新状态文本
statusText.Text = message;
stageText.Text = label;
detailText.Text = message;
progressIndicator.IsIndeterminate = false;
progressIndicator.Value = progress;
// 根据阶段更新进度
var progress = ResolveProgress(stage);
if (progress > 0)
{
progressIndicator.IsIndeterminate = false;
progressIndicator.Value = progress;
}
else
{
progressIndicator.IsIndeterminate = true;
}
});
}
private static (string Label, double Progress) ResolveStageInfo(string stage)
/// <summary>
/// 更新进度0-100
/// </summary>
public void UpdateProgress(int percent, string? message = null)
{
foreach (var (s, label, progress) in StageMap)
Dispatcher.UIThread.Post(() =>
{
if (string.Equals(s, stage, StringComparison.OrdinalIgnoreCase))
{
return (label, progress);
}
}
var statusText = this.GetControl<TextBlock>("StatusText");
var progressIndicator = this.GetControl<ProgressBar>("ProgressIndicator");
return (stage, 0);
if (!string.IsNullOrEmpty(message))
{
statusText.Text = message;
}
progressIndicator.IsIndeterminate = false;
progressIndicator.Value = Math.Clamp(percent, 0, 100);
});
}
/// <summary>
/// 更新状态文本
/// </summary>
public void UpdateStatus(string message)
{
Dispatcher.UIThread.Post(() =>
{
var statusText = this.GetControl<TextBlock>("StatusText");
statusText.Text = message;
});
}
/// <summary>
/// 根据阶段名称解析进度值
/// </summary>
private static int ResolveProgress(string stage)
{
return stage.ToLowerInvariant() switch
{
"initializing" => 10,
"update" => 30,
"plugins" => 50,
"launch" => 70,
"ready" => 100,
_ => 0
};
}
}