从Viewmodel访问View

时间:2012-06-28 14:19:56

标签: wpf xaml data-binding mvvm

我知道这是糟糕的设计,但我需要从我的viewmodel访问视图。这是因为我有一些旧的控制,例如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>

因此模板NavigationViewTemplate绑定到集合Tools的一个项目,这是我的类型为NavigationViewModel的ViewModel。

我没有问题绑定,例如一个TextBox到我的viewmodel的属性。但我不知道如何从我的NavigationViewModel访问模板中的tvext:TreeViewExtended控件以填充它。

TIA Michael

3 个答案:

答案 0 :(得分:4)

是的,我不是让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;
     }

答案 1 :(得分:3)

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

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

  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>
    
  3. 为您需要绑定到ViewModel的所有Winforms控件属性的自定义控件添加依赖项属性。

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

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

  6. 在数据模板中,将自定义控件放在任何必要的数据绑定中:

    <DataTemplate>
        <local:TreeViewExtendedWrapper MyProperty={Binding MyProperty}/> 
    </DataTemplate> 
    
  7. 使用这种方法,您可以使用数据绑定来连接ViewModel和Winforms控件,即您不违反MVVM原则。

答案 2 :(得分:3)

在您的viewmodel中创建事件,并在视图中订阅这些事件,因此视图和视图模型仍然没有很强的耦合,您可以获得所需的内容。