未显示单例 ViewModel 的视图
View is not shown for singleton ViewModel
我有一个使用 Caliburn.Micro 2.0.1 的 wpf 应用程序,我发现 Caliburn.Micro 有奇怪的行为。最新的3.0.3也有。
重现错误的超级简单项目:https://github.com/ihtfw/CaliburnMicroBug
MainWindowViewModel 是屏幕的导体。
有两个 ScreenViewModel 作为 MainWindowViewModel 的 Items 和 属性 SingletonViewModel。
在 MainViewModel 中,我可以切换到第一个 ScreenViewModel 或第二个。
除了 SingletonViewModel 的视图在切换到第二个屏幕后仅在第二个 ScreenViewModel 上显示外,一切正常。
重现:
- 启动应用程序
- 切换到第二屏幕
- 切换回第一屏
- 未显示 SingletonViewModel 的视图 =(
如何解决这个问题?
更新
无法使用 ContentControl 制作这些。应该使用显式视图。在这种情况下,SingletonView 并设置它的 DataContext。要连接 Caliburn.Micro 动作,请在没有上下文的情况下绑定它们。所以结果将是:
<local:SingletonView DataContext={Binding Path=SingletonViewModel} cal:Action.TargetWithoutContext="{Binding}" />
我处理过你的项目,发现你的问题出在 MainWindows 构造函数中。像下面这样更改 MainWindow 的构造函数:
public MainWindowViewModel()
{
Items.Add(new ScreenViewModel
{
DisplayName = "Screen 1",
SingletonViewModel = new SingletonViewModel()
});
Items.Add(new ScreenViewModel
{
DisplayName = "Screen 2",
SingletonViewModel = new SingletonViewModel()
});
ActiveItem = Items.First();
}
这不是 Bug。
一个UIElement
如UserControl
只能有一个逻辑父。
您正在创建两个 ScreenViews
,并且您正试图向它们添加相同的 SingletonView
,这是不可能的,因为 SingletonView
不能有两个父项。
如其他答案所述,您不能有 "singleton view",xaml UI 模型将不允许相同的 UI 元素出现在可视化树中两次。
但是,您可以将同一视图的两个实例绑定到视图模型。
为此,您需要避开框架的一项功能。
在内部,任何实现 IViewAware
的视图模型都持有对视图的弱引用,每当框架尝试为视图模型定位视图时,它首先查询视图模型以获取该引用。
如果返回引用,则视图将从其现有位置删除并插入到新位置。
为了阻止这种情况,您可以做以下三件事之一:
- 不要让您的
SingletonViewModel
继承自实现 IViewAware
的东西,例如 PropertyChangedBase
.
- 做一些非常 hacky 的事情,并使用下面的第一个代码示例取消附加视图。
- 更改
ViewLocator.LocateForModel
以使用下面的第二个代码示例删除此行为。
Hacky 无效视图
protected override void OnViewAttached(object view, object context)
{
Views[context ?? ViewAware.DefaultContext] = null;
}
删除 IViewAware
行为
ViewLocator.LocatorForModel = (model, location, context) => ViewLocator.LocateForModelType(model.GetType(), location, context);
我有一个使用 Caliburn.Micro 2.0.1 的 wpf 应用程序,我发现 Caliburn.Micro 有奇怪的行为。最新的3.0.3也有。
重现错误的超级简单项目:https://github.com/ihtfw/CaliburnMicroBug
MainWindowViewModel 是屏幕的导体。 有两个 ScreenViewModel 作为 MainWindowViewModel 的 Items 和 属性 SingletonViewModel。 在 MainViewModel 中,我可以切换到第一个 ScreenViewModel 或第二个。
除了 SingletonViewModel 的视图在切换到第二个屏幕后仅在第二个 ScreenViewModel 上显示外,一切正常。
重现:
- 启动应用程序
- 切换到第二屏幕
- 切换回第一屏
- 未显示 SingletonViewModel 的视图 =(
如何解决这个问题?
更新
无法使用 ContentControl 制作这些。应该使用显式视图。在这种情况下,SingletonView 并设置它的 DataContext。要连接 Caliburn.Micro 动作,请在没有上下文的情况下绑定它们。所以结果将是:
<local:SingletonView DataContext={Binding Path=SingletonViewModel} cal:Action.TargetWithoutContext="{Binding}" />
我处理过你的项目,发现你的问题出在 MainWindows 构造函数中。像下面这样更改 MainWindow 的构造函数:
public MainWindowViewModel()
{
Items.Add(new ScreenViewModel
{
DisplayName = "Screen 1",
SingletonViewModel = new SingletonViewModel()
});
Items.Add(new ScreenViewModel
{
DisplayName = "Screen 2",
SingletonViewModel = new SingletonViewModel()
});
ActiveItem = Items.First();
}
这不是 Bug。
一个UIElement
如UserControl
只能有一个逻辑父。
您正在创建两个 ScreenViews
,并且您正试图向它们添加相同的 SingletonView
,这是不可能的,因为 SingletonView
不能有两个父项。
如其他答案所述,您不能有 "singleton view",xaml UI 模型将不允许相同的 UI 元素出现在可视化树中两次。
但是,您可以将同一视图的两个实例绑定到视图模型。
为此,您需要避开框架的一项功能。
在内部,任何实现 IViewAware
的视图模型都持有对视图的弱引用,每当框架尝试为视图模型定位视图时,它首先查询视图模型以获取该引用。
如果返回引用,则视图将从其现有位置删除并插入到新位置。
为了阻止这种情况,您可以做以下三件事之一:
- 不要让您的
SingletonViewModel
继承自实现IViewAware
的东西,例如PropertyChangedBase
. - 做一些非常 hacky 的事情,并使用下面的第一个代码示例取消附加视图。
- 更改
ViewLocator.LocateForModel
以使用下面的第二个代码示例删除此行为。
Hacky 无效视图
protected override void OnViewAttached(object view, object context)
{
Views[context ?? ViewAware.DefaultContext] = null;
}
删除 IViewAware
行为
ViewLocator.LocatorForModel = (model, location, context) => ViewLocator.LocateForModelType(model.GetType(), location, context);