Accessing View from Viewmodel

2020-07-23 04:48发布

I know that's bad design, but I need access to the view from my viewmodel. This is because I have some old controls, e.g. Winforms controls, that don't support binding and need to be filled by code.

I'm using the MVVM model of AvalonDock 2.0 and have something similar to this:

   <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>

So the template NavigationViewTemplate is bound to one item of the collection Tools, which is my ViewModel of type NavigationViewModel.

I have no problem binding e.g. a TextBox to a property of my viewmodel. But I don't know how I can get access from my NavigationViewModel to the tvext:TreeViewExtended control inside the template in order to fill it.

TIA Michael

3条回答
唯我独甜
2楼-- · 2020-07-23 05:07

create Events in your viewmodel and just subscribe to these events in your view, so view and viewmodel are still not strong coupled and you get what you want.

查看更多
迷人小祖宗
3楼-- · 2020-07-23 05:08

yeah, I am not a big fan of letting the ViewModel know about the view, but since you asked, here's one idea:

 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;
     }
查看更多
老娘就宠你
4楼-- · 2020-07-23 05:23

I suggest that you do not access the Winforms control from your ViewModel. Keep everything that relates to the view in the view. You can do this as follows:

  1. Create a WPF custom control, e.g. named TreeViewExtendedWrapper. (See this article for a short tutorial how to create custom WPF controls).

  2. Inside the control template of the custom control (in the Themes\Generic.xaml file), place your Winforms control:

    <ControlTemplate TargetType="{x:Type local:TreeViewExtendedWrapper}">
        <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
            <tvext:TreeViewExtended />
        </Border>
    </ControlTemplate>
    
  3. Add dependency properties to your custom control for all Winforms control properties that you need to bind to the ViewModel.

  4. Also add dependency properties to your custom control for all commands that you need to bind to the view model.

  5. Write C# code inside the code-behind of the custom control to connect the dependency properties of your custom control to the properties, events, and methods of the Winforms control.

  6. Inside your data template, place your custom control with any necessary data bindings:

    <DataTemplate>
        <local:TreeViewExtendedWrapper MyProperty={Binding MyProperty}/> 
    </DataTemplate> 
    

With this approach, you can use data binding to connect ViewModel and Winforms control, i.e. you do not violate MVVM principles.

查看更多
登录 后发表回答