From c36e1edeede184581f22eacc18c046eb5e23e698 Mon Sep 17 00:00:00 2001 From: lindexi Date: Wed, 16 Mar 2022 21:22:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E6=B5=8B=E8=AF=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WPFDemo.Api/CommandLines/Options.cs | 13 +++ .../WPFDemo.Api/Properties/AssemblyInfo.cs | 6 ++ .../WPFDemo.Api/Startup/Foo1Startup.cs | 22 +++++ .../WPFDemo.Api/Startup/OptionStartup.cs | 16 ++++ .../AssemblyMetadataExporter.cs | 34 +++++++ .../StartupTaskFramework/StartupContext.cs | 42 +++++++++ .../StartupTaskFramework/StartupLogger.cs | 29 ++++++ .../StartupTaskFramework/StartupManager.cs | 25 ++++++ .../StartupTaskFramework/StartupNodes.cs | 36 ++++++++ .../StartupTaskFramework/StartupTask.cs | 24 +++++ demo/WPFDemo/WPFDemo.Api/WPFDemo.Api.csproj | 20 +++++ demo/WPFDemo/WPFDemo.App/App.xaml | 8 ++ demo/WPFDemo/WPFDemo.App/App.xaml.cs | 17 ++++ demo/WPFDemo/WPFDemo.App/AssemblyInfo.cs | 10 +++ demo/WPFDemo/WPFDemo.App/MainWindow.xaml | 12 +++ demo/WPFDemo/WPFDemo.App/MainWindow.xaml.cs | 26 ++++++ demo/WPFDemo/WPFDemo.App/Program.cs | 88 +++++++++++++++++++ .../WPFDemo.App/Properties/AssemblyInfo.cs | 6 ++ .../Properties/launchSettings.json | 8 ++ .../WPFDemo.App/Startup/BusinessStartup.cs | 28 ++++++ .../WPFDemo.App/Startup/MainWindowStartup.cs | 23 +++++ .../MainThreadDispatcher.cs | 16 ++++ demo/WPFDemo/WPFDemo.App/WPFDemo.App.csproj | 22 +++++ .../WPFDemo.Lib1/Properties/AssemblyInfo.cs | 6 ++ .../WPFDemo.Lib1/Startup/Foo2Startup.cs | 16 ++++ .../WPFDemo.Lib1/Startup/Foo3Startup.cs | 16 ++++ .../WPFDemo.Lib1/Startup/LibStartup.cs | 15 ++++ demo/WPFDemo/WPFDemo.Lib1/WPFDemo.Lib1.csproj | 17 ++++ dotnetCampus.ApplicationStartupManager.sln | 27 +++++- .../StartupManagerBase.cs | 4 +- 30 files changed, 628 insertions(+), 4 deletions(-) create mode 100644 demo/WPFDemo/WPFDemo.Api/CommandLines/Options.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/Properties/AssemblyInfo.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/Startup/Foo1Startup.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/Startup/OptionStartup.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/AssemblyMetadataExporter.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupContext.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupLogger.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupManager.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupNodes.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupTask.cs create mode 100644 demo/WPFDemo/WPFDemo.Api/WPFDemo.Api.csproj create mode 100644 demo/WPFDemo/WPFDemo.App/App.xaml create mode 100644 demo/WPFDemo/WPFDemo.App/App.xaml.cs create mode 100644 demo/WPFDemo/WPFDemo.App/AssemblyInfo.cs create mode 100644 demo/WPFDemo/WPFDemo.App/MainWindow.xaml create mode 100644 demo/WPFDemo/WPFDemo.App/MainWindow.xaml.cs create mode 100644 demo/WPFDemo/WPFDemo.App/Program.cs create mode 100644 demo/WPFDemo/WPFDemo.App/Properties/AssemblyInfo.cs create mode 100644 demo/WPFDemo/WPFDemo.App/Properties/launchSettings.json create mode 100644 demo/WPFDemo/WPFDemo.App/Startup/BusinessStartup.cs create mode 100644 demo/WPFDemo/WPFDemo.App/Startup/MainWindowStartup.cs create mode 100644 demo/WPFDemo/WPFDemo.App/StartupTaskFramework/MainThreadDispatcher.cs create mode 100644 demo/WPFDemo/WPFDemo.App/WPFDemo.App.csproj create mode 100644 demo/WPFDemo/WPFDemo.Lib1/Properties/AssemblyInfo.cs create mode 100644 demo/WPFDemo/WPFDemo.Lib1/Startup/Foo2Startup.cs create mode 100644 demo/WPFDemo/WPFDemo.Lib1/Startup/Foo3Startup.cs create mode 100644 demo/WPFDemo/WPFDemo.Lib1/Startup/LibStartup.cs create mode 100644 demo/WPFDemo/WPFDemo.Lib1/WPFDemo.Lib1.csproj diff --git a/demo/WPFDemo/WPFDemo.Api/CommandLines/Options.cs b/demo/WPFDemo/WPFDemo.Api/CommandLines/Options.cs new file mode 100644 index 0000000..62494f4 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/CommandLines/Options.cs @@ -0,0 +1,13 @@ +using dotnetCampus.Cli; + +namespace WPFDemo.Api.CommandLines +{ + /// + /// 启动参数 + /// + public class Options + { + [Option("Name")] + public string? Name { set; get; } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/Properties/AssemblyInfo.cs b/demo/WPFDemo/WPFDemo.Api/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e44dfdc --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Telescope; + +using WPFDemo.Api.StartupTaskFramework; + +[assembly: MarkExport(typeof(StartupTask), typeof(StartupTaskAttribute))] diff --git a/demo/WPFDemo/WPFDemo.Api/Startup/Foo1Startup.cs b/demo/WPFDemo/WPFDemo.Api/Startup/Foo1Startup.cs new file mode 100644 index 0000000..9377dba --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/Startup/Foo1Startup.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using dotnetCampus.ApplicationStartupManager; + +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.Api.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.CoreUI, AfterTasks = StartupNodes.Foundation)] + public class Foo1Startup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + context.Logger.LogInfo("Foo1 Startup"); + return base.RunAsync(context); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/Startup/OptionStartup.cs b/demo/WPFDemo/WPFDemo.Api/Startup/OptionStartup.cs new file mode 100644 index 0000000..22c95b0 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/Startup/OptionStartup.cs @@ -0,0 +1,16 @@ +using dotnetCampus.ApplicationStartupManager; +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.Api.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.Foundation, AfterTasks = "LibStartup")] + class OptionStartup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + context.Logger.LogInfo("Command " + context.CommandLineOptions.Name); + + return CompletedTask; + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/AssemblyMetadataExporter.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/AssemblyMetadataExporter.cs new file mode 100644 index 0000000..d5f3e99 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/AssemblyMetadataExporter.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Telescope; + +namespace WPFDemo.Api.StartupTaskFramework +{ + public class AssemblyMetadataExporter + { + public AssemblyMetadataExporter(Assembly[] assemblies) + { + _assemblies = assemblies; + } + + public IEnumerable ExportStartupTasks() + { + var collection = Export(); + return collection.Select(x => new StartupTaskMetadata(x.RealType.Name.Replace("Startup", ""), x.CreateInstance) + { + Scheduler = x.Attribute.Scheduler, + BeforeTasks = x.Attribute.BeforeTasks, + AfterTasks = x.Attribute.AfterTasks, + //Categories = x.Attribute.Categories, + CriticalLevel = x.Attribute.CriticalLevel, + }); + } + + public IEnumerable> Export() where TAttribute : Attribute + { + return AttributedTypes.FromAssembly(_assemblies); + } + + private readonly Assembly[] _assemblies; + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupContext.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupContext.cs new file mode 100644 index 0000000..3001cb5 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupContext.cs @@ -0,0 +1,42 @@ +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Cli; +using dotnetCampus.Configurations; +using dotnetCampus.Configurations.Core; +using WPFDemo.Api.CommandLines; + +namespace WPFDemo.Api.StartupTaskFramework +{ + public class StartupContext : IStartupContext + { + public StartupContext(IStartupContext startupContext, CommandLine commandLine, StartupLogger logger, FileConfigurationRepo configuration, IAppConfigurator configs) + { + _startupContext = startupContext; + Logger = logger; + Configuration = configuration; + Configs = configs; + CommandLine = commandLine; + CommandLineOptions = CommandLine.As(); + } + + public StartupLogger Logger { get; } + + public CommandLine CommandLine { get; } + + public Options CommandLineOptions { get; } + + public FileConfigurationRepo Configuration { get; } + + public IAppConfigurator Configs { get; } + + public Task ReadCacheAsync(string key, string @default = "") + { + return Configuration.TryReadAsync(key, @default); + } + + private readonly IStartupContext _startupContext; + public Task WaitStartupTaskAsync(string startupKey) + { + return _startupContext.WaitStartupTaskAsync(startupKey); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupLogger.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupLogger.cs new file mode 100644 index 0000000..d5d8727 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupLogger.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using System.Text; + +using dotnetCampus.ApplicationStartupManager; + +namespace WPFDemo.Api.StartupTaskFramework +{ + /// + /// 和项目关联的日志 + /// + public class StartupLogger : StartupLoggerBase + { + public void LogInfo(string message) + { + Debug.WriteLine(message); + } + + public override void ReportResult(IReadOnlyList wrappers) + { + var stringBuilder = new StringBuilder(); + foreach (var keyValuePair in MilestoneDictionary) + { + stringBuilder.AppendLine($"{keyValuePair.Key} - [{keyValuePair.Value.threadName}] Start:{keyValuePair.Value.start} Elapsed:{keyValuePair.Value.elapsed}"); + } + + Debug.WriteLine(stringBuilder.ToString()); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupManager.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupManager.cs new file mode 100644 index 0000000..e5e67d7 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupManager.cs @@ -0,0 +1,25 @@ +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Cli; +using dotnetCampus.Configurations.Core; + +namespace WPFDemo.Api.StartupTaskFramework +{ + /// + /// 和项目关联的启动管理器,用来注入业务相关的逻辑 + /// + public class StartupManager : StartupManagerBase + { + public StartupManager(CommandLine commandLine, FileConfigurationRepo configuration, Func fastFailAction, IMainThreadDispatcher mainThreadDispatcher) : base(new StartupLogger(), fastFailAction, mainThreadDispatcher) + { + var appConfigurator = configuration.CreateAppConfigurator(); + Context = new StartupContext(StartupContext, commandLine, (StartupLogger) Logger, configuration, appConfigurator); + } + + private StartupContext Context { get; } + + protected override Task ExecuteStartupTaskAsync(StartupTaskBase startupTask, IStartupContext context, bool uiOnly) + { + return base.ExecuteStartupTaskAsync(startupTask, Context, uiOnly); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupNodes.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupNodes.cs new file mode 100644 index 0000000..225a7a7 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupNodes.cs @@ -0,0 +1,36 @@ +namespace WPFDemo.Api.StartupTaskFramework +{ + /// + /// 包含预设的启动节点。 + /// + public class StartupNodes + { + /// + /// 基础服务(日志、异常处理、容器、生命周期管理等)请在此节点之前启动,其他业务请在此之后启动。 + /// + public const string Foundation = "Foundation"; + + /// + /// 需要在任何一个 Window 创建之前启动的任务请在此节点之前。 + /// 此节点之后将开始启动 UI。 + /// + public const string CoreUI = "CoreUI"; + + /// + /// 需要在主 创建之后启动的任务请在此节点之后。 + /// 此节点完成则代表主要 UI 已经初始化完毕(但不一定已显示)。 + /// + public const string UI = "UI"; + + /// + /// 应用程序已完成启动。如果应该显示一个窗口,则此窗口已布局、渲染完毕,对用户完全可见,可开始交互。 + /// 不被其他业务依赖的模块可在此节点之后启动。 + /// + public const string AppReady = "AppReady"; + + /// + /// 任何不关心何时启动的启动任务应该设定为在此节点之前完成。 + /// + public const string StartupCompleted = "StartupCompleted"; + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupTask.cs b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupTask.cs new file mode 100644 index 0000000..3e3d1c6 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/StartupTaskFramework/StartupTask.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using dotnetCampus.ApplicationStartupManager; + +namespace WPFDemo.Api.StartupTaskFramework +{ + /// + /// 表示一个和当前业务强相关的启动任务 + /// + public class StartupTask : StartupTaskBase + { + protected sealed override Task RunAsync(IStartupContext context) + { + return RunAsync((StartupContext) context); + } + + protected virtual Task RunAsync(StartupContext context) + { + return CompletedTask; + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Api/WPFDemo.Api.csproj b/demo/WPFDemo/WPFDemo.Api/WPFDemo.Api.csproj new file mode 100644 index 0000000..8f375f9 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Api/WPFDemo.Api.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + false + + + + + + + + + + + + + diff --git a/demo/WPFDemo/WPFDemo.App/App.xaml b/demo/WPFDemo/WPFDemo.App/App.xaml new file mode 100644 index 0000000..97b0aec --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/demo/WPFDemo/WPFDemo.App/App.xaml.cs b/demo/WPFDemo/WPFDemo.App/App.xaml.cs new file mode 100644 index 0000000..7ad2734 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace WPFDemo.App +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/demo/WPFDemo/WPFDemo.App/AssemblyInfo.cs b/demo/WPFDemo/WPFDemo.App/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/demo/WPFDemo/WPFDemo.App/MainWindow.xaml b/demo/WPFDemo/WPFDemo.App/MainWindow.xaml new file mode 100644 index 0000000..b71d7cc --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/demo/WPFDemo/WPFDemo.App/MainWindow.xaml.cs b/demo/WPFDemo/WPFDemo.App/MainWindow.xaml.cs new file mode 100644 index 0000000..921bb55 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/MainWindow.xaml.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace WPFDemo.App +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.App/Program.cs b/demo/WPFDemo/WPFDemo.App/Program.cs new file mode 100644 index 0000000..ffcb547 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/Program.cs @@ -0,0 +1,88 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using System.Threading.Tasks; + +using dotnetCampus.Cli; +using dotnetCampus.Configurations.Core; + +using WPFDemo.Api.Startup; +using WPFDemo.Api.StartupTaskFramework; +using WPFDemo.App.StartupTaskFramework; +using WPFDemo.Lib1.Startup; + +namespace WPFDemo.App +{ + class Program + { + [STAThread] + static void Main(string[] args) + { + var commandLine = CommandLine.Parse(args); + + var app = new App(); + + //开始启动任务 + StartStartupTasks(commandLine); + + app.Run(); + } + + private static void StartStartupTasks(CommandLine commandLine) + { + Task.Run(() => + { + // 获取应用配置文件逻辑 + var configFilePath = "App.coin"; + var repo = ConfigurationFactory.FromFile(configFilePath); + + var assemblyMetadataExporter = new AssemblyMetadataExporter(BuildStartupAssemblies()); + + var startupManager = new StartupManager(commandLine, repo, HandleShutdownError, new MainThreadDispatcher()) + .UseCriticalNodes + ( + StartupNodes.Foundation, + StartupNodes.CoreUI, + StartupNodes.UI, + StartupNodes.AppReady, + StartupNodes.StartupCompleted + ) + // 导出程序集的启动项 + .AddStartupTaskMetadataCollector(() => + assemblyMetadataExporter.ExportStartupTasks()); + startupManager.Run(); + }); + } + + private static Assembly[] BuildStartupAssemblies() + { + // 初始化预编译收集的所有模块。 + return new Assembly[] + { + // WPFDemo.App + typeof(Program).Assembly, + // WPFDemo.Lib1 + typeof(Foo2Startup).Assembly, + // WPFDemo.Api + typeof(Foo1Startup).Assembly, + }; + } + + private static Task HandleShutdownError(Exception ex) + { + // 这是启动过程的异常,需要进行退出 + +#if DEBUG + Debug.WriteLine("========== [初始化过程中出现致命错误,详情请查看异常信息] =========="); + Debug.WriteLine(ex.ToString()); + + if (Debugger.IsAttached) + { + Debugger.Break(); + } +#endif + + return Task.CompletedTask; + } + } +} diff --git a/demo/WPFDemo/WPFDemo.App/Properties/AssemblyInfo.cs b/demo/WPFDemo/WPFDemo.App/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e44dfdc --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Telescope; + +using WPFDemo.Api.StartupTaskFramework; + +[assembly: MarkExport(typeof(StartupTask), typeof(StartupTaskAttribute))] diff --git a/demo/WPFDemo/WPFDemo.App/Properties/launchSettings.json b/demo/WPFDemo/WPFDemo.App/Properties/launchSettings.json new file mode 100644 index 0000000..7ef3a56 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "WPFDemo.App": { + "commandName": "Project", + "commandLineArgs": "-Name ApplicationStartupManager" + } + } +} \ No newline at end of file diff --git a/demo/WPFDemo/WPFDemo.App/Startup/BusinessStartup.cs b/demo/WPFDemo/WPFDemo.App/Startup/BusinessStartup.cs new file mode 100644 index 0000000..fad8408 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/Startup/BusinessStartup.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using dotnetCampus.ApplicationStartupManager; +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.App.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.AppReady, AfterTasks = "MainWindowStartup", Scheduler = StartupScheduler.UIOnly)] + internal class BusinessStartup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + if (Application.Current.MainWindow.Content is Grid grid) + { + grid.Children.Add(new Button() + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Bottom, + Margin = new Thickness(10, 10, 10, 10), + Content = "Click" + }); + } + + return CompletedTask; + } + } +} diff --git a/demo/WPFDemo/WPFDemo.App/Startup/MainWindowStartup.cs b/demo/WPFDemo/WPFDemo.App/Startup/MainWindowStartup.cs new file mode 100644 index 0000000..0981928 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/Startup/MainWindowStartup.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using dotnetCampus.ApplicationStartupManager; + +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.App.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.AppReady, AfterTasks = StartupNodes.UI, Scheduler = StartupScheduler.UIOnly)] + internal class MainWindowStartup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + var mainWindow = new MainWindow(); + mainWindow.Show(); + + return CompletedTask; + } + } +} diff --git a/demo/WPFDemo/WPFDemo.App/StartupTaskFramework/MainThreadDispatcher.cs b/demo/WPFDemo/WPFDemo.App/StartupTaskFramework/MainThreadDispatcher.cs new file mode 100644 index 0000000..13e731d --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/StartupTaskFramework/MainThreadDispatcher.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using System.Windows; +using dotnetCampus.ApplicationStartupManager; + +namespace WPFDemo.App.StartupTaskFramework +{ + // 因为没有在 WPFDemo.Api 引用 WPF 程序集,因此代码写在这里 + class MainThreadDispatcher : IMainThreadDispatcher + { + public async Task InvokeAsync(Action action) + { + await Application.Current.Dispatcher.InvokeAsync(action); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.App/WPFDemo.App.csproj b/demo/WPFDemo/WPFDemo.App/WPFDemo.App.csproj new file mode 100644 index 0000000..6e0bd75 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.App/WPFDemo.App.csproj @@ -0,0 +1,22 @@ + + + + WinExe + net6.0-windows + enable + true + WPFDemo.App.Program + false + + + + + + + + + + + + + diff --git a/demo/WPFDemo/WPFDemo.Lib1/Properties/AssemblyInfo.cs b/demo/WPFDemo/WPFDemo.Lib1/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e44dfdc --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Lib1/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using dotnetCampus.ApplicationStartupManager; +using dotnetCampus.Telescope; + +using WPFDemo.Api.StartupTaskFramework; + +[assembly: MarkExport(typeof(StartupTask), typeof(StartupTaskAttribute))] diff --git a/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo2Startup.cs b/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo2Startup.cs new file mode 100644 index 0000000..e60ad59 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo2Startup.cs @@ -0,0 +1,16 @@ +using dotnetCampus.ApplicationStartupManager; + +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.Lib1.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.CoreUI, AfterTasks = StartupNodes.Foundation)] + public class Foo2Startup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + context.Logger.LogInfo("Foo2 Startup"); + return base.RunAsync(context); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo3Startup.cs b/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo3Startup.cs new file mode 100644 index 0000000..46f3822 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Lib1/Startup/Foo3Startup.cs @@ -0,0 +1,16 @@ +using dotnetCampus.ApplicationStartupManager; + +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.Lib1.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.CoreUI, AfterTaskList = new[] { nameof(WPFDemo.Lib1.Startup.Foo2Startup), "Foo1Startup" })] + public class Foo3Startup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + context.Logger.LogInfo("Foo3 Startup"); + return base.RunAsync(context); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Lib1/Startup/LibStartup.cs b/demo/WPFDemo/WPFDemo.Lib1/Startup/LibStartup.cs new file mode 100644 index 0000000..9dcf0db --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Lib1/Startup/LibStartup.cs @@ -0,0 +1,15 @@ +using dotnetCampus.ApplicationStartupManager; +using WPFDemo.Api.StartupTaskFramework; + +namespace WPFDemo.Lib1.Startup +{ + [StartupTask(BeforeTasks = StartupNodes.Foundation)] + class LibStartup : StartupTask + { + protected override Task RunAsync(StartupContext context) + { + context.Logger.LogInfo("Lib Startup"); + return base.RunAsync(context); + } + } +} diff --git a/demo/WPFDemo/WPFDemo.Lib1/WPFDemo.Lib1.csproj b/demo/WPFDemo/WPFDemo.Lib1/WPFDemo.Lib1.csproj new file mode 100644 index 0000000..10bd6d9 --- /dev/null +++ b/demo/WPFDemo/WPFDemo.Lib1/WPFDemo.Lib1.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + false + + + + + + + + + + diff --git a/dotnetCampus.ApplicationStartupManager.sln b/dotnetCampus.ApplicationStartupManager.sln index 163d9e4..7f14254 100644 --- a/dotnetCampus.ApplicationStartupManager.sln +++ b/dotnetCampus.ApplicationStartupManager.sln @@ -1,12 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31605.320 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5D196596-756D-45C2-8A05-C8E4AB8A36E6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.ApplicationStartupManager", "src\dotnetCampus.ApplicationStartupManager\dotnetCampus.ApplicationStartupManager.csproj", "{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{A02845A0-C78A-407C-ACF2-529AE6600906}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WPFDemo.App", "demo\WPFDemo\WPFDemo.App\WPFDemo.App.csproj", "{32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFDemo.Api", "demo\WPFDemo\WPFDemo.Api\WPFDemo.Api.csproj", "{68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFDemo.Lib1", "demo\WPFDemo\WPFDemo.Lib1\WPFDemo.Lib1.csproj", "{F4102A0A-E10A-462E-9AB1-0F80C83065D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,12 +25,27 @@ Global {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|Any CPU.Build.0 = Release|Any CPU + {32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF}.Release|Any CPU.Build.0 = Release|Any CPU + {68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32}.Release|Any CPU.Build.0 = Release|Any CPU + {F4102A0A-E10A-462E-9AB1-0F80C83065D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4102A0A-E10A-462E-9AB1-0F80C83065D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4102A0A-E10A-462E-9AB1-0F80C83065D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4102A0A-E10A-462E-9AB1-0F80C83065D1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F7ED61F4-920C-49EB-8DC1-74B2BE6AF272} = {5D196596-756D-45C2-8A05-C8E4AB8A36E6} + {32CDDF87-194D-4A6C-9DF5-B9D21A8F45AF} = {A02845A0-C78A-407C-ACF2-529AE6600906} + {68AD8EB7-A9A1-4EA7-A419-9BA1F74ACA32} = {A02845A0-C78A-407C-ACF2-529AE6600906} + {F4102A0A-E10A-462E-9AB1-0F80C83065D1} = {A02845A0-C78A-407C-ACF2-529AE6600906} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8AAD3A7E-EBB6-4125-9048-4CDE053D079B} diff --git a/src/dotnetCampus.ApplicationStartupManager/StartupManagerBase.cs b/src/dotnetCampus.ApplicationStartupManager/StartupManagerBase.cs index 9bf4996..82a00b2 100644 --- a/src/dotnetCampus.ApplicationStartupManager/StartupManagerBase.cs +++ b/src/dotnetCampus.ApplicationStartupManager/StartupManagerBase.cs @@ -444,9 +444,9 @@ namespace dotnetCampus.ApplicationStartupManager private static string StartupTypeToKey(Type type) => type.Name.Remove(type.Name.Length - "startup".Length); - internal virtual Task ExecuteStartupTaskAsync(StartupTaskBase startupTask, IStartupContext context, bool uiOnly) + protected internal virtual Task ExecuteStartupTaskAsync(StartupTaskBase startupTask, IStartupContext context, bool uiOnly) { return startupTask.JoinAsync(context, uiOnly); } } -} \ No newline at end of file +}