【问题标题】:Accessing View from Viewmodel从 Viewmodel 访问视图
【发布时间】:2012-06-28 14:19:55
【问题描述】:

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

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

TIA 迈克尔

【问题讨论】:

  • 不要害怕在你的代码中添加一些额外的代码来达到这个目的。如果您想与不同的 UI 共享您的视图模型,那么这样做会很困难。您的视图模型服务于 UI,但不应直接依赖特定的 UI 组件。

标签: wpf xaml data-binding mvvm


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案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> 
        

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

      【讨论】:

      • 我正在尝试您的方法。但由于我从未编写过自定义 WPF 控件,您能告诉我,如何在 TreeViewExtendedWrapper 中为 DataContextChanged 建立处理程序?
      • 您可以使用DependencyProperty.OverrideMetadata() 方法来执行此操作。 This article 包含一些示例代码,展示了如何在 DataContext 属性更改时收到通知。
      • 文章中的示例不起作用,我收到错误 CS0123: Keine Überladung für "OnDataContextChanged" stimmt mit dem Delegen "System.Windows.PropertyChangedCallback" überein。
      • 属性中OnDataContextChanged的签名好像文章里写错了。应该是static void OnDataContextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
      • 我接受这个作为正确答案,因为包装器的想法。尽管自定义控件对于此目的来说太复杂了。我改为使用用户控件。
      猜你喜欢
      • 1970-01-01
      • 2014-03-06
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2011-11-04
      相关资源
      最近更新 更多