显示闪屏曹景伟出现InvalidOperationException
问题显示闪屏曹景伟出现InvalidOperationException
我有一个使用Caliburn.Micro作为MVVM框架和MEF的“依赖注入”一个MVVM应用程序(在引号,因为我知道它不是严格意义上的DI容器) 。根据MEF在应用程序启动过程中正在执行的构图数量,此大型应用程序的构图过程开始花费越来越多的时间,因此我想使用动画启动画面。
下面我将概述我当前的代码显示在一个单独的线程启动画面,并尝试启动主要应用
public class Bootstrapper : BootstrapperBase
{
private List<Assembly> priorityAssemblies;
private ISplashScreenManager splashScreenManager;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
var directoryCatalog = new DirectoryCatalog(@"./");
AssemblySource.Instance.AddRange(
directoryCatalog.Parts
.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
.Where(assembly => !AssemblySource.Instance.Contains(assembly)));
priorityAssemblies = SelectAssemblies().ToList();
var priorityCatalog = new AggregateCatalog(priorityAssemblies.Select(x => new AssemblyCatalog(x)));
var priorityProvider = new CatalogExportProvider(priorityCatalog);
var mainCatalog = new AggregateCatalog(
AssemblySource.Instance
.Where(assembly => !priorityAssemblies.Contains(assembly))
.Select(x => new AssemblyCatalog(x)));
var mainProvider = new CatalogExportProvider(mainCatalog);
Container = new CompositionContainer(priorityProvider, mainProvider);
priorityProvider.SourceProvider = Container;
mainProvider.SourceProvider = Container;
var batch = new CompositionBatch();
BindServices(batch);
batch.AddExportedValue(mainCatalog);
Container.Compose(batch);
}
protected virtual void BindServices(CompositionBatch batch)
{
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(Container);
batch.AddExportedValue(this);
}
protected override object GetInstance(Type serviceType, string key)
{
String contract = String.IsNullOrEmpty(key) ?
AttributedModelServices.GetContractName(serviceType) :
key;
var exports = Container.GetExports<object>(contract);
if (exports.Any())
return exports.First().Value;
throw new Exception(
String.Format("Could not locate any instances of contract {0}.", contract));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return Container.GetExportedValues<object>(
AttributedModelServices.GetContractName(serviceType));
}
protected override void BuildUp(object instance)
{
Container.SatisfyImportsOnce(instance);
}
protected override void OnStartup(object sender, StartupEventArgs suea)
{
splashScreenManager = Container.GetExportedValue<ISplashScreenManager>();
splashScreenManager.ShowSplashScreen();
base.OnStartup(sender, suea);
DisplayRootViewFor<IMainWindow>(); // HERE is the Problem line.
splashScreenManager.CloseSplashScreen();
}
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[] { Assembly.GetEntryAssembly() };
}
protected CompositionContainer Container { get; set; }
internal IList<Assembly> PriorityAssemblies
{
get { return priorityAssemblies; }
}
}
我ISplashScreenManager
实现
[Export(typeof(ISplashScreenManager))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class SplashScreenManager : ISplashScreenManager
{
private ISplashScreenViewModel splashScreen;
private Thread splashThread;
private Dispatcher splashDispacher;
public void ShowSplashScreen()
{
splashDispacher = null;
if (splashThread == null)
{
splashThread = new Thread(new ThreadStart(DoShowSplashScreen));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.IsBackground = true;
splashThread.Name = "SplashThread";
splashThread.Start();
Log.Trace("Splash screen thread started");
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Application.Current.MainWindow = null;
}
}
private void DoShowSplashScreen()
{
splashScreen = IoC.Get<ISplashScreenViewModel>();
splashDispacher = Dispatcher.CurrentDispatcher;
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(splashDispacher));
splashScreen.Closed += (s, e) =>
splashDispacher.BeginInvokeShutdown(DispatcherPriority.Background);
splashScreen.Show();
Dispatcher.Run();
Log.Trace("Splash screen shown and dispatcher started");
}
public void CloseSplashScreen()
{
if (splashDispacher != null)
{
splashDispacher.BeginInvokeShutdown(DispatcherPriority.Send);
splashScreen.Close();
Log.Trace("Splash screen close requested");
}
}
public ISplashScreenViewModel SplashScreen
{
get { return splashScreen; }
}
}
其中ISplashScreenViewModel.Show()
和ISplashScreenViewModel.Close()
方法分别显示和关闭相应的视图。
错误
此代码似乎只要它启动在后台线程启动画面和飞溅的动漫作品等。然而,当代码返回引导程序行以及工作
DisplayRootViewFor<IMainWindow>();
抛出InvalidOperationException
以下消息
调用线程不能访问此OBJ因为不同的线程拥有它。
堆栈跟踪是
在System.Windows.Threading.Dispatcher.VerifyAccess()在System.Windows.DependencyObject.GetValue(的DependencyProperty DP)在MahApps.Metro.Controls.MetroWindow.get_Flyouts ()在d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ Controls \ MetroWindow.cs中:第269行MahApps.Metro.Controls.MetroWindow.ThemeManagerOnIsThemeChanged(Object sender,OnThemeChangedEventArgs e )在d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ Controls \ MetroWindow.cs中:在System.EventHandler1.Invoke中的第962行(对象发件人,TEventArgs e) 位于MahApps。 Metro.Controls.SafeRaise在MahApps中的第26行:.Raise [T](EventHandler1 eventToRaise,Object sender,T args)在d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ Controls \ SafeRaise.cs中。 Metro.ThemeManager.OnThemeChanged(Accent newAccent,AppTheme newTheme)在d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ ThemeManager \ ThemeManager.cs中:第591行,位于MahApps.Metro.ThemeManager .ChangeAppStyle(ResourceDictionary资源,Tuple`2 oldThemeInfo,Accent newAccent,AppTheme newTheme)在d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ ThemeManager \ ThemeManager.cs中:第407行MahApps.Metro.ThemeManager.ChangeAppStyle(Application application,Accent newAccent,AppTheme newTheme)in d:\ projects \ git \ MahApps.Metro \ src \ MahApps.Metro \ MahApps.Metro.Shared \ ThemeManager \ ThemeManager.cs:line 345 at在Augur.Modules.Shell.ViewModels.ShellViewModel.OnViewLoaded(Obj)中的第46行, F:\ Camus \ Augur \ Src \ Augur \ Modules \ Shell \ ViewModels \ ShellViewModel.cs:Caliburn.Micro.XamlPlatformProvider的第73行。在Caliburn.Micro.View上的c__DisplayClass11_0.b__0(Object s,RoutedEventArgs e)。 <> c_DisplayClass8_0.b__0(Object s,RoutedEventArgs e)在System.Windows.EventRoute。在System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root,RoutedEvent routedEvent)System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(System.Windows.UIElement.RaiseEventImpl(DependencyObject sender,RoutedEventArgs args))上的InvokeHandlersImpl(Object source,RoutedEventArgs args,Boolean reRaised)对象根)在System.Windows.Media.MediaContext.FireLoadedPendingCallbacks()在System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()在System.Windows.Media.MediaContext.RenderMessageHandlerCore(对象resizedCompositionTarget)在MS.Internal.LoadedOrUnloadedOperation.DoWork() )System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)System.Windows.Interop.HwndTarget.OnResize()System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg,IntPtr wparam,IntPtr lparam)在系统。 Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,布尔型(System.Windows.Threading.ExceptionWrapper.InternalRealCall)上的MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)处的MS.Win32.HwndWrapper.WndProc(IntPtr hwnd,Int32 msg,IntPtr wParam,IntPtr lParam,布尔&已处理) (System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority,System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source,Delegate callback,Object args,Int32 numArgs,Delegate catchHandler)时间跨度超时,代表方法中,在MS.Win32.HwndSubclass.SubclassWndProc(IntPtr的HWND,MSG的Int32,IntPtr的wParam中,IntPtr的LPARAM)对象指定参数时,的Int32 numArgs)
尝试的解决方案
我试图改变执行DisplayRootViewFor<IMainWindow>();
如下
base.OnStartup(sender, suea);
Application.Current.Dispatcher.Invoke(() => DisplayRootViewFor<IMainWindow>()); // Still throws the same exception.
和
base.OnStartup(sender, suea);
Application.Current.Dispatcher.BeginInvoke(
new System.Action(delegate { DisplayRootViewFor<IMainWindow>(); })); // Still throws the same exception.
甚至
TaskScheduler guiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
splashScreenManager = Container.GetExportedValue<ISplashScreenManager>();
splashScreenManager.ShowSplashScreen();
Task.Factory.StartNew(() =>
{
base.OnStartup(sender, suea);
DisplayRootViewFor<IMainWindow>();
}, CancellationToken.None,
TaskCreationOptions.None,
guiScheduler);
试图强制使用的使用调度代码Gui MainThread。以上所有都会抛出同样的异常。
问题
我如何可以调用
DisplayRootViewFor<IMainWindow>()
方法,避免此异常?这种显示动画效果的方法是合法吗?
谢谢你的时间。
编辑。我已经从令人敬畏的Hans Passant https://*.com/a/4078528/626442发现了这个答案。鉴于此,我试图添加应用程序static App() { }
ctor。
static App()
{
// Other stuff.
Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };
}
但(可能是意料之中),这并没有帮助我。在同一地点也有同样的例外...
您正在新后台线程上创建SplashScreenView
的实例,但在主UI线程中调用MetroThemeManager.ChangeAppStyle
类ThemeManager
类。
因为MetroWindow
类是你SplashScreenView
你不能阻止它的内部预订至ThemeManager.IsThemeChanged
事件,您可以通过调用MetroThemeManager.ChangeAppStyle
内ThemeManager
类触发父。
由于内部MetroWindow
的ThemeManager.IsThemeChanged
事件处理程序的代码不能在主UI线程是从线程闪屏不同的是创造了你应该为[1]派生SplashScreenView
从标准Window
类,以避免依赖执行MetroWindow
或[2]避免拨打MetroThemeManager.ChangeAppStyle
而您的SplashScreenView
仍然存在。
注释掉两行代码是删除违规行为,请参阅下面的行。但显然,实际的解决方案需要在另一个层面上完成,见上文。
// this line is causing violation due to hidden dependency
MetroThemeManager.ChangeAppStyle(Application.Current, metroAccent, metroTheme);
// this line is causing an error when closing the splash screen
splashDispacher.BeginInvokeShutdown(DispatcherPriority.Send);
伟大的工作。发现。应该看到一个公平的......这是最近才添加的代码。 – MoonKnight
我很高兴它有帮助,但间接依赖关系很难发现,但imho内部处理Loaded事件并不是Metro方面的最佳设计决定,如果需要很难覆盖。 –
看起来像Splash show正在访问UI线程。我们可以尝试Dispatcher.Invoke(()=> {splashScreen.Show();});在DoShowSplashScreen()方法中 – Simsons
你的启动画面代码看起来很奇怪。它使用Application.Current,它设置SynchronizationContext一次永远不会改回它?否则,你有一个小的复制项目?你为什么不使用标准的WPF的SplashScreen?源甚至可以得到灵感:https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/SplashScreen.cs,9c43c23f03d92271 –
嗨@Simsons我试过这个。这没有帮助,我得到同样的例外。 – MoonKnight