feat(01-06): build WPF shell — MainWindow XAML, ViewModels, LogPanelSink wiring
- Add FeatureTabBase UserControl with ProgressBar/TextBlock/CancelButton strip (Visibility bound to IsRunning, shown only during operations) - Add MainWindowViewModel with TenantProfiles ObservableCollection, ConnectCommand, ClearSessionCommand, ManageProfilesCommand, ProgressUpdatedMessage subscription - Add ProfileManagementViewModel wrapping ProfileService CRUD with input validation - Add SettingsViewModel (extends FeatureViewModelBase) with language/folder settings - Update MainWindow.xaml: DockPanel shell with Toolbar, TabControl (8 tabs), 150px RichTextBox LogPanel, StatusBar (tenant name | ProgressStatus | ProgressPercentage) - MainWindow.xaml.cs: DI constructor, DataContext=viewModel, LoadProfilesAsync on Loaded - App.xaml.cs: register all services, wire LogPanelSink after MainWindow resolved, register DispatcherUnhandledException and UnobservedTaskException global handlers - App.xaml: add BoolToVisibilityConverter resource
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using SharepointToolbox.Infrastructure.Auth;
|
||||
using SharepointToolbox.Infrastructure.Logging;
|
||||
using SharepointToolbox.Services;
|
||||
using SharepointToolbox.ViewModels;
|
||||
using SharepointToolbox.ViewModels.Tabs;
|
||||
using System.Windows;
|
||||
|
||||
namespace SharepointToolbox;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
[STAThread]
|
||||
@@ -16,7 +20,7 @@ public partial class App : Application
|
||||
using IHost host = Host.CreateDefaultBuilder(args)
|
||||
.UseSerilog((ctx, cfg) => cfg
|
||||
.WriteTo.File(
|
||||
System.IO.Path.Combine(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"SharepointToolbox", "logs", "app-.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
@@ -27,16 +31,49 @@ public partial class App : Application
|
||||
host.Start();
|
||||
App app = new();
|
||||
app.InitializeComponent();
|
||||
app.MainWindow = host.Services.GetRequiredService<MainWindow>();
|
||||
|
||||
var mainWindow = host.Services.GetRequiredService<MainWindow>();
|
||||
|
||||
// Wire LogPanelSink now that we have the RichTextBox
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.File(
|
||||
Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"SharepointToolbox", "logs", "app-.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
retainedFileCountLimit: 30)
|
||||
.WriteTo.Sink(new LogPanelSink(mainWindow.GetLogPanel()))
|
||||
.CreateLogger();
|
||||
|
||||
// Global exception handlers
|
||||
app.DispatcherUnhandledException += (s, e) =>
|
||||
{
|
||||
Log.Fatal(e.Exception, "Unhandled UI exception");
|
||||
MessageBox.Show(
|
||||
$"A fatal error occurred:\n{e.Exception.Message}\n\nCheck log for details.",
|
||||
"Fatal Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
e.Handled = true;
|
||||
};
|
||||
TaskScheduler.UnobservedTaskException += (s, e) =>
|
||||
{
|
||||
Log.Fatal(e.Exception, "Unobserved task exception");
|
||||
e.SetObserved();
|
||||
};
|
||||
|
||||
app.MainWindow = mainWindow;
|
||||
app.MainWindow.Visibility = Visibility.Visible;
|
||||
app.Run();
|
||||
}
|
||||
|
||||
private static void RegisterServices(HostBuilderContext ctx, IServiceCollection services)
|
||||
{
|
||||
// Placeholder — services registered in subsequent plans
|
||||
services.AddSingleton<MsalClientFactory>();
|
||||
services.AddSingleton<SessionManager>();
|
||||
services.AddSingleton<ProfileService>();
|
||||
services.AddSingleton<SettingsService>();
|
||||
services.AddSingleton<MainWindowViewModel>();
|
||||
services.AddTransient<ProfileManagementViewModel>();
|
||||
services.AddTransient<SettingsViewModel>();
|
||||
services.AddSingleton<MainWindow>();
|
||||
// LogPanelSink registered in plan 01-06 after MainWindow is created
|
||||
// (requires RichTextBox reference from MainWindow)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user