【问题标题】:If setting a DataContext within a constructor, does it matter if I set it before or after the call to InitializeComponent()?如果在构造函数中设置 DataContext,在调用 InitializeComponent() 之前或之后设置它是否重要?
【发布时间】:2017-07-11 04:50:33
【问题描述】:

我有一个 WPF 窗口,它在其构造函数中接受了一些参数。然后我使用这些构造函数来设置窗口的状态。该构造函数过程的一部分是实例化我的视图模型类,然后将其设置为窗口DataContext

我的问题是我应该什么时候将我的DataContext 设置为等于我的视图模型对象——在调用InitializeComponent() 之前还是之后?

我问是因为如果我事先设置它,那么我需要手动启动在窗口初始化之后执行的代码,因为在分配DataContext 时应该触发某些事件,或者重新分配。

我的假设是,如果我在调用 InitializeComponent() 之后将 DataContext 设置为不应该有任何绑定问题,但我想在最后调用连接我的窗口之前就此事征求意见这边走。如果我在调用InitializeComponent() 后设置我的DataContext,我是否会错过一些可能会回来困扰我的东西?

【问题讨论】:

  • 您回答了您的问题,在调用 InitializeComponents()(加载/解析/处理 xaml 的任务)之前设置 DataContext 将需要刷新(例如,将其设置为 null 然后返回),所以必须在之后设置。
  • 我总是在 InitializeComponent 之后设置 DataContext,从来没有遇到过与该事实严格相关的任何问题。
  • 我的建议是以不同的方式处理它:不要在窗口内设置窗口数据上下文。相反,在窗口内的子元素(例如网格)上设置数据上下文,并在 Window.Loaded 事件中进行,而不是在构造函数中进行。但是,如果我没记错的话,这与 UserControl 比与 Window 更相关,而且我只是将这种方法作为一种习惯进行了调整。
  • @grek40 我明白你在解释什么,但请解释为什么这是(可能)正确的方法。实际上,请简要回答我的问题,但请在答案中解释您的建议。如果我看到优点,我会给你答案。谢谢。
  • 也许我可以稍后再写一个答案,现在时间不够,需要重新检查一些细节,才能确定答案是否有价值。

标签: c# .net wpf mvvm


【解决方案1】:

我的问题是我应该在什么时候将我的 DataContext 设置为等于我的视图模型对象——在调用 InitializeComponent() 之前还是之后?

除非您依赖在调用 InitializeComponent() 期间建立的某些绑定,否则这无关紧要,例如 ElementName 绑定:

Cannot bind ItemsSource to ElementName

InitializeComponent() 方法本身定位已编译 XAML 文件的 URI,并将其传递给解析 BAML(即已编译 XAML)的 LoadComponent() 方法,并创建您在 XAML 标记中定义的元素的实例:

What is the connection between .xaml and .xaml.cs files

只需将窗口的DataContext 属性设置为视图中的元素绑定到的视图模型类的实例,也可以在调用InitializeComponent() 方法之后完成。在构造函数返回之前,这些绑定不会被解析。

【讨论】:

  • 我刚刚进行了测试,在InitializeComponents 之前设置了DataContext,并且我的绑定在InitializeComponents 返回之前解决了
【解决方案2】:

与您的要求不同,我建议进行两个更改:

  1. 设置内部元素的DataContext 而不是Window / UserControl 本身。
  2. Loaded 上设置DataContext 而不是构造函数。

在查看UserControl 时,这些点更加明显,它可能会嵌入多个点,但请记住,Window 可以通过显式启动代码而不是某些App.StartupUri 创建。

关于第一点,请考虑 OOP 设计基础。忘掉 WPF / XAML 细节,记住你从 Window 类派生并创建它的子类。此类的契约包括一个名为DataContext 的公共get/set 属性,它接受任何类型的object。所以你至少应该考虑一下,如果有人从外面替换你的DataContext,你会搞砸到什么程度。当您改为在窗口内的下一个内部 FrameworkElement 上设置 DataContext 时,它托管在窗口所拥有的环境中。

Loaded 上设置DataContext 对我有用,而我在构造函数时间设置方面遇到了问题。但是,我实际上不记得它的细节,也许它与视觉设计师有关(我不再使用)。对于其他控件,更容易解释:当托管在虚拟化面板中时,构造函数时间初始化很糟糕,而且属性初始值设定项(new MyControl { Prop = Value },XAML 属性分配,...)也不会在构造函数运行时处理,因此对象往往是与稍后呈现的状态不同。

【讨论】:

  • 如果在 usercontrols ctor 中发生异常,那么你会得到类似 XamlInitializationExeption 的东西,这可能会造成混淆。如果 viemodel ctor 花费的时间太长,您可能遇到的其他问题是减慢 ui。但我不建议在加载的事件中设置视图模型。
  • @Liero 大多数时候我都是从外部源绑定视图模型,所以我不必担心时间和变化,但如果你想建立一个内部数据上下文,你会在哪里/如何做到这一点,为什么加载的事件不是一个好地方?
  • 您不应该在 Loaded EventHandler 中设置 DataContext,因为此时,DataContext 已经从父级继承。所以想象一下你有WindowViewModelUserControlViewModel。如果您在UserControl.Loaded 事件中设置数据上下文,则您将其从WindowViewModel 更改为UserControlViewModel
【解决方案3】:

这是我对@mm8 答案的补充:

  1. 通常没关系,但是在InitializeComponents之后设置DataContext。当DataContextChanged 事件被调用时,您自然会期望组件已经初始化。

    此外,最好了解组件是否可以在没有 DataContext 的情况下进行初始化,并将可能的初始化问题与绑定问题分开。如果在 InitializeComponents 之前设置 DataContext,绑定问题可能会导致 InitializeComponents 出现异常。

  2. 让你的 ViewModel 构造器非常快。不要进行任何 DB 调用或任何 I/O 调用等。您希望尽快显示 UI。

  3. 确保您的 ViewModel 构造函数永远不会引发异常。参数验证是可以的,但仅用于调试目的。它不应该在生产中发生。

  4. 如果您需要将数据加载到视图模型中,请创建单独的异步方法,例如Activate(),您将从 View 的 LoadedOnNavigatedTo 事件中调用它。

    另外,如果您订阅了 ViewModel 中的某些事件,您应该取消订阅。订阅的理想位置是激活方法,分别Deactivate 取消订阅。如果您订阅 ViewModel 的 ctor,可能会发生永远不会调用 Activate/Deactivate 并引入内存泄漏的情况。

  5. 如果您觉得您的绑定拖慢了 UI,请尝试使用 {Binding IsAsync=True}、resp x:Bind,或者在最坏的情况下尝试使用代码隐藏来设置属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 2011-07-20
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    相关资源
    最近更新 更多