Организация кода пользовательского интерфейса в формах .NET - PullRequest
11 голосов
/ 16 июня 2010

Я - тот, кто научил себя программированию и не имел никакого официального обучения программированию на .NET.

Некоторое время назад я запустил C # для разработки программы с графическим интерфейсом для управления датчиками, и проект расцвел. Мне просто было интересно, как лучше организовать код, особенно код пользовательского интерфейса, в моих формах.

Мои формы в настоящее время являются беспорядком, или, по крайней мере, кажутся мне беспорядком.

  • У меня есть конструктор, который инициализирует все параметры и создает события.
  • У меня есть гигантское свойство State, которое обновляет состояние Enable всех моих элементов управления формой, когда пользователи проходят через приложение (т. Е. Отключено, подключено, настроено, сканирует), контролируемое перечислением States.
  • У меня есть 3-10 закрытых переменных, доступ к которым осуществляется через свойства, некоторые из которых имеют побочные эффекты при изменении значений элементов формы.
  • У меня есть много функций "UpdateXXX" для обработки элементов пользовательского интерфейса, которые зависят от других элементов пользовательского интерфейса, то есть: если датчик изменяется, то измените раскрывающийся список скорости передачи. Они разделены на регионы
  • У меня много событий, вызывающих эти функции обновления
  • У меня есть фоновый работник, который выполняет все сканирование и анализ.

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

Как вы структурируете свои формы .net?

Спасибо

Ответы [ 8 ]

5 голосов
/ 16 июня 2010

Существует ряд шаблонов, которые помогают вам отделять логику в приложениях, что приводит к получению более чистого и более удобного кода. Шаблон MVP хорош для начала. Он основан на определении 3 областей ответственности, т. Е. MVP M = модель, V = вид, P = презентатор. Если вы знакомы с использованием интерфейсов, у вас все будет хорошо, иначе это было бы хорошим началом (пересмотрите основные принципы ОО: инкапсуляция, абстракция, полиморфизм). Основной принцип MVP состоит в том, чтобы поместить логику приложения в Presenter. Presnter общается с представлением (вашей формой) через интерфейс, и представление перезванивает докладчику (я тоже использую интерфейс для этого), когда пользователь взаимодействует с ним. Модель - это иерархия объектов домена решения, которая реализует бизнес-логику и отношения сущностей.

Большинство шаблонов пользовательского интерфейса (MVP, MCV и т. Д.) Пытаются делать то же самое, разделяя ваши интересы. Ниже приведен простой пример:

// Интерфейс просмотра

interface IUserDetailsView
{

      string Username{set;get;}
      string FirstName{get;set;}
      string LastName{get;set;}
      UserDetailsPresenter Presenter{get;set;}
      void DisplayMessage(string message);


}

// Имплементация вида // Стандартная форма окна с текстовыми полями, надписями, комбо и т. Д., Которые

class UserDetailsView : Form, IUserDetails
{

      public string Username{set{txtUserName.text = value;}get{return txtUserName.text;}}
      public string FirstName{set{txtFirstName.text = value;}get{return txtFirstName.text;}}
      public string LastName{set{txtLastName.text = value;}get{return txtLastName.text;}}

      Public UserDetailsPresenter Presenter{get;set;}

      public void DisplayMaessage(string message)
      {
         MessageBox.Show(message);
      }

      private void saveButton_Click(object sender, EventArgs e)
      {
         Presenter.SaveUserDetails();

      }
}

// Логика презентации

Класс Presenter UserDetailsPresenter {

  //Constructor
  public userDetailsPresenter(IUserDetailsView view)
  {
    //Hold a reference to the view interface and set the view's presnter
     _view = view;
     _view.Presenter = this;
  }

  private IUserDetailsView _view;

  DisplayUser(string userName)
  {
     //Get the user from some service ...
     UserDetails details = service.GetUser(userName);

     //Display the data vioa the interface
     _view.UserName = details.UserName;
     _view.FirstName = details.FirstName;
     _view.LastName = details.LastName;

  }

  public void SaveUserDetails()
  {

       //Get the user dryaiols from the view (i.e. the screen
       UserDetails details = new UserDetails();

       details.UserName = _view.UserName;
       details.FirstName = _view.FirstName;
       details.LastName = _view.LastName;

       //Apply some business logic here (via the model)
       if(!details.IsValidUserDetails())
       {
          _view.DisplayMessage("Some detail outlining the issues");
         return;
       }

       //Call out to some service to save the data
       service.UpdateUser(details);

  }

}

