【问题标题】:WPF ViewModel instantiation affecting child inheritance影响子继承的 WPF ViewModel 实例化
【发布时间】:2015-09-16 06:19:54
【问题描述】:

我有一个子用户控件 (Page1),当它在 XAML 中声明为内联时,它未能继承在我的 WPF 窗口的 DataContext 上设置的 ViewModel (WizardPageViewModel):

<Window x:Class="WPFToolkitWizard.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:common="clr-namespace:WPFToolkitWizard"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <common:WizardPageViewModel/>
</Window.DataContext>
<Grid>
    <common:MyWizardControl>
        <common:MyWizardControl.Pages>
            <common:Page1 Title="Page 1" Description="First page" IsValid="true" />
        </common:MyWizardControl.Pages>
    </common:MyWizardControl>
</Grid>

但是,如果我将其更改为静态资源并按如下方式引用它:

<Window.Resources>
<common:WizardPageViewModel x:Key="vm" />
</Window.Resources>

<common:Page1 Title="Page 1" Description="First page" IsValid="true" DataContext="{StaticResource vm}" />

在 Page1 中声明的依赖于注入的 VM 的绑定工作正常。问题是我想从 Window 的 DataContext 属性中引用 ViewModel,因为这是我们在整个团队中声明我们的 VM 的方式:

<Window.DataContext>
<common:WizardPageViewModel/>
</Window.DataContext>

我尝试如下设置绑定,但没有这样的运气:

<common:Page1 Title="Page 1" Description="First page" IsValid="true" DataContext="{Binding}" />

我也读过关于自动继承 DataContext 的帖子,但在这种情况下它不起作用。

查看详细的调试输出如下:

System.Windows.Data Warning: 60 : BindingExpression (hash=26218178): Default mode resolved to TwoWay
System.Windows.Data Warning: 61 : BindingExpression (hash=26218178): Default update trigger resolved to LostFocus
System.Windows.Data Warning: 62 : BindingExpression (hash=26218178): Attach to     System.Windows.Controls.TextBox.Text (hash=35377412)
System.Windows.Data Warning: 67 : BindingExpression (hash=26218178): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=26218178): Found data context element: TextBox (hash=35377412) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=26218178): DataContext is null
System.Windows.Data Warning: 65 : BindingExpression (hash=26218178): Resolve source deferred

它声明 DataContext 为空,但我只是不知道为什么它不能自动找到我的 VM 实例。

MyWizardControl 是一个用户控件

Page1 是一个 ContentControl

【问题讨论】:

  • 我知道您正在尝试寻找解决方法,但要小心将您的虚拟机用作静态资源。所有资源(静态和动态)都是共享实例,因此绑定到该资源的任何其他内容都将被赋予与本示例中的 Window 相同的实例。如果您无法使其正常工作,请确保将 x:Shared 设置为 false。

标签: wpf xaml mvvm binding datacontext


【解决方案1】:

对我来说似乎按预期工作。

根据您的代码结构,我假设common:MyWizardControlItemsControl,因为它包含Pages 属性。

如果这是真的,那么 ItemsControl 元素(在您的情况下是您的页面)从 ItemControl 的 Source ItemsSource 属性继承 ViewModel,在您的情况下这将是 ObservableCollection&lt;WizardPageViewModel&gt;

由于您没有在ItemsControl 上设置ItemSource 属性,因此它是空的,WPF 无法为其分配任何数据上下文。

静态的有效,因为您明确设置它,覆盖ItemSource 设置的任何可能的DataContext

话虽如此,你有三个选择:

  1. 静态实例化它并自己分配它,就像您在示例中所做的那样(不是很优雅)
  2. 使用带有 ViewModelLocator 的 MVVM 框架(即 Microsoft 的 Prism 框架),这将在每次实例化视图(通过代码或从 XAML)时显式解析 View
  3. 创建一个包含ObservableCollection&lt;WizardPageViewModel&gt; Pages {get;set;}MyWizardViewModel 并将其绑定到ItemsSourceWizardPageViewModel

第一个选项感觉有点脏,但对于演示或教学项目来说可能没问题。

第二个适用于可重用视图/视图模型,如果是 Prims,ViewModelLocator 将为您在 XAML 中插入的每个 Page1 视图创建一个新的视图模型实例。如果您使用依赖注入框架(如 Unity、MEF、Autofac 等),则可以在多个 Page1 控件中使用 ViewModel 的单个实例

第三个选项是首选,因为 ViewModel 旨在与视图一起使用,而您的 ViewPage1,而不是 MyWizardControl(实际上 MyWizardControl 是它自己的带有子视图的视图,他们每个人都意味着拥有自己的 ViewModel,代表 View 需要的数据)。

第三个选项最容易实现,无需使用 MVVM 框架或依赖注入

编辑:一个例子

// Prism ViewModel base class, adjust for your framework or whatever you use as base
public class MyWizardViewModel : BindableBase 
{
    public ObservableCollection<WizardPageViewModel> Pages { get; set; }

    public MyWizardViewModel()
    {
        Pages = new ObservableCollection<WizardPageViewModel>()
        {
            new WizardPage1ViewModel(), // public class WizardPage1ViewModel : WizardPageViewModel
            new WizardPage2ViewModel() // public class WizardPage2ViewModel : WizardPageViewModel
        }
    }
}

XAML:

<Window x:Class="WPFToolkitWizard.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:common="clr-namespace:WPFToolkitWizard"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <common:WizardPageViewModel/>
</Window.DataContext>
<Grid>
    <common:MyWizardControl ItemsSource="{Binding Pages}" />
</Grid>

然后您只需为每个 ViewModel 制作 DataTemplates,然后 ContenControl 将根据 DataContext 的类型显示正确的模板。见here

【讨论】:

  • 他说MyWizardControlUserControl。 Page1 是一个内容控件。他基本上是在 UserControl 上将 ContentControl 添加到它们的集合中。
  • 另外,如果他确实选择了只走静态资源路线,他需要确定并设置x:Shared="false"。否则,该视图模型实例将来可能会与其他元素共享,因为 WPF 将其视为单例实例。
  • &lt;common:MyWizardControl.Pages&gt; 行否则毫无意义。它是复数形式,它可以包含多个视图,而 ItemsControl 就是为此而生的。 MyWizardControlPagesItemsControl 或从它派生的东西(TabControl 等),否则您不能在其中托管多个视图。无论如何,事实仍然是它与ItemsControl 以及它管理它的ItemsSource 元素的DataContext 的方式有关
猜你喜欢
  • 2011-12-03
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 2020-05-19
相关资源
最近更新 更多