我应该如何在WPF窗口上动态创建UserControl?

时间:2011-05-10 15:47:43

标签: c# wpf xaml mvvm user-controls

我正在使用一些WPF的东西。在将三种非常相似的形式合并为一种形式的过程中,我提出了一个问题。我编写的代码有效,但它似乎远非最佳解决方案,所以我认为我会向更广泛的受众询问反馈和/或更好的解决方案。

一些背景
这三种形式共享很多XAML和代码隐藏,但每种形式中都有一个不同的部分。我在一个表单中保留了所有共享代码,并将不同的部分拆分为UserControls,目的是在运行时将其中一个动态加载到容器中。我们正在使用MVVM,因此表单具有ViewModel,新的UserControls也是如此。当然,表单的ViewModel充当父级,并包含新UserControls的ViewModel作为子级。

问题本身
最初,我使用带有DataTemplateSelector的ContentControl从多个DataTemplate中进行选择,每个DataTemplate包含一个UserControl。绑定到ContentControl的Content属性的父视图模型中的属性在DataTemplateSelector中用于选择要使用的DataTemplate。由于我并不过分迷恋于必须设置Content属性,所以我转而使用ContentControl上的Style with DataTriggers,执行几乎相同的任务。但是,我需要能够遍历逻辑树(如果您想知道错误检查),并且使用任一方法设置ContentTemplate似乎不允许我沿着逻辑树继续从ContentControl进入UserControl一旦它加载。 UserControl在容器中可见,并且通过其DataTemplate定义具有与之关联的正确ViewModel,但ContentControl没有逻辑子项,因为Content属性为null。因此,我已经回归的解决方案是在窗体的构造函数中检查父视图模型上的相关属性,并显式地将其中一个UserControl实例化为ContentControl的Content属性。完成后,我可以将逻辑树移动到UserControl中。似乎必须有比这更好的做事方式,也许是在XAML中做到这一切的一些方法。

因此...
有没有更好的方法来动态选择和实例化我的一个UserControls?将基本不同形式的部分拆分成UserControls的基本技术听起来合乎逻辑吗?看起来这应该是相当普遍的地方,重新使用窗口但改变它的某些部分,但我没有发现我与谷歌的旅行。也许我没有找对地方......

1 个答案:

答案 0 :(得分:2)

我认为您应该查看内容的VisualTree,而不是LogicalTree

此外,我无法从上面的问题中确切地说出来,但听起来您可能正在为您的内容查找ContentTemplate。如果是这种情况,它将无法工作,因为它是一个模板,因此它实际上不包含内容。这就像看着Cookie Cutter找到Cookie一样。

作为旁注,我更喜欢使用DataTemplates而不是DataTriggers。您不需要DataTemplateSelector

在我的ParentViewModel中,我将拥有这样的属性:

private ViewModelBase _currentContent;
public ViewModelBase CurrentContent 
{
    get {return _currentContent;}
    set
    {
        if (value != _currentContent)
        {
            _currentContent = value;
            OnPropertyChanged("CurrentContent");
        }
    }
}

在ParentViewModel的XAML中我会有类似

的内容
<ContentControl Content="{Binding CurrentContent}" />

然后我将为不同的子视图模型类型定义我的数据模板

<DataTemplate DataType="{x:Type local:SubViewModelA}">
    <local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SubViewModelB}" />
    <local:ViewB />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SubViewModelC}" />
    <local:ViewC />
</DataTemplate>

要切换内容,我会将主人的CurrentContent属性更改为应显示的任何SubViewModel