我有一个WPF窗口,它在构造函数中包含一些参数。然后我使用这些构造函数来设置窗口的状态。该构造函数进程的一部分是实例化我的视图模型类,然后将其设置为窗口DataContext
。
我的问题是,在调用DataContext
之前或之后,我应该何时将InitializeComponent()
设置为与我的视图模型对象相等?
我问,因为如果我事先设置它,那么我需要手动启动窗口初始化后执行的代码,因为某些事件应该在分配DataContext
时触发,或重新分配。
我的假设是,如果我在调用DataContext
之后设置InitializeComponent()
,则不应该存在任何约束性问题,但我想在最后一次调用之前询问有关此问题的建议这样就把我的窗户连接起来。如果我在致电DataContext
后设置InitializeComponent()
,我可能会遗漏一些可能会让我困扰的事情吗?
答案 0 :(得分:3)
我的问题是,在调用InitializeComponent()之前或之后,我应该何时将DataContext设置为等于我的视图模型对象?
除非您依赖于在调用InitializeComponent()期间建立的某些绑定(例如ElementName
绑定),否则无关紧要:
Cannot bind ItemsSource to ElementName
InitializeComponent()
方法本身将URI定位到已编译的XAML文件,并将其传递给解析BAML的LoadComponent()
方法,即已编译的XAML,并创建已定义的元素的实例在您的XAML标记中:
What is the connection between .xaml and .xaml.cs files
只需将窗口的DataContext
属性设置为视图模型类的实例,视图中的元素也可以在调用InitializeComponent()
方法后完成。在构造函数返回之前,这些绑定尚未解析。
答案 1 :(得分:1)
与您的要求不同,我建议进行两项更改:
DataContext
,而不是Window
/ UserControl
本身。DataContext
而不是构造函数上设置Loaded
。这些观点在查看UserControl
时更为明显,Window
可能会嵌入多个点,但请记住,App.StartupUri
可以通过显式启动代码而不是Window
来创建}。
关于第一点,请考虑OOP设计基础知识。忘掉WPF / XAML细节,记住你从DataContext
类派生并创建它的子类。此类的合同包括一个名为object
的公共get / set属性,它接受任何类型的DataContext
。所以,如果有人从外面替换你的DataContext
,你至少应该考虑,你会有多糟糕。当您在窗口内的下一个内部FrameworkElement
上设置DataContext
时,它将托管在窗口所拥有的环境中。
在Loaded
上设置new MyControl { Prop = Value }
对我有用,而我在构造函数时间设置方面遇到了问题。但是,我实际上无法回想起它的细节,也许它与视觉设计师(我不再使用)有关。对于其他控件,更容易解释:构造函数时间初始化在虚拟化面板中托管时很糟糕,属性初始化器(allow nulls
,XAML属性赋值,...)也不会在构造函数运行时处理,因此对象倾向于处于与以后呈现的状态不同的状态。
答案 2 :(得分:1)
这是我对@ mm8的回答:
通常无关紧要,但在InitializeComponents之后设置DataContext。调用DataContextChanged
事件时,您自然希望组件已经初始化。
另外,了解是否可以在没有DataContext的情况下初始化组件并将可能的初始化问题与绑定问题分开是很好的。如果在InitializeComponents之前设置DataContext,则绑定问题可能会导致InitializeComponents中出现异常。
使ViewModel构造函数非常快。不要进行任何数据库调用或任何I / O调用等。您希望尽快显示用户界面。
确保您的ViewModel构造函数永远不会抛出异常。参数验证没问题,但仅用于调试目的。它绝不应该在生产中发生。
如果需要将数据加载到viewmodel中,请创建单独的异步方法,例如, Activate()
,您可以在View Loaded
或OnNavigatedTo
事件中调用。
此外,如果您订阅ViewModel中的某些事件,则应取消订阅。订阅的理想场所是Activate方法,resp Deactivate
取消订阅。如果您在ViewModel的ctor中订阅,可能会发生永远不会调用Activate / Deactivate并引入内存泄漏的情况。
如果您认为绑定会降低用户界面的速度,请尝试使用{Binding IsAsync=True}
,resp x:Bind
,或尝试使用代码隐藏在最坏的情况下设置属性。