WPF Window.Show 引发 InvalidOperationException:窗口必须是树的根.无法将窗口添
本文关键字:窗口 Show Window 引发 InvalidOperationException WPF | 更新日期: 2023-09-27 17:56:13
如果这是相关的;我在我的WPF应用程序中使用Caliburn.Micro和Castle.Windsor。
我有一个带有LoginViewModel的LoginView,它是在应用程序启动时启动的,没有戏剧性。然后,一旦用户登录,从 LoginViewModel 中,我启动一个初始屏幕,主应用程序视图如下所示;
IoC.Get<IWindowManager>().ShowWindow(IoC.Get<SplashScreenViewModel>());
IoC.Get<IWindowManager>().ShowWindow(IoC.Get<MainViewModel>());
第一个调用有效,第二个调用在 Caliburn.Micro 中抛出异常;
Window.Show();
InvalidOperationException
消息是:"窗口必须是树的根。无法将窗口添加为视觉对象的子项。
我的理解是 Window.Show() 将窗口放在可视化树的根目录下......此外,或多或少相同的代码在此代码库的先前版本中运行良好,但我在其他领域进行了一些重大重构,现在出现了这种情况。
我猜在我使用 Castle.Windsor Caliburn.Micro 的相关重构中有一些失败,但此错误消息并不能帮助我弄清楚这可能是什么......
有什么建议吗?
我仍然无法确切确定导致此异常的原因,但是我已经通过修改我的(非常广泛的)ViewModel组件注册来解决此问题;
Register(Classes.FromAssembly(GetType().Assembly)
.Where(x => x.Name.EndsWith("ViewModel"))
.WithService.Self()
.WithService.AllInterfaces()
.Configure(x => x.LifeStyle.Is(LifestyleType.Transient)));
自;
Register(Classes.FromAssembly(GetType().Assembly)
.Where(x => x.Name.EndsWith("ViewModel"))
.WithService.Self()
.WithService.DefaultInterfaces()
.Configure(x => x.LifeStyle.Is(LifestyleType.Transient)));
似乎使用 AllInterfaces
会产生一个复杂且不必要的依赖树。在这种情况下,通过使用DefaultInterfaces
来减少依赖项并没有害处,这已经解决了这个异常,我推测这与某些 View 实例之间的不良循环关系有关。
在我的情况下,"窗口必须是树的根"错误是由于尝试将ListBox
的ItemsSource
绑定到DependencyObjects集合引起的,该集合恰好在索引零处有一个Window
实例。
建立集合绑定时,WPF 会检查绑定集合的内容,以确定它是否需要为每个项使用"项容器",以及是否希望集合的内容显示为本机UIElement
(没有容器)。我相信这涉及以各种方式测试第一个元素。
这是一个很好的优化WPF
:如果数据可以直接显示,则不要生成项目容器。但是,如果你确实打算将集合中的项视为数据(即,你的应用是调试辅助工具或反射工具),并且如果数据可以包含Window,
则 WPF 的无提示决策会出错,因为如果没有项容器,则无法将Window
放在树中。
不仅仅是Window
实例在这里可能成为问题。也许更严重的是,如果您计划稍后将集合中的这些UIElement
插入到另一个可视化树中,这也会失败,因为 UIElements 不能位于多个可视化树中。
解决方案很简单:只需指定一个ItemTemplate
,这将强制 WPF 使用项容器。
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate DataType="DependencyObject">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
[编辑:] 上面的代码可能不足以解决问题。请参阅 https://stackoverflow.com/a/3844808/147511