如何在 viewmodel 中访问 mvvm 模型中的控件?

Posted

技术标签:

【中文标题】如何在 viewmodel 中访问 mvvm 模型中的控件?【英文标题】:How can i access a control in mvvm model in viewmodel? 【发布时间】:2012-12-23 13:25:40 【问题描述】:

我有一个 WPF 窗口,在那个窗口中我有一个网格。

我使用 M-V-VM 模型,我想在代码中(在视图模型中)动态地将 TextBox 添加到网格中

如何访问网格?

【问题讨论】:

在走这条路之前,我会确保它是绝对必要的,除非绝对没有其他选择,否则从视图模型访问视图通常是不好的做法。 @BenjaminPaul 所说的。使用 MVVM 时,您无需从 ViewModel 添加控件。使用绑定来完成这项工作。 是的,我知道,这正是我的意思。然而,我有时需要在视图模型中获得对控件的引用,以适应不支持常规绑定的控件。这也是我所指的场景。 【参考方案1】:

使用 Supervising Controller 模式。

阅读:

CaliburnMicro MVVM 框架的示例实现如下所示(对所有其他框架都一样——或者如果你自己做 MVVM,你也可以手动做):

http://drc.ideablade.com/devforce-2012/bin/view/Documentation/cocktail-tutorial-talk-to-view

示例:

1) 定义接口IView,其中ViewModel (VM) 将使用所需的方法与View 对话

public interface IView 

    void AddTextBoxToGrid();

2) 从你的IView继承View后面的代码并实现IView.AddTextboxToGrid()方法

public partial class View: IView 

    public void AddTextBoxToGrid() 
      
        // implement here your custom view logic using standard code behind; 
    

3) IView 类型的属性添加到您的VM

public class ViewModel 

    public IView View  get; set; 

4) VM 上的View 属性设置为View 的实例IView 例如在后面的代码中:

 DataContext.View = this as IView; 

或者在 Caliburn 中你可以使用 IScreen.OnViewAttached 覆盖方法)

public partial class View: IView 

    public View()
    
        // access you VM by the strategy of your framework or choice - this example is when you store your VM in View's DataContext
        (DataContext as ViewModel).View = this as IView;
     

    public void AddTextBoxToGrid() 
      
        // implement here your custom view logic using standard code behind; 
    

5) 在您的VM 致电IView.AddTextboxToGrid()

public class ViewModel 

    public IView View  get; set; 

    public void AddTextBoxToGrid() 
    
        if (View == null) return;
        View.AddTextBoxToGrid()
    

【讨论】:

最好使用构造函数注入将IVew实例发送到ViewModel(我的意思是,ViewModel类的构造函数应该接受IView实例)。 需要强调的是,在视图中从 viewmodel 创建依赖也不是最纯粹的 mvvm。当您使用(DataContext as SomeViewModel) 时,您的视图将依赖于SomeViewModel,这可能是不可取的。你怎么看? @MohammadDehghan:您将如何使用 Caliburn 实现这一点? 非常明确的答案。在保持 MVVM 模式的同时还有其他选择吗? @heltonbiker 为什么不受欢迎?视图总是知道视图模型(请记住,所有绑定都引用视图模型的属性)并且添加一个直接依赖项不会以任何其他方式造成伤害。什么是“禁止”是视图模型直接知道视图。【参考方案2】:

您应该将创建代码移至 View,并且 ViewModel 应该在应该调用它时通知视图。

【讨论】:

【参考方案3】:

您还可以在视图后面的代码中使用视图的 DataContext(即 ViewModel),并将文本框添加到那里的网格中。那会更有意义。

如果您在 XAML 文件中为网格命名,您将能够立即在后面的代码中访问网格。

【讨论】:

【参考方案4】:

如果您使用的是 Caliburn Micro,请执行以下步骤:

    使ViewModel继承自接口IViewAware;你要实现这个接口的两个方法AttachView和GetView。

    定义一个 View 类型的变量来获取对 View 的引用

    详见下文:

    private SomeViewClass v;
    public void AttachView(object view, object context = null)
    
        v = view as BomView;
        if (ViewAttached != null)
             ViewAttached(this,
             new ViewAttachedEventArgs()  Context = context, View = view );
    
    
    public object GetView(object context = null)
    
        return v;
    
    

稍后您可以通过 v 访问视图上的单个元素,例如 v.txtName="John";等等……

【讨论】:

BomView = SomeViewClass。视图类的名称。

以上是关于如何在 viewmodel 中访问 mvvm 模型中的控件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MVVM 和 Caliburn Micro 在多个 ViewModel 中使用模型中的数据

MVVM:修改模型,如何正确更新 ViewModel 和 View?

如何在MVVM中使用相同的ViewModel拥有多个视图?

mvvm

如何使用 mvvm 模式从不同的 ViewModel 获取属性和调用命令

MVVM:GUI 和 ViewModel 之间的真正分离