問題顯示閃屏曹景偉出現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://stackoverflow.com/a/4078528/626442發現了這個答案。鑑於此,我試圖添加應用程序static App() { }
ctor。
static App()
{
// Other stuff.
Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };
}
但(可能是意料之中),這並沒有幫助我。在同一地點也有同樣的例外...
看起來像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