从视图模型访问视图

MTR*_*MTR 3 data-binding wpf xaml mvvm

我知道这是糟糕的设计,但我需要从我的视图模型访问视图。这是因为我有一些旧控件,例如 Winforms 控件,它们不支持绑定,需要通过代码填充。

我正在使用 AvalonDock 2.0 的 MVVM 模型并且有类似的东西:

   <ad:DockingManager x:Name="dockManager" 
                  DocumentsSource="{Binding Files}"
                  AnchorablesSource="{Binding Tools}"
        ActiveContent="{Binding ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}">
        <ad:DockingManager.LayoutItemTemplateSelector>
            <local:PanesTemplateSelector>
                <local:PanesTemplateSelector.NavigationViewTemplate>
                    <DataTemplate>
                        <tvext:TreeViewExtended />
                    </DataTemplate>
                </local:PanesTemplateSelector.NavigationViewTemplate>
            </local:PanesTemplateSelector>
        </ad:DockingManager.LayoutItemTemplateSelector>
Run Code Online (Sandbox Code Playgroud)

因此模板 NavigationViewTemplate 绑定到集合 Tools 的一项,即我的 NavigationViewModel 类型的 ViewModel。

我没有问题,例如将 TextBox 绑定到我的视图模型的属性。但我不知道如何从我的 NavigationViewModel 访问模板内的 tvext:TreeViewExtended 控件以填充它。

蒂亚·迈克尔

bli*_*eis 6

在视图模型中创建事件,然后在视图中订阅这些事件,因此视图和视图模型仍然不强耦合,您会得到您想要的。


den*_*zov 5

是的,我不是让 ViewModel 知道视图的忠实粉丝,但是既然你问了,这里有一个想法:

 1. Create an interface for your View (if you haven't already) and add whatever functionality to that interface that you need access to from the ViewModel. Lets call it ISomeView
 2. add/implement the interface on the View
 3. add property to the ViewModel ISomeView View {get;set;} 
 4. in the view depending where the ViewModel is being injected assign populate the ViewModel's property, for example you can do it on DataContextChanged:

    private void OnDataContextChanged (object sender, ...EventArgs e)
    {
         // making up your ViewModel's name as ISomeViewModel
         ((ISomeViewModel)sender).View = this;
     }
Run Code Online (Sandbox Code Playgroud)


小智 4

我建议您不要从 ViewModel 访问 Winforms 控件。将与视图相关的所有内容保留在视图中。您可以按如下方式执行此操作:

  1. 创建一个WPF自定义控件,例如名为TreeViewExtendedWrapper. (有关如何创建自定义 WPF 控件的简短教程,请参阅本文)。

  2. 在自定义控件的控件模板内(在 Themes\Generic.xaml 文件中),放置 Winforms 控件:

    <ControlTemplate TargetType="{x:Type local:TreeViewExtendedWrapper}">
        <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
            <tvext:TreeViewExtended />
        </Border>
    </ControlTemplate>
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将依赖属性添加到您需要绑定到 ViewModel 的所有 Winforms 控件属性的自定义控件。

  4. 还要为需要绑定到视图模型的所有命令添加依赖属性到自定义控件。

  5. 在自定义控件的代码隐藏内编写 C# 代码,以将自定义控件的依赖属性连接到 Winforms 控件的属性、事件和方法。

  6. 在数据模板内,将自定义控件与任何必要的数据绑定一起放置:

    <DataTemplate>
        <local:TreeViewExtendedWrapper MyProperty={Binding MyProperty}/> 
    </DataTemplate> 
    
    Run Code Online (Sandbox Code Playgroud)

通过这种方法,您可以使用数据绑定来连接ViewModel和Winforms控件,即您不违反MVVM原则。