Как использовать Caliburn Micro в приложении WinForms с одной формой WPF - PullRequest
5 голосов
/ 01 февраля 2012

У нас есть (массивное) устаревшее приложение WinForms, которое через пункт меню открывает форму WPF.Эта форма WPF будет содержать сетку инфраструктуры и некоторые кнопки / раскрывающиеся списки.

Эта одиночная форма WPF представляет собой зарождающуюся стадию перехода к WPF.Позднее все больше компонентов приложения будут перенесены в WPF, а в конечном итоге и само приложение.

В рамках миграции мы хотели бы использовать Caliburn Micro.Следовательно, было бы неплохо, если бы мы могли начать с использования этой единственной формы WPF.

  • Может кто-нибудь дать несколько советов о том, как использовать Caliburn Micro с формой WPF?
  • Или, может быть, скажите, почему использование Caliburn Micro пока не имеет смысла?

Документация, которую я до сих пор читал, включает загрузочные стрипперы, которые обеспечивают запуск приложения сЖелаемая модель корневого представления, а не сценарий выше.

Большое спасибо!

Ответы [ 3 ]

6 голосов
/ 03 февраля 2012

После долгих поисков и поиска исходного кода 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);
    
  • Приведенный выше загрузчик - это то, что вам придется написать.

  • Для получения дополнительной информации обо всем, что нужно сделать, см. Настройка загрузчика из Caliburn Micro документация .
  • Для целейэтого поста, сделайте его производным от класса 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;
    }
    
  • Последнее, что нужно отметитьявляется то, что вам нужно установить свойство зависимостей cal: Bind.Model в XAML WpfControlView.

    
    cal:Bind.Model="WpfControls.ViewModels.WpfControl1ViewModel"
    
  • Значение свойства зависимостей используется в качестве строки дляBootstrapper.GetInstance (Тип serviceType, строковый ключ), который должен затем использоватьЧтобы разрешить WpfControlViewModel.

  • Поскольку используемый мной контейнер (Autofac) не поддерживает разрешение только для строк, я решил установить для свойства полное имя модели представления.Затем это имя можно преобразовать в тип и использовать для разрешения из контейнера.
2 голосов
/ 05 декабря 2013

Следуя принятому ответу (хороший!), Я хотел бы показать вам, как реализовать загрузчик WinForms в подходе ViewModel First таким образом:

  1. Вы выигралине нужно создавать окно WPF и,
  2. Вам не нужно будет привязываться напрямую к ViewModel из View.

Для этого нам нужно создать нашу собственную версиюWindowManager, убедитесь, что мы не вызываем метод Show в Window (если это применимо к вашему случаю), и разрешаем привязку происходить.

Вот полный код:

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                
        }
    }
}

Надеюсь, это поможет кому-то с такими же потребностями.Это точно спасло меня.

1 голос
/ 02 февраля 2012

Вот кое-что, что вы можете начать с

  • Создание ViewModels и наследование их от класса PropertyChangedBase, предоставляемого каркасом CM.
  • При необходимости используйте имплементацию EventAggregator для слабосвязанной связи \ интеграции
  • Реализация AppBootStrapper без универсальной реализации, которая определяет модель корневого представления.

Теперь вы можете использовать первый подход представления и привязать представление к модели, используя присоединенное свойство Bind.Model вПосмотреть.Я создал пример приложения для описания подхода здесь .

...