从 MainWindow 访问 UserControl 中的属性 (WPF/MVVM)

Accesing properties in a UserControl from the MainWindow (WPF/MVVM)

我有一个小问题,我正在寻找实现它的正确方法。

我有一个 MainWindow 和一个 UserControl 来显示一些结果,在 MainWindow 中我有一个按钮 "load" 来加载一些数据,UserControl 应该显示它们。

我不确定在 WPF 和 MVVM 中执行此操作的正确方法是什么:

我应该将 MainWindowModel 传递给 UserControlModel 吗?;

我应该将 UserControlModel 传递给 MainWindowModel 吗?;

我是否应该公开 属性 我需要在我的 UserControl 中填充为 DependencyProperty,然后在 MainWindow 上填充它?

如有任何建议,我们将不胜感激。谢谢!

编辑 1: 这就是我调用 UserControl 的方式:

<TabControl Grid.Row="1"
            Grid.RowSpan="2"
            Grid.Column="0"
            VerticalAlignment="Stretch">

    <!--Result View-->
    <TabItem Header="{Binding TabImportHeader}">
        <results:ResultView/>
    </TabItem>

    <!--Configuration Tab-->
    <TabItem Header="{Binding TabConfigurationHeader}">
        <configuration:ConfigurationView />
    </TabItem>
</TabControl>

出现问题的我的 UserControl 是 ResultView

MainWindowVMInstance.UserControlVMInstance.Property

UserControl 在你的 MainWindow 里面。

因此你的 MainWindow 有你的 UserControlVM 的 属性(/实例)。

注意:如果您还需要在 UserControlVM 中引用 MainWindowVM,请将其传递给构造函数并将其存储为 属性。

在 xaml 中看起来像这样(在 MainWindow.xaml 中):

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

别忘了 DataTemplate:

<DataTemplate DataType="{x:Type vm:UserControlVM}">
    <view:UserControlView/>
</DataTemplate>

问题更新后编辑:

这是一个示例,其中包含您的部分代码,用于演示 WPF 和 MVVM 的实际应用。您只需在 UserControl.Resources 中定义 DataTemplate,然后通过 BindingContentControl 提供 UserControlVM 的一个实例。 WPF 知道此类型有一个 DataTemplate,并将在 ContentControl 所在的位置添加一个 UserControlView 的实例。

<MainWindow>
    <MainWindow.Resources>
        <DataTemplate DataType="{x:Type vm:UserControlVM}">
            <view:UserControlView/>
        </DataTemplate>
    </MainWindow.Resources>

    <!-- Your TabControl -->
    <TabControl>
        <!--Result View-->
        <TabItem Header="{Binding TabImportHeader}">
            <ContentControl Content="{Binding TabImportCONTENT}"/>
        </TabItem>
    </TabControl>
</MainWindow>

Microsoft WPF 控件是使用 DependencyProperties 构建的

DependencyProperty 支持以下功能,在引入特定于控件的视图模型时您将失去这些功能:

  • DP可以在样式/触发器中设置(样式和模板很重要,开发人员经常希望重用控件的功能,但完全改变外观)

  • DP可以通过数据绑定设置(包括2个控件之间的交叉绑定)

  • DP可以从元素树中的父元素自动继承它的值

  • DP可以动画

在 ViewModel 之间共享资源是一种常见的情况,但是如果它确实成为问题,它通常是架构中出现问题或缺乏问题的一个很好的指标。

最简单的解决方案是让 UserControlVM 持有对 MainWidowVM 的引用并订阅某个事件,该事件由接口通过 Command 调用。然而,声明功能的 ViewModels 之间的交叉引用可能会变得非常混乱,交叉引用和循环引用最终会阻止 GarbageCollection 正确完成其工作,从而导致不必要的内存泄漏。

您还可以让您的 MainWindowVM 保留对每个其他 ViewModel 的引用,以通过至少集中交叉引用来提供集中的通信点。

下一个最简单的解决方案是使用 weak event binding 实现类似外部消息传递系统的东西。这摆脱了其他对象中对象的固定引用,但代价是性能极其沉重。如果您正在开发的应用程序具有例如包含 select 元素的主列表和不同 windows 中的许多模块化详细列表,这可能是可行的 - 来自 1 个主端的单向通信具有不频繁的更改和异步许多细节端的实现。对于您的目的,这应该没问题,如果这些事件将自动触发,但我建议完全采用不同的路由器。

上面列出的问题就是为什么MVVM不仅有ModelViewModelView而且通常还用Services来封装共享逻辑and/or 数据(资源)从 ViewModels 转移到有效的共享资源中。 您的 ViewModel(例如,在 LocalizationService 的情况下可能还有视图)将保存对服务的引用,并且通常都指向该服务的单个实例。这也消除了不需要的交叉引用,并允许正确绑定和正确(取消)订阅事件,而不会受到弱绑定的性能影响。

使用 Services 而不是交叉引用 ViewModels 的另一个好处是,它确实让您有机会实际遵循 SOLID principles of object-oriented programming 并在其所属的位置拥有逻辑,而无需重复。

之后可以使用 IoC-Container 在运行时正确地注入引用和依赖项,但是这会带来隐藏依赖项的风险以及项目设置、文档等方面的大量准备时间.如果第一次做的不对and/or。我个人不会走这条路,除非团队中的每个成员都真正知道他们在做什么。