在运行时动态创建(封装)子级的WPF元素
我想创建一个WPF元素,该元素在运行时完全控制其子元素——在其属性更改时添加和删除子UI。有点像ItemsControl在修改其ItemsSource属性时所做的操作,不过在我的例子中只有一个子项 这将是一个MVVM的视图容器——当您给它一个模型或ViewModel时,它将神奇地创建正确的视图并连接所有内容。我的视图容器不需要是可模板化的(因为它创建了用户定义的视图,这些视图是用户控件,并且有自己的模板),我更希望它尽可能地进行封装。我可以很容易地做到这一点,当我自己的属性发生变化时,我可以从类似网格的地方降下来,并添加子控件;但网格公开了它的子元素集合,并允许任何人添加和删除内容 为了最大限度地封装,我应该从哪个WPF类开始,以及如何在运行时向它添加子元素 基于我对文档的理解,我尝试使用FrameworkElement和AddVisualChild,只是想看看是否可以在运行时创建子控件。我不清楚AddLogicalChild是否必要,但我把它放在以防万一:在运行时动态创建(封装)子级的WPF元素,wpf,encapsulation,frameworkelement,dynamic-ui,Wpf,Encapsulation,Frameworkelement,Dynamic Ui,我想创建一个WPF元素,该元素在运行时完全控制其子元素——在其属性更改时添加和删除子UI。有点像ItemsControl在修改其ItemsSource属性时所做的操作,不过在我的例子中只有一个子项 这将是一个MVVM的视图容器——当您给它一个模型或ViewModel时,它将神奇地创建正确的视图并连接所有内容。我的视图容器不需要是可模板化的(因为它创建了用户定义的视图,这些视图是用户控件,并且有自己的模板),我更希望它尽可能地进行封装。我可以很容易地做到这一点,当我自己的属性发生变化时,我可以从类
public class ViewContainer : FrameworkElement {
private TextBlock _child;
public ViewContainer() {
_child = new TextBlock { Text = "ViewContainer" };
AddLogicalChild(_child);
AddVisualChild(_child);
InvalidateMeasure();
}
public object Content { get; set; }
protected override Size ArrangeOverride(Size finalSize) {
_child.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Size MeasureOverride(Size availableSize) {
_child.Measure(availableSize);
return _child.DesiredSize;
}
}
当我将ViewContainer放入窗口并运行它时,我希望看到一个文本块显示“ViewContainer”。但是相反,我只看到一个空白的窗口。所以很明显我错过了什么
我如何修复上面的代码,使“child”控件在运行时出现,但不会被其他人弄乱(这是可以避免的)您是否考虑过利用WPF对隐式数据模板的支持 我处理类似于您的需求的方法是使用
ContentControl
。我将内容
属性绑定到我的ViewModel。然后,我确保在ContentControl上方的树中引用的资源字典中,为可能分配给Content属性的所有类型的ViewModels定义了DataTemplates
通过这种方式,WPF负责为我的ViewModel连接正确的视图。要回答您的特定问题,您还需要重写和属性以使子元素能够显示。是的,我以前已经这样做过,但这次我想更进一步——我希望我的ViewContainer能够获取一个模型,并自动发现并为其创建正确的ViewModel,然后从那里转到视图。我不认为DataTemplate.DataType会在没有帮助的情况下把我带到那里。当然,DataTemplate.DataType不适合,但是DataTemplateSelector可能会。很酷——就是这样。虽然我有点困惑,为什么会有一个AddVisualChild,如果它实际上没有添加到任何东西中。啊,我明白了。它使孩子了解自己的父母;但许多家长不需要孩子的列表(没有孩子或只有一个孩子),因此存储的选择和责任由家长决定。积极的关注点分离:我非常喜欢WPF的一个方面。但这意味着“AddVisualChild”这个名字有误导性,因为它表明它正在将它添加到某种列表中,而事实并非如此。