WPF ReactiveUI中主窗口显示ContainerView时ChildView不显示的原因?
问题
我正在学习WPF和ReactiveUI,现通过简化示例说明问题:我创建了绑定到ContainerViewModel的ContainerView,该ViewModel包含Child属性,与ChildView控件的ViewModel绑定;ContainerView的样式中设置了包含名为“Child”的ViewModelViewHost控件的ControlTemplate。但在主窗口中显示ContainerView时,ChildView并未显示。
ContainerView/ViewModel 代码
C# 代码
public class ContainerView : Control, IViewFor<ContainerViewModel> { public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register( nameof(ViewModel), typeof(ContainerViewModel), typeof(ContainerView), new PropertyMetadata(default(ContainerViewModel))); public ContainerViewModel ViewModel { get { return (ContainerViewModel)GetValue(ViewModelProperty); } set { SetValue(ViewModelProperty, value); } } object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (ContainerViewModel)value; } public ContainerView() { this.WhenActivated(d => { this.OneWayBind(ViewModel, vm => vm.Child, view => view.Child.ViewModel); }); } private ViewModelViewHost Child { get; set; } public override void OnApplyTemplate() { Child = GetTemplateChild(nameof(Child)) as ViewModelViewHost; } } public class ContainerViewModel : ReactiveObject { private ChildViewModel _child; public ChildViewModel Child { get => _child; set => this.RaiseAndSetIfChanged(ref _child, value); } public ContainerViewModel() { Child = new ChildViewModel(); } }
XAML 样式
<Style TargetType="{x:Type views:ContainerView}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type views:ContainerView}"> <Grid> <reactiveUi:ViewModelViewHost x:Name="Child"></reactiveUi:ViewModelViewHost> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
ChildView/ViewModel 代码
C# 代码
public class ChildView : Control, IViewFor<ChildViewModel> { public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register( nameof(ViewModel), typeof(ChildViewModel), typeof(ChildView), new PropertyMetadata(default(ChildViewModel))); public ChildViewModel ViewModel { get { return (ChildViewModel)GetValue(ViewModelProperty); } set { SetValue(ViewModelProperty, value); } } object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (ChildViewModel)value; } public ChildView() { DefaultStyleKey = nameof(ChildView); } }
XAML 样式
<Style TargetType="{x:Type views:ChildView}"> <Setter Property="Background" Value="Brown"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type views:ChildView}"> <Ellipse Width="200" Height="200" Fill="Yellow"></Ellipse> </ControlTemplate> </Setter.Value> </Setter> </Style>
原因分析与解决方法
核心原因
- 绑定时机错误:
WhenActivated在控件构造阶段执行,此时OnApplyTemplate还没调用,Child字段是null,绑定无法正确关联到ViewModelViewHost控件。 - 缺少视图映射注册:ReactiveUI不知道
ChildViewModel对应的视图是ChildView,如果没有自动匹配的命名约定,需要手动注册映射关系。 - 遗漏基类方法调用:
OnApplyTemplate中没调用base.OnApplyTemplate(),会干扰WPF的模板加载流程。
修复步骤
1. 调整绑定时机,确保控件加载完成后再绑定
修改ContainerView的代码,监听Child控件的加载状态,完成后再建立绑定:
public class ContainerView : Control, IViewFor<ContainerViewModel> { // ... 保留原有DependencyProperty和ViewModel属性 ... private ViewModelViewHost? Child { get; set; } public ContainerView() { this.WhenActivated(disposables => { // 等待Child控件加载完成后再创建绑定 this.WhenAnyValue(v => v.Child) .Where(child => child != null) .Subscribe(child => { this.OneWayBind(ViewModel, vm => vm.Child, view => view.Child!.ViewModel) .DisposeWith(disposables); }).DisposeWith(disposables); }); } public override void OnApplyTemplate() { base.OnApplyTemplate(); // 必须调用基类方法 Child = GetTemplateChild(nameof(Child)) as ViewModelViewHost; } }
2. 注册视图与ViewModel的映射
在应用启动时(比如App.xaml.cs的OnStartup方法)添加注册代码:
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // 方式一:手动注册单个映射 Locator.CurrentMutable.Register(() => new ChildView(), typeof(IViewFor<ChildViewModel>)); // 方式二:自动注册当前程序集中所有符合命名约定的视图(ViewModel对应[ViewModel名]View) // Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetExecutingAssembly()); }
完成以上修改后,ChildView就能正常显示了。
内容的提问来源于stack exchange,提问作者dba12
相关产品推荐
相关产品推荐