// Наконец, модель

public class UserDetails
{

   public UserName {get;set;}
   public FirstName{get;set;}
   public LastName{get;set;}

   public bool IsValidUserDetails()
   {
       if(LastName == "Smith")
       {
          //We do not allow smiths, remember what happened last time ... or whatever
          return false;
       }

       return true;
   }

}

Надеюсь, это объясняет, как разделена ответственность. Форма не имеет никакой логики, кроме отображения / форматирования и т. Д., Ее также можно отключить для тестирования. Презентатор является посредником между представлением и моделью и выполняет вызовы к услугам, модель влияет на вашу бизнес-логику. Как уже предлагалось, в этом шаблоне есть вариации, которые могут сделать ваш код немного более тонким и гибким, но в нем изложены основные принципы. Надеюсь, это поможет.

: -)

2 голосов
/ 16 июня 2010

Со сложными формами я обычно делю код на отдельные файлы.Вы можете сделать это с помощью «частичного класса».Каждый файл исходного кода назван в зависимости от формы.Например, MainForm.cs, MainForm.State.cs, MainForm.Update.cs, MainForm.Menu.cs и так далее.Если у меня много сложных форм, я создам подпапку для каждой.Один совет здесь, чтобы создать форму MainForm.Wip.cs.Эта частичная форма класса - это код, над которым вы сейчас работаете.Когда вы закончите работу с этим кодом, вы можете либо переименовать его, либо переместить код в другие файлы исходного кода.

Кроме того, я также создам пользовательские элементы управления.Это дает преимущество повторного использования кода и выводит большую часть функциональности из формы.Ознакомьтесь с разделом «Разработка пользовательских элементов управления Windows Forms с помощью .NET Framework» по адресу http://msdn.microsoft.com/en-us/library/6hws6h2t.aspx.

Проверьте Никого не волнует, как выглядит ваш код при http://www.codinghorror.com/blog/2007/12/nobody-cares-what-your-code-looks-like.html. Что-топодумать, прежде чем "организовать".

1 голос
/ 16 июня 2010

Несколько быстрых предложений:

Постарайтесь убрать весь код, не относящийся к пользовательскому интерфейсу, из форм, вам нужно только иметь GUI-код в реальной форме, если это возможно. Если свойство имеет побочный эффект, оно, вероятно, должно быть функцией. Ваше свойство State почти наверняка должно быть методом и посмотреть, сможете ли вы разбить его код на отдельные методы, чтобы это был только один вызов функции на состояние.

1 голос
/ 16 июня 2010

Взгляните на паттерн Model-View-Presenter: http://en.wikipedia.org/wiki/Model_View_Presenter

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

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

Удачи:)

0 голосов
/ 30 сентября 2010

Я использую регионы, как это:

#Region "_Edit"
    Private Sub _edit_VisibleChanged(...) Handles _edit.VisibleChanged
    End Sub
#End Region

сверху вниз, мой код формы имеет:

  • частные объявления
  • свойства друзей
  • Friend Subs
  • Личные свойства
  • Личные Sub *
  • События
  • Обработчики событий для частных форм / классов

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

0 голосов
/ 16 июня 2010

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

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

Вы даже можете создавать пользовательские элементы управления, которые не отображаются в форме, как таймер.

0 голосов
/ 16 июня 2010

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

Во-вторых, прочитайте о некоторых шаблонах проектирования и принципах. Вы можете найти несколько замечательных примеров здесь , в вашем случае я бы проверил поведенческие паттерны, в частности паттерн State и Mediator. Они не серебряные пули для решения ваших проблем, но они должны дать вам лучшее представление о том, как разделить ваше приложение и логику пользовательского интерфейса.

0 голосов
/ 16 июня 2010

Вот ссылка на шаблон архитектуры, используемый довольно часто.

http://en.wikipedia.org/wiki/Model_View_ViewModel

Я бы посмотрел и другие архитектурные паттерны, а также изучил бы этот, просмотрел пример кода и т. Д.

...