在运行时动态创建(封装)子元素的WPF元素

4
我想创建一个WPF元素,在运行时完全控制其子元素——当其属性更改时添加和删除子UI。这有点像修改ItemsControl的ItemsSource属性时所做的操作,不过在我的情况下只会有一个子元素。
这将是MVVM的视图容器——当您提供一个模型或视图模型时,它将自动创建正确的视图并连接一切。我的视图容器不需要可模板化(因为它创建用户定义的视图,这些视图是UserControls并具有自己的模板),我希望尽可能地封装它。我可能可以通过从类似Grid的东西中下降,并在自己的属性更改时添加子控件来轻松完成此操作;但是Grid公开了其子元素的集合,并允许任何人添加和删除内容。
哪个WPF类应该从最大的封装下降,并如何在运行时向其中添加子元素?
基于我对文档的理解,我尝试使用FrameworkElement和AddVisualChild,只是为了看看是否能够在运行时创建子控件。我不确定是否需要AddLogicalChild,但我还是加上了:
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放入一个Window中,运行程序时,我期望看到一个TextBlock显示"ViewContainer"。但是实际上,我只看到了一个空白窗口。显然,我遗漏了一些东西。
如何修复上述代码,让"child"控件在运行时出现,但其他人无法乱搞它(除非完全避免不了)?
2个回答

6

很酷 - 它做到了。虽然我有点困惑为什么会有AddVisualChild,如果它实际上并没有将其添加到任何地方。 - Joe White
1
啊,我明白了。它设置子级以了解其父级;但许多父级不需要子级列表(没有或只有一个),因此它留下了存储的选择和责任给父级。关注点的积极分离:这是我非常喜欢WPF的事情之一。但这意味着“AddVisualChild”名称是误导性的,因为它表明它正在将其添加到某种列表中,但实际上并不是这样。 - Joe White

0
你有没有考虑利用WPF对隐式DataTemplates的支持?
我处理类似需求的方式是使用ContentControl。我将Content属性绑定到我的ViewModel。然后,我确保在树中引用的Resource Dictionaries中为可能分配给Content属性的所有ViewModel类型定义了DataTemplates。
这样,WPF会负责连接正确的视图和我的ViewModel。

是的,我以前做过这个,但这一次我想更进一步——我希望我的ViewContainer能够接受一个Model,并自动发现和创建适合它的正确ViewModel,然后从那里到达View。我认为如果没有帮助,DataTemplate.DataType无法帮我实现这一点。 - Joe White
当然,DataTemplate.DataType 不适合,但是有 DataTemplateSelector 可能会适合。 - Romain Hautefeuille

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接