Вопрос:

Как вызывать Application.Run () для главного докладчика приложения MVP WinForms?

c# winforms mvp

2667 просмотра

4 ответа

2921 Репутация автора

Я учусь применять MVP к простому приложению WinForms (только одна форма) в C # и столкнулся с проблемой при создании основного докладчика в static void Main(). Является ли хорошей идеей предоставить представление от докладчика, чтобы предоставить его в качестве параметра для Application.Run ()?

В настоящее время я реализовал подход, который позволяет мне не предоставлять представление как свойство Presenter:

    static void Main()
    {
        IView view = new View();
        Model model = new Model();
        Presenter presenter = new Presenter(view, model);
        presenter.Start();
        Application.Run();
    }

Методы запуска и остановки в Presenter:

    public void Start()
    {
        view.Start();
    }

    public void Stop()
    {
        view.Stop();
    }

Методы запуска и остановки в представлении (форма Windows):

    public void Start()
    {
        this.Show();
    }

    public void Stop()
    {
        // only way to close a message loop called 
        // via Application.Run(); without a Form parameter
        Application.Exit();
    }

Вызов Application.Exit () кажется неэффективным способом закрытия формы (и приложения). Другой альтернативой может быть представление View как открытого свойства Presenter для вызова Application.Run () с параметром Form.

    static void Main()
    {
        IView view = new View();
        Model model = new Model();
        Presenter presenter = new Presenter(view, model);
        Application.Run(presenter.View);
    }

Методы запуска и остановки в Presenter остаются без изменений. Дополнительное свойство добавлено, чтобы вернуть представление как форму:

    public void Start()
    {
        view.Start();
    }

    public void Stop()
    {
        view.Stop();
    }

    // New property to return view as a Form for Application.Run(Form form);
    public System.Windows.Form View
    {
        get { return view as Form(); }
    }

Методы «Пуск» и «Стоп» в представлении (форма Windows) будут записаны следующим образом:

    public void Start()
    {
        this.Show();
    }

    public void Stop()
    {
        this.Close();
    }

Кто-нибудь может предложить, какой подход лучше и почему? Или есть еще лучшие способы решить эту проблему?

Автор: anonymous Источник Размещён: 03.05.2010 10:40

Ответы (4)


5 плюса

12921 Репутация автора

Я бы пошел на второй подход. Вы также можете избавиться от дополнительного свойства, просто приведя view к форме в пустом Main, так как вы знаете, что это форма в любом случае в этот момент (я не вижу причин делать ее более общей, так как она просто запускает приложение winform). )

Application.Run(view as Form);
Автор: Roger Johansson Размещён: 05.05.2010 10:49

9 плюса

125466 Репутация автора

Решение

Как насчет следующего:

// view
public void StartApplication() // implements IView.StartApplication
{ 
    Application.Run((Form)this);
}

// presenter
public void StartApplication()
{
    view.StartApplication();
}

// main
static void Main()     
{     
    IView view = new View();     
    Model model = new Model();     
    Presenter presenter = new Presenter(view, model);     
    presenter.StartApplication();     
}     

Таким образом, вам не нужно выставлять вид снаружи. Кроме того, представление и докладчик знают, что это представление было запущено как «основная форма», которая может быть полезной информацией.

Автор: Heinzi Размещён: 05.05.2010 11:01

1 плюс

19587 Репутация автора

Все становится немного сложнее, если вы разрешаете более одного выхода из приложения (например, пункт меню для выхода) или если вы препятствуете закрытию приложения при определенных условиях. В любом случае фактический вызов закрытия приложения обычно должен вызываться из кода презентатора, а не путем простого закрытия конкретного представления. Этого можно достичь, используя перегрузки Application.Run () или Application.Run (ApplicationContext) и выставляя действие выхода из приложения через инверсию управления.

Точный подход к регистрации и использованию действия выхода из приложения будет зависеть от используемого вами механизма IoC (например, указатель службы и / или внедрение зависимости). Поскольку вы еще не упомянули, каким может быть ваш текущий подход к IoC, вот пример, который не зависит от каких-либо конкретных структур IoC:

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        ApplicationActions.ExitApplication = Application.Exit;

        MainPresenter mainPresenter = new MainPresenter(new MainView(), new Model());
        mainPresenter.Start();

        Application.Run(); 
    }
}

public static class ApplicationActions
{
    public static Action ExitApplication { get; internal set; }
}

public class MainPresenter : Presenter
{
    //...

    public override void Stop()
    {
        base.Stop();

        ApplicationActions.ExitApplication();
    }
}

Этот базовый подход может быть легко адаптирован к вашему предпочтительному подходу IoC. Например, если вы используете локатор службы, вы, вероятно, захотите удалить хотя бы установщик в свойстве ApplicationActions.ExitApplication и вместо этого сохранить делегат в локаторе службы. Если бы получатель ExitApplication оставался, он предоставил бы простой фасад для извлечения экземпляра локатора службы. например:

public static Action ExitApplication
{
    get
    {
        return ServiceLocator.GetInstance<Action>("ExitApplication");
    }
}
Автор: Nicole Calinoiu Размещён: 06.05.2010 06:25

0 плюса

45026 Репутация автора

Вы можете сделать это сотнями способов, чтобы достичь конечной цели разделения проблем. Здесь нет жестких и быстрых правил, основная идея заключается в том, что докладчик имеет дело с логикой представления представления, в то время как представление имеет только тупое знание о своих собственных классах и материалах, специфичных для GUI. Некоторые способы, которые я могу придумать (в широком смысле):

1) Посмотрите, что происходит, и пусть решает его ведущий. Вы начинаете как,new View().Start();

// your reusable MVP framework project 
public interface IPresenter<V>
{
    V View { get; set; }
}
public interface IView<P>
{
    P Presenter { get; }
}
public static class PresenterFactory
{
    public static P Presenter<P>(this IView<P> view) where P : new()
    {
        var p = new P();
        (p as dynamic).View = view;
        return p;
    }
}

// your presentation project
public interface IEmployeeView : IView<EmployeePresenter>
{
    void OnSave(); // some view method
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
    public IEmployeeView View { get; set; } // enforced

    public void Save()
    {
        var employee = new EmployeeModel
        {
            Name = View.Bla // some UI element property on IEmployeeView interface
        };
        employee.Save();
    }
}

// your view project
class EmployeeView : IEmployeeView
{
    public EmployeePresenter Presenter { get; } // enforced

    public EmployeeView()
    {
        Presenter = this.Presenter(); // type inference magic
    }

    public void OnSave()
    {
        Presenter.Save();
    }
}

Вариант вышеупомянутого подхода будет заключаться в усилении общих ограничений для представления и докладчика, но я не думаю, что сложность перевешивает преимущества. Что-то вроде этого:

// your reusable MVP framework project 
public interface IPresenter<P, V> where P : IPresenter<P, V> where V : IView<P, V>
{
    V View { get; set; }
}
public interface IView<P, V> where P : IPresenter<P, V> where V : IView<P, V>
{
    P Presenter { get; }
}
public static class PresenterFactory
{
    public static P Presenter<P, V>(this IView<P, V> view)
        where P : IPresenter<P, V>, new() where V : IView<P, V>
    {
        return new P { View = (V)view };
    }
}

// your presentation project
public interface IEmployeeView : IView<EmployeePresenter, IEmployeeView>
{
    //...
}
public class EmployeePresenter : IPresenter<EmployeePresenter, IEmployeeView>
{
    //...
}

Недостатки

  • взаимодействие между формами менее интуитивно для меня.

Шаги вовлечены:

  • воплощать в жизнь IEmployeeView
  • создать экземпляр Presenter, вызвав PresenterFactoryи передав thisиз конструктора представления
  • убедитесь, что события представления связаны с соответствующими методами презентатора
  • начать, как new EmployeeView()....

2) Ведущий запускает вещи и позволяет ему решать их мнение. Вы начинаете как,new Presenter().Start();

В этом подходе презентатор создает свой собственный вид (например, подход 1) с помощью некоторой инъекции зависимостей или около того, или представление может быть передано в конструктор презентатора. Например

// your reusable MVP framework project 
public abstract class IPresenter<V> // OK may be a better name here
{
    protected V View { get; }

    protected IPresenter()
    {
        View = ...; // dependenchy injection or some basic reflection, or pass in view to ctor
        (View as dynamic).Presenter = this;
    }
}
public interface IView<P>
{
    P Presenter { get; set; }
}

// your presentation project
public interface IEmployeeView : IView<EmployeePresenter>
{
    void OnSave(); // some view method
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
    public void Save()
    {
        var employee = new EmployeeModel
        {
            Name = View.Bla // some UI element property on IEmployeedView interface
        };
        employee.Save();
    }
}

// your view project
class EmployeeView : IEmployeeView
{
    public EmployeePresenter Presenter { get; set; } // enforced

    public void OnSave()
    {
        Presenter.Save();
    }
}

Шаги вовлечены:

  • воплощать в жизнь IEmployeeView
  • убедитесь, что события представления связаны с соответствующими методами презентатора
  • начать, как new EmployeePresenter(....

3) Событие, стиль наблюдателя

Здесь вы можете либо инкапсулировать представление в представителе (создание экземпляра в представлении), например, подход 1, либо включить представление в презентатор (создание представления в презентаторе), как подход 2, но, по моему опыту, последний всегда будет более чистым проектом для работы. Пример последнего:

// your reusable MVP framework project
public abstract class IPresenter<V> where V : IView
{
    protected V View { get; }

    protected IPresenter()
    {
        View = ...; // dependenchy injection or some basic reflection, or pass in view to ctor
        WireEvents();
    }

    protected abstract void WireEvents();
}

// your presentation project
public interface IEmployeeView : IView
{
    // events helps in observing
    event Action OnSave; // for e.g.
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
    protected override void WireEvents()
    {
        View.OnSave += OnSave;
    }

    void OnSave()
    {
        var employee = new EmployeeModel
        {
            Name = View.Bla // some UI element property on IEmployeedView interface
        };
        employee.Save();
    }
}

// your view project
class EmployeeView : IEmployeeView
{
    public event Action OnSave;
    void OnClicked(object sender, EventArgs e) // some event handler
    {
        OnSave();
    }
}
// you kick off like new EmployeePresenter()....

Недостаток:

  • Вы должны связать события как со стороны просмотра, так и со стороны докладчика - удвоить работу

Шаги вовлечены:

  • воплощать в жизнь IEmployeeView
  • убедитесь, что события iview вызываются из методов обработчика событий представления
  • убедитесь, что участники события iview инициализированы от докладчика
  • начать, как new EmployeePresenter()....

Ограничения языка иногда делают шаблоны проектирования более сложными. Например, если бы множественное наследование было возможно в C #, это был только вопрос наличия абстрактного базового класса представления со всеми деталями реализации, кроме специфических компонентов пользовательского интерфейса, которые затем могли быть реализованы классом представления. Нет ведущих, классический полиморфизм и просто мертвецы! К сожалению, это невозможно, поскольку большинство классов представлений в .NET (например, FormWinForms) уже наследуются от классов суперпредставлений. Поэтому мы должны реализовать интерфейс и перейти к композиции. Кроме того, C # не позволяет иметь закрытых членов в реализации интерфейса, поэтому мы вынуждены сделать все члены, указанные как IEmployeeViewpublic, что нарушает естественные правила инкапсуляции класса представления (т. Е. Другие представления в проекте представления могут видеть деталиEmployeeViewне имеет отношения к ним). В любом случае, используя мощные методы расширения C #, можно использовать гораздо более простой, но очень ограниченный подход.

4) метод расширения метода

Это просто глупо.

// your presentation project
public interface IEmployeeView
{
    void OnSave(); // some view method
}
public static class EmployeePresenter // OK may need a better name
{
    public void Save(this IEmployeeView view)
    {
        var employee = new EmployeeModel
        {
            Name = view.Bla // some UI element property on IEmployeedView interface
        };
        employee.Save();
    }
}

// your view project
class EmployeeView : IEmployeeView
{       
    public void OnSave()
    {
        this.Save(); // that's it. power of extensions.
    }
}

Недостатки:

  • довольно непригодный для чего-либо отдаленно сложного

Шаги вовлечены:

  • воплощать в жизнь IEmployeeView
  • убедитесь, что this....метод расширения вызывается из событий просмотра
  • начать все, позвонив в новый View ...

Из всех 2 и 3 выглядят лучше для меня.

Автор: nawfal Размещён: 13.06.2019 06:23
Вопросы из категории :
32x32