【问题标题】:Calling method of UserControl from ViewModel with Caliburn.Micro使用 Caliburn.Micro 从 ViewModel 调用 UserControl 的方法
【发布时间】:2013-11-12 02:32:10
【问题描述】:

我正在使用 Caliburn.Micro 编写 ViewModel-first MVVM 应用程序 我的视图包含一个第 3 方用户控件,它实现了我想要/需要从关联的 ViewModel 调用的方法。如何在坚持 MVVM 原则的同时做到这一点?

在 SO 上存在一个旧的 thread,在更具体的上下文中提出了类似的问题。如果有人可以充实那里建议的方法,我将不胜感激。

方法一表明视图可以订阅 IEventAggregator 消息。但是我不必使用文件后面的代码来做到这一点吗? (我认为这在 MVVM 中是一个很大的禁忌)

关于方法二,我不知道该怎么做。关于方法三,这是我首先尝试的方法,但不知何故我并没有让它发挥作用。

【问题讨论】:

    标签: c# wpf mvvm caliburn.micro


    【解决方案1】:

    让我澄清一下你的理解:

    是的,通常避免使用代码隐藏中的代码,但这只是因为 MVVM 可以很容易地绑定到视图模型属性和命令,以便将您的视觉元素与幕后功能联系起来

    在视图的代码隐藏中特定于视图的代码是完全可以接受的,前提是它不跨越关注的边界。例如,我的应用程序中有一个视图,它对页面进行一些可视化处理,为此我需要视图中有代码。此代码也可能与视图模型层交互,但它不会直接引用视图模型,因此我的组件保持松散耦合

    如果您有需要调用特定方法的控件,那么创建一个事件聚合器消息以将通知传播到视图是非常好的,因为您仍然保持视图模型和视图之间的关注点分离(并且应用程序组件仍然被封装并且可测试)

    示例视图(为了清楚起见,我已将所有事件聚合器连接代码和潜在的依赖注入内容放在一边):

    public class MyView : IHandle<SomeNotificationMessageType>
    {
        // Handler for event aggregator messages of type SomeNotificationMessageType
        public void Handle(SomeNotificationMessageType message)
        {
            // Call a method on one of the page controls
            SomePageControl.SomeMethod();
        }
    }
    

    显然,您不会在 ViewModel 中做这样的事情:

    public class MyViewModel : IViewAware
    {
        public void DoSomethingThatAffectsView()
        {
            var view = this.GetView() as MyView;
    
            view.SomePageControl.SomeMethod();
        }
    }
    

    这违反了 MVVM 原则,因为您将 MyViewModel 和 MyView 紧密耦合。

    如果您想在 caliburn micro 中使用Context 属性,该属性允许同一视图模型上的多个视图,该怎么办?上面的代码会中断——即使你检查了 View 类型,你仍然会得到意大利面条代码,例如

    public class MyViewModel : IViewAware
    {
        public void DoSomethingThatAffectsView()
        {
            var myview = this.GetView() as MyView;
    
            if(myview != null)        
                myview.SomePageControl.SomeMethod();
    
            var myotherview = this.GetView() as MyOtherView;
    
            if(myotherview != null)        
                myotherview.SomePageControl.SomeMethod();
    
            // ad infinitum...
        }
    }
    

    当然这是主观的:可能是您的用户控件以复杂的方式影响视图模型和视图,在这种情况下,您可能需要考虑查看架构并找出用户控件如何更好地适应

    您对 UC 是什么以及它的方法有什么了解吗?

    【讨论】:

    • 感谢您的解释。我将采用 EventAggregator 的想法。我正在谈论的 UC 是 AvalonDock,我想要做的是保存/加载布局。在版本 1.* 中有一个 SaveLayout() 方法;现在从 2.0 版开始,显然必须将整个 UC DockingManager 传递给一个特殊的序列化器。
    • “为清楚起见,将所有...依赖注入的东西都放在了外面”,现在还不清楚。 :) 我能想到的获得事件聚合器的唯一方法是在我的视图的无参数构造函数中使用IoC.Get&lt;IEventAggregator&gt;()。有没有更好的方法来注入依赖而不是直接从容器中拉出对象?
    • 您的视图也参与了依赖注入 - 只需将其添加到构造函数中即可。
    • 如果我这样做,那么我会得到运行时异常:“没有为此对象定义无参数构造函数。”还是我错过了什么?到目前为止,我最终在 OnViewAttached() 方法内部的 ViewModel 中调用了 eventAggregator.Subscribe(view),我认为这比在 View 构造函数中使用 IoC.Get&lt;IEventAggregator&gt;() 更好。
    猜你喜欢
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 2019-09-05
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2022-10-18
    相关资源
    最近更新 更多