【问题标题】:How to use Caliburn Micro in a WinForms app with one WPF form如何通过一个 WPF 表单在 WinForms 应用程序中使用 Caliburn Micro
【发布时间】:2012-01-31 23:14:41
【问题描述】:

我们有一个(大量)旧版 WinForms 应用程序,它通过一个菜单项打开一个 WPF 表单。此 WPF 表单将承载 Infragistics 网格和一些按钮/下拉菜单。

这种单独的 WPF 形式代表了向 WPF 迁移的初期阶段。稍后,应用程序的更多组件将迁移到 WPF,最终将迁移到整个应用程序本身。

作为迁移的一部分,我们希望使用 Caliburn Micro。因此,如果我们可以从这个单独的 WPF 表单开始使用它会很好。

  • 谁能提供一些关于如何在 WPF 表单中使用 Caliburn Micro 的指导?
  • 或者告诉我为什么现在使用 Caliburn Micro 可能没有意义?

到目前为止,我阅读的文档涉及引导程序,可确保应用程序以所需的根视图模型启动,而不是上述场景。

非常感谢!

【问题讨论】:

  • 我认为,在这种情况下,C.M.会给你带来更多的麻烦而不是解决。厘米。旨在帮助纯 WPF 应用程序通过其广泛/自动化的 MVVM 框架更快地“启动并运行”。你的应用程序已经过了从框架构建的阶段,在这个阶段引入一个新的框架将是非常具有挑战性的。可能对您最有帮助的是应用可靠的 MVVM(硬方法)来过渡到 WPF。由于该项目是在 WinForms 中,因此它是用一个体面的 MVC 实现构建的可能性非零,因此向 MVVM 的过渡可能不那么痛苦。祝你好运!

标签: wpf caliburn.micro


【解决方案1】:

经过大量谷歌搜索并浏览了 Caliburn Micro 源代码,我想出了一种适用于示例测试应用程序的方法。由于某些原因,我无法在此处发布测试应用程序,但简而言之就是这里的方法。

  • 使用按钮创建 WinForm。
  • 单击按钮时,显示 ChildWinForm
  • 在 ChildWinForm 的加载处理程序中:

    
    // You'll need to reference WindowsFormsIntegration for the ElementHost class
    // ElementHost acts as the "intermediary" between WinForms and WPF once its Child
    // property is set to the WPF control. This is done in the Bootstrapper below.    
    var elementHost = new ElementHost{Dock = DockStyle.Fill};
    Controls.Add(elementHost);
    new WpfControlViewBootstrapper(elementHost);
    
  • 上面的引导程序是您必须编写的。

  • 有关它需要做的所有事情的更多信息,请参阅Customizing the Bootstrapper 中的Caliburn Micro documentation
  • 出于本文的目的,使其派生自 Caliburn Bootstrapper 类。
  • 它应该在其构造函数中执行以下操作:

    
    // Since this is a WinForms app with some WPF controls, there is no Application.
    // Supplying false in the base prevents Caliburn Micro from looking
    // for the Application and hooking up to Application.Startup
    protected WinFormsBootstrapper(ElementHost elementHost) : base(false)
    {
        // container is your preferred DI container
        var rootViewModel = container.Resolve();
        // ViewLocator is a Caliburn class for mapping views to view models
        var rootView = ViewLocator.LocateForModel(rootViewModel, null, null);
        // Set elementHost child as mentioned earlier
        elementHost.Child = rootView;
    }
    
  • 最后要注意的是,您必须在 WpfControlView 的 XAML 中设置 cal:Bind.Model 依赖属性。

    
    cal:Bind.Model="WpfControls.ViewModels.WpfControl1ViewModel"
    
  • 使用依赖属性的值作为字符串传递给 Bootstrapper.GetInstance(Type serviceType, string key),然后它必须使用它来解析 WpfControlViewModel。

  • 由于我使用的容器 (Autofac) 不支持纯字符串解析,因此我选择将属性设置为视图模型的完全限定名称。然后可以将此名称转换为类型,并用于从容器中解析。

【讨论】:

  • 可以使用 ViewModelBinder.Bind(rootViewModel, rootView, null);在 ViewLocator.LocateForModel 之后而不是 cal:Bind.Model
【解决方案2】:

根据已接受的答案(很好!),我想向您展示如何以 ViewModel First 方法实现 WinForms Bootstrapper,其方式如下:

  1. 您不必创建 WPF 窗口,
  2. 您不必从 View 中直接绑定到 ViewModel。

为此,我们需要创建自己的 WindowManager 版本,确保我们不在 Window 上调用 Show 方法(如果适用于您的情况),并允许发生绑定。

这里是完整的代码:

public class WinformsCaliburnBootstrapper<TViewModel> : BootstrapperBase where TViewModel : class
{

    private UserControl rootView;

    public WinformsCaliburnBootstrapper(ElementHost host)
        : base(false)
    {
        this.rootView = new UserControl();
        rootView.Loaded += rootView_Loaded;
        host.Child = this.rootView;
        Start();
    }

    void rootView_Loaded(object sender, RoutedEventArgs e)
    {
        DisplayRootViewFor<TViewModel>();
    }

    protected override object GetInstance(Type service, string key)
    {
        if (service == typeof(IWindowManager))
        {
            service = typeof(UserControlWindowManager<TViewModel>);
            return new UserControlWindowManager<TViewModel>(rootView);
        }
        return Activator.CreateInstance(service);
    }

    private class UserControlWindowManager<TViewModel> : WindowManager where TViewModel : class
    {
        UserControl rootView;

        public UserControlWindowManager(UserControl rootView)
        {
            this.rootView = rootView;
        }

        protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
        {
            if (isDialog) //allow normal behavior for dialog windows.
                return base.CreateWindow(rootModel, isDialog, context, settings);

            rootView.Content = ViewLocator.LocateForModel(rootModel, null, context);
            rootView.SetValue(View.IsGeneratedProperty, true);
            ViewModelBinder.Bind(rootModel, rootView, context);
            return null;
        }

        public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
        {              
            CreateWindow(rootModel, false, context, settings); //.Show(); omitted on purpose                
        }
    }
}

我希望这可以帮助有相同需求的人。它确实救了我。

【讨论】:

    【解决方案3】:

    你可以从这里开始

    • 创建 ViewModel 并从 CM 框架提供的 PropertyChangedBase 类继承它们。
    • 如果需要,使用 EventAggregator 实现松散耦合通信\集成
    • 在没有定义根视图模型的通用实现的情况下实现 AppBootStrapper。

    现在您可以使用视图优先方法并使用视图上的 Bind.Model 附加属性将视图绑定到模型。我创建了一个示例应用程序来描述here 的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 2018-03-10
      • 2020-05-22
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多