使用 Caliburn.Micro 的动画启动画面
Animated Splashscreen using Caliburn.Micro
我正在使用 Caliburn.Micro 创建一个应用程序。该应用程序在启动期间与 api 通信,这就是我需要向用户显示启动画面的原因。我创建了自己的动画启动画面作为 Window,它在 OnStartup 方法中从引导程序激活。
启动过程由启动画面视图模型管理。
当所有与启动相关的过程都完成后,我如何告诉引导程序关闭闪屏并激活另一个 window?
我考虑过发起一个事件,但我无法将引导程序订阅到 IEventaggregator。
我尝试在 ShellView 的 contentcontrol 中显示 Splashscreen,并在加载完成后切换到不同的虚拟机。这里的问题是启动画面应该显示在透明、无边框的 window 上,在创建 window 后无法更改。
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IEventAggregator, EventAggregator>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag
)
)
);
DisplayRootViewFor<AnimatedSplashViewModel>();
//DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
public class AnimatedSplashViewModel : Screen
{
private IEventAggregator _events;
private string _splashMessage;
public string SplashMessage
{
get { return _splashMessage; }
set
{
_splashMessage = value;
NotifyOfPropertyChange(() => SplashMessage);
}
}
public AnimatedSplashViewModel(IEventAggregator events)
{
_events = events;
SplashMessage = "Please wait";
// Simulation of long tasks
var worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashFinishedEvent());
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(10000);
}
}
您应该为根视图使用 ShellViewModel
并用 "main" 视图替换初始屏幕视图,或者您可以等到初始屏幕显示后再显示根视图关闭:
protected override void OnStartup(object sender, StartupEventArgs e)
{
Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var windowManager = IoC.Get<IWindowManager>();
var eventAggregator = IoC.Get<IEventAggregator>();
windowManager.ShowDialog(new AnimatedSplashViewModel(eventAggregator));
DisplayRootViewFor(typeof(ShellViewModel));
}
...
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TryClose();
}
我假设有人想知道最终的解决方案:
首先,我使用 WindowManager 创建了一个 Splashscreen 的对话框,并让 SplashscreenViewModel 完成所有工作。
原来这种方法需要很长时间才能加载。因此,当我尝试执行时,Dailog 需要大约 8 秒的时间才能显示出来。这对我不耐烦的用户来说太长了。
我认为这是因为我使用 IoC 将大量依赖项注入 SplashscreenViewModel。
windowManager.ShowDialog(new AnimatedSplashViewModel(locationEndpoint, userEndpoint, applicationEndpoint, adUser, clientInfo, locationInfo, loggedInUser));
第二种方法是将 Splashscreen 创建为一个对话框,并使用 BackgroundWorker 来处理 Bootstrapper 中的所有计算和 api 内容。
虽然这工作得非常快,但我觉得必须有更好的方法。
第三个也是最后一个解决方案:
Bootstrapper 调用 ShellViewModel。
public Bootstrapper()
{
Initialize();
DisplayRootViewFor<ShellViewModel>();
}
在 OnInitialize 方法中,我创建了一个 BackgroundWorker,它执行所有长 运行 任务,同时使用 WindowManager 将 SplashScreen 显示为对话框。
protected override void OnInitialize()
{
var windowManager = new WindowManager();
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.DoWork += InitializeApplication;
bw.RunWorkerCompleted += InitializationCompleted;
bw.RunWorkerAsync();
windowManager.ShowDialog(new AnimatedSplashViewModel(_events));
}
}
AnimatedSplashscreenViewModel 现在只需要一个依赖项,即 EventAggregator。我让它处理一个名为 SplashMessageChangedEvent 的自定义事件。
public class SplashMessageChangedEvent
{
public string Content { get; set; }
public bool CloseDialog { get; set; } = false;
public SplashMessageChangedEvent(string content)
{
Content = content;
}
public SplashMessageChangedEvent(bool closeDialog)
{
CloseDialog = closeDialog;
}
}
在 ShellViewModel 的 InitializationCompleted 事件中,我发布了以下事件以关闭对话框:
private void InitializationCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashMessageChangedEvent(true));
}
现在这个最终方法比其他两个方法快得多。
启动可执行文件后立即显示启动画面。
我正在使用 Caliburn.Micro 创建一个应用程序。该应用程序在启动期间与 api 通信,这就是我需要向用户显示启动画面的原因。我创建了自己的动画启动画面作为 Window,它在 OnStartup 方法中从引导程序激活。 启动过程由启动画面视图模型管理。
当所有与启动相关的过程都完成后,我如何告诉引导程序关闭闪屏并激活另一个 window?
我考虑过发起一个事件,但我无法将引导程序订阅到 IEventaggregator。
我尝试在 ShellView 的 contentcontrol 中显示 Splashscreen,并在加载完成后切换到不同的虚拟机。这里的问题是启动画面应该显示在透明、无边框的 window 上,在创建 window 后无法更改。
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IEventAggregator, EventAggregator>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag
)
)
);
DisplayRootViewFor<AnimatedSplashViewModel>();
//DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
public class AnimatedSplashViewModel : Screen
{
private IEventAggregator _events;
private string _splashMessage;
public string SplashMessage
{
get { return _splashMessage; }
set
{
_splashMessage = value;
NotifyOfPropertyChange(() => SplashMessage);
}
}
public AnimatedSplashViewModel(IEventAggregator events)
{
_events = events;
SplashMessage = "Please wait";
// Simulation of long tasks
var worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashFinishedEvent());
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(10000);
}
}
您应该为根视图使用 ShellViewModel
并用 "main" 视图替换初始屏幕视图,或者您可以等到初始屏幕显示后再显示根视图关闭:
protected override void OnStartup(object sender, StartupEventArgs e)
{
Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var windowManager = IoC.Get<IWindowManager>();
var eventAggregator = IoC.Get<IEventAggregator>();
windowManager.ShowDialog(new AnimatedSplashViewModel(eventAggregator));
DisplayRootViewFor(typeof(ShellViewModel));
}
...
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TryClose();
}
我假设有人想知道最终的解决方案:
首先,我使用 WindowManager 创建了一个 Splashscreen 的对话框,并让 SplashscreenViewModel 完成所有工作。
原来这种方法需要很长时间才能加载。因此,当我尝试执行时,Dailog 需要大约 8 秒的时间才能显示出来。这对我不耐烦的用户来说太长了。 我认为这是因为我使用 IoC 将大量依赖项注入 SplashscreenViewModel。
windowManager.ShowDialog(new AnimatedSplashViewModel(locationEndpoint, userEndpoint, applicationEndpoint, adUser, clientInfo, locationInfo, loggedInUser));
第二种方法是将 Splashscreen 创建为一个对话框,并使用 BackgroundWorker 来处理 Bootstrapper 中的所有计算和 api 内容。 虽然这工作得非常快,但我觉得必须有更好的方法。
第三个也是最后一个解决方案: Bootstrapper 调用 ShellViewModel。
public Bootstrapper()
{
Initialize();
DisplayRootViewFor<ShellViewModel>();
}
在 OnInitialize 方法中,我创建了一个 BackgroundWorker,它执行所有长 运行 任务,同时使用 WindowManager 将 SplashScreen 显示为对话框。
protected override void OnInitialize()
{
var windowManager = new WindowManager();
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.DoWork += InitializeApplication;
bw.RunWorkerCompleted += InitializationCompleted;
bw.RunWorkerAsync();
windowManager.ShowDialog(new AnimatedSplashViewModel(_events));
}
}
AnimatedSplashscreenViewModel 现在只需要一个依赖项,即 EventAggregator。我让它处理一个名为 SplashMessageChangedEvent 的自定义事件。
public class SplashMessageChangedEvent
{
public string Content { get; set; }
public bool CloseDialog { get; set; } = false;
public SplashMessageChangedEvent(string content)
{
Content = content;
}
public SplashMessageChangedEvent(bool closeDialog)
{
CloseDialog = closeDialog;
}
}
在 ShellViewModel 的 InitializationCompleted 事件中,我发布了以下事件以关闭对话框:
private void InitializationCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashMessageChangedEvent(true));
}
现在这个最终方法比其他两个方法快得多。 启动可执行文件后立即显示启动画面。