如何在具有一个WPF格式的WinForms应用程序中使用Caliburn Micro

时间:2012-01-31 23:14:42

标签: wpf caliburn.micro

我们有一个(大规模)遗留WinForms应用程序,通过菜单项打开一个WPF表单。这个WPF表单将托管一个Infragistics网格,以及一些按钮/下拉菜单。

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

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

  • 有人可以提供一些关于如何使用Caliburn Micro和WPF表格的指示吗?
  • 或者也许告诉我为什么使用Caliburn Micro可能没有意义呢?

我到目前为止阅读的文档涉及引导过滤器,它确保应用程序以所需的根视图模型开始,而不是上面的场景。

非常感谢!

3 个答案:

答案 0 :(得分:6)

经过大量Google搜索并浏览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(类型serviceType,字符串键),然后必须使用它来解析WpfControlViewModel。

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

答案 1 :(得分:2)

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

  1. 您不必创建WPF窗口,
  2. 您不必从View中直接绑定到ViewModel。
  3. 为此,我们需要创建自己的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                
            }
        }
    }
    

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

答案 2 :(得分:1)

以下是您可以从

开始的事情
  • 创建ViewModels并从CM框架提供的PropertyChangedBase类继承它们。
  • 如果需要,使用EventAggregator impelmentation进行松散耦合的通信\集成
  • 实现AppBootStrapper,而不使用定义根视图模型的通用实现。

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