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

发布于 2024-11-06 09:17:50 字数 1168 浏览 0 评论 0原文

我现在正在处理一些 WPF 的东西。在将三种非常相似的形式合并为一种的过程中,我提出了一个问题。我编写的代码可以工作,但它似乎远不是最好的解决方案,所以我想我应该向更广泛的受众寻求反馈和/或更好的解决方案。

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

问题本身
最初,我使用带有 DataTemplateSelector 的 ContentControl 从多个 DataTemplate 中进行选择,每个 DataTemplate 都包含一个 UserControl。父视图模型中绑定到 ContentControl 的 Content 属性的属性用于在 DataTemplateSelector 中选择要使用的 DataTemplate。由于我不太喜欢设置 Content 属性,因此我转而使用 ContentControl 上带有 DataTriggers 的 Style,执行几乎相同的任务。但是,我需要能够遍历逻辑树(如果您想知道,则用于错误检查),并且使用任一方法设置 ContentTemplate 似乎都不允许我继续沿着逻辑树从 ContentControl 进入 UserControl一旦加载完成。 UserControl 在容器中可见,并且通过其 DataTemplate 定义具有与其关联的正确 ViewModel,但 ContentControl 没有逻辑子级,因为 Content 属性为 null。因此,我所采用的解决方案是在表单的构造函数中检查父视图模型上的相关属性,并将 UserControl 之一显式实例化到 ContentControl 的 Content 属性中。完成此操作后,我可以将逻辑树放入用户控件中。似乎一定有比这更好的方法,也许是在 XAML 中完成这一切的某种方法。

所以...
有没有更好的方法来动态选择和实例化我的用户控件之一?我将表单的不同部分分成用户控件的基本技术听起来合乎逻辑吗?看起来这应该是相当常见的事情,重复使用窗口但改变它的某些部分,但我在谷歌旅行中没有发现太多。也许我没有找对地方......

I'm working with some WPF stuff at the moment. In the process of coalescing three very similar forms into one, I've come up with an issue. The code that I've written works, but it seems far from the best solution, so I thought I'd ask a wider audience for feedback and/or better solutions.

Some background
The three forms share a lot of XAML and code-behind, but there is a section in each form that is different. I've retained all the shared code in one form, and split off the parts that are different into UserControls, with the intention of dynamically loading one of them into a container at runtime. We're using MVVM, so the form has a ViewModel, as do the new UserControls. Naturally, the form's ViewModel acts as parent and contains the ViewModels of the new UserControls as children.

The issue itself
Initially, I used a ContentControl with a DataTemplateSelector to choose from a number of DataTemplates that each contained one of the UserControls. A property from the parent view model, bound to the ContentControl's Content property, was used in the DataTemplateSelector to select which DataTemplate to use. As I wasn't overly enamoured of having to set the Content property, I switched to using a Style with DataTriggers on the ContentControl, doing pretty much the same task. However, I need to be able to traverse the logical tree (for error checking, if you're wondering), and setting the ContentTemplate with either method doesn't seem to permit me to continue along the logical tree into the UserControl from the ContentControl once it's loaded. The UserControl is visible in the container, and has the right ViewModel associated with it through it's DataTemplate definition, but the ContentControl has no logical children because the Content property is null. So, the solution I've fallen back to is checking the relevant property on the parent view model in the form's constructor, and explicitly instantiating one of the UserControls into the ContentControl's Content property. Once this is done, I can walk the logical tree into the UserControl. It seems there must be a better way of doing things than this, perhaps some way of doing it all in the XAML.

So...
Is there a better way of dynamically selecting and instantiating one of my UserControls? Does my basic technique of having the parts of the form that are different split off into UserControls sound logical? It seems like this is something that should be fairly common-place, re-using a window but changing certain parts of it, but I didn't find much on my travels with Google. Perhaps I wasn't looking in the right places...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

抽个烟儿 2024-11-13 09:17:50

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

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

附带说明一下,我更喜欢使用 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>

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

I think you should be looking at the VisualTree for the Content, not your LogicalTree.

Also, I couldn't really tell from your question above, but it sounded like you might be looking your ContentTemplate for your Content. If that is the case, it won't work because it's a template, so it doesn't actually contain the content. It's like looking at the Cookie Cutter to find the Cookie.

And as a side note, I prefer to use DataTemplates than DataTriggers. You don't need a DataTemplateSelector either

In my ParentViewModel I'll have a property like this:

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

and in the XAML for the ParentViewModel I would have something like

<ContentControl Content="{Binding CurrentContent}" />

Then I'd define my data templates for the different sub-viewmodel types

<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>

To switch content, I'd change the master's CurrentContent property to whatever SubViewModel should be displayed

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文