Есть форма Form1
и одна единственная кнопка button1
private void button1_Click(object sender, EventArgs e)
{
//do something..
}
Задумался над тем как правильно обрабатывать событие Click
, в том плане, что не просто писать кучу кода вместо do something
, а сделать это по крайней мере в стиле ООП. Вопрос 1. Подскажите пожалуйста, как решают этот вопрос при разработке коммерческих программ?
Сейчас я делаю примерно так
//Файл Form1.cs
private Department department = new Department();
private void button1_Click(object sender, EventArgs e)
{
department.Add += (obj, ev) =>
{
Task task = Task.Factory.StartNew(() =>
{
//do something..
});
};
department.Adding(Employees.Engineer);
}
//Файл Department.cs
class Department
{
public delegate void DepartmentHandler(object sender, DepartmentEventArgs e);
public event DepartmentHandler Add;
public void Adding(Employees employee)
{
Add?.Invoke(this, new DepartmentEventArgs(employee));
}
}
class DepartmentEventArgs: EventArgs
{
public readonly Employees _employee;
public DepartmentEventArgs(Employees employee)
{
_employee = employee;
}
}
Я думаю суть понятна. Вопрос 2. Подскажите пожалуйста, правильно ли я делаю, написав код таким образом?
Всем большое спасибо за ответы!
В этом видео дается самый простой пример MVP, без привязок и с простейшей переброской событий от кнопок в Presenter
. Если вы начнете писать что-то более серьезное, то быстро осознаете необходимость работы с привязками, потому стоит внимательно ознакомиться с классом BindingSource.
По существу заданного вопроса хочу дать несколько примеров, как можно события обернуть в свойства через классы посредников.
1) Пример на простейшее событие выбора в ComboBox
Создадим такой класс, который потом будет использовать для создания свойства в форме
public class SimpleEventHandler
{
private Action _handlerExecutor;
//ctor
public SimpleEventHandler(Action handlerExecutor)
{
_handlerExecutor = handlerExecutor ??
throw new ArgumentNullException(nameof(handlerExecutor));
Handler = new EventHandler(OnEvent);
}
/// <summary>
/// Ссылка на метод,
/// который буден вызван при возникновении события у целевого контрола
/// </summary>
public EventHandler Handler { get; private set; }
/// <summary>
/// Вызов по событию исполняемого метода
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnEvent(object sender, EventArgs e)
{
_handlerExecutor();
}
}
Тогда вьюшка будет такой
public partial class MainView : Form, IMainView
{
private BindingSource _bsGroups = new BindingSource();
public MainView()
{
InitializeComponent();
SetBindings();
this.CenterToScreen();
this.Text = "SimpleEvent";
}
private void SetBindings()
{
//ComboBox
_bsGroups.DataSource = typeof(BindingList<Group>);
_comboBox.DisplayMember = nameof(Group.Number);
_comboBox.DataSource = _bsGroups;
}
/// <summary>
/// Список групп для комбобокса
/// </summary>
public BindingList<Group> Groups
{
get => _bsGroups.List as BindingList<Group>;
set => _bsGroups.DataSource = value;
}
/// <summary>
/// Выбранная группа
/// </summary>
public Group SelectedGroup => _bsGroups.Current as Group;
/// <summary>
/// Событие выбора в комбобоксе группа
/// </summary>
public SimpleEventHandler SelectedGroupChanged
{
set
{
_bsGroups.CurrentChanged += value.Handler;
}
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
}
А презентер такой
public class MainPresenter
{
private readonly IMainView _mainView;
//ctor
public MainPresenter(IMainView mainView)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
LoadGroups();
//событие выбора в комбобоксе
_mainView.SelectedGroupChanged = new SimpleEventHandler(OnSelectedGroup);
}
/// <summary>
/// Событие выбора в комбобоксе
/// </summary>
private void OnSelectedGroup()
{
string message = $"Вы выбрали: {_mainView.SelectedGroup.Number}";
_mainView.ShowMessage(message);
}
/// <summary>
/// Загрузка данных для комбобокса
/// </summary>
private async void LoadGroups()
{
var groups = await Program.Repository.GetAllGroups();
_mainView.Groups = new BindingList<Group>(groups.ToList());
}
}
2) Событые имеет значемые для презентера параметры.
Создадим такой класс посредник
public class ParameterizedEventHandler
{
private Action<Dictionary<string, object>> _handlerExecutor;
//ctor
public ParameterizedEventHandler(Action<Dictionary<string, object>> handlerExecutor)
{
_handlerExecutor = handlerExecutor ??
throw new ArgumentNullException(nameof(handlerExecutor));
}
public void RaiseEvent(Dictionary<string, object> parameters)
{
_handlerExecutor(parameters);
}
}
Параметры в презентер будем передовать в словаре вот так
public partial class MainView : Form, IMainView
{
public MainView()
{
InitializeComponent();
this.CenterToScreen();
this.Text = nameof(ParamEvent);
//событие клика мыши по картинке
_pictureBox.MouseClick += _pictureBox_MouseClick;
}
/// <summary>
/// Обработка события нажатия кнопки мыши по картинке
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void _pictureBox_MouseClick(object sender, MouseEventArgs e)
{
//собираем нужную инфо
string mouseButton = e.Button.ToString();
int x = e.X;
int y = e.Y;
//формируем параметры
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add(nameof(mouseButton), mouseButton);
parameters.Add(nameof(x), x);
parameters.Add(nameof(y), y);
//вызываем событие и передаем эти параметры
MouseClickEvent.RaiseEvent(parameters);
}
private ParameterizedEventHandler _MouseClickEvent;
public ParameterizedEventHandler MouseClickEvent
{
get => _MouseClickEvent;
set
{
if (_MouseClickEvent != null) return;
_MouseClickEvent = value;
}
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
}
Презентер такой
public class MainPresenter
{
private readonly IMainView _mainView;
//ctor
public MainPresenter(IMainView mainView)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
//
_mainView.MouseClickEvent = new ParameterizedEventHandler(OnMouseClick);
}
private void OnMouseClick(Dictionary<string, object> parameters)
{
string message = $"Была нажата кнопка мыши: {parameters["mouseButton"]}"
+ Environment.NewLine
+ $"Координаты курсора мыши: X={parameters["x"]}, Y={parameters["y"]}";
_mainView.ShowMessage(message);
}
}
3) Пример на кнопки с привязкой к свойствам Enabled
и Text
Для кнопки слева сделана привязка к свойству Enabled
с помощью такого класса
public class SimpleButtonEventHandler : SimpleEventHandler, INotifyPropertyChanged
{
private Func<bool> _setterEnableProperty;
//ctor
public SimpleButtonEventHandler(Action handlerExecutor) : this(handlerExecutor, null)
{
}
public SimpleButtonEventHandler(Action handlerExecutor, Func<bool> setterEnableProperty) : base(handlerExecutor)
{
if (setterEnableProperty == null)
{
_setterEnableProperty = () => true;
}
else
{
_setterEnableProperty = setterEnableProperty;
}
}
/// <summary>
/// Доступность кнопки для нажатия
/// </summary>
private bool _Enabled = true;
public bool Enabled
{
get => _Enabled;
private set
{
_Enabled = value;
OnPropertyChanged();
}
}
/// <summary>
/// Инициализация проверки доступности кнопки
/// </summary>
public void CheckEnabled()
{
Enabled = _setterEnableProperty();
}
//INPC
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Для кнопки справа используется привязка к свойствам Enabled
и Text
public class TextButtonEventHandler : SimpleButtonEventHandler
{
//ctor
public TextButtonEventHandler(Action handlerExecutor) : base(handlerExecutor)
{ }
public TextButtonEventHandler(Action handlerExecutor, Func<bool> setterEnableProperty) : base(handlerExecutor, setterEnableProperty)
{ }
/// <summary>
/// Надпись на кнопке
/// </summary>
private string _Text;
public string Text
{
get => _Text;
set
{
_Text = value;
OnPropertyChanged();
}
}
}
Вьюшка такая
public partial class MainView : Form, IMainView
{
private BindingSource _bsButtonClick = new BindingSource();
private BindingSource _bsButtonText = new BindingSource();
public MainView()
{
InitializeComponent();
SetBindings();
this.CenterToScreen();
this.Text = nameof(ButtonEvents);
}
/// <summary>
/// Устанавливаем привязки
/// </summary>
private void SetBindings()
{
// Кнопка слева
_bsButtonClick.DataSource = typeof(SimpleButtonEventHandler);
_buttonClickOk.DataBindings.Add("Enabled", _bsButtonClick, "Enabled");
// Кнопка справа
_bsButtonText.DataSource = typeof(TextButtonEventHandler);
_buttonTextOk.DataBindings.Add("Enabled", _bsButtonText, "Enabled");
_buttonTextOk.DataBindings.Add("Text", _bsButtonText, "Text");
}
/// <summary>
/// Кнопка слева
/// </summary>
public SimpleButtonEventHandler ClickOK
{
get => _bsButtonClick.Current as SimpleButtonEventHandler;
set
{
if (_bsButtonClick.Count > 0) return;
_bsButtonClick.Add(value);
_buttonClickOk.Click += value.Handler;
}
}
/// <summary>
/// Лейбл отсчета кликов
/// </summary>
public string ClickOutput
{
get => _labelCount.Text;
set => _labelCount.Text = value;
}
/// <summary>
/// Кнопка справа
/// </summary>
public TextButtonEventHandler TextOK
{
get => _bsButtonText.Current as TextButtonEventHandler;
set
{
if (_bsButtonText.Count > 0) return;
_bsButtonText.Add(value);
_buttonTextOk.Click += value.Handler;
}
}
}
Презентер к ней
public class MainPresenter
{
private readonly IMainView _mainView;
private int _clickCount = 5;
private int _clickCountText = 5;
//ctor
public MainPresenter(IMainView mainView)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
//Кнопка слева
_mainView.ClickOK = new SimpleButtonEventHandler(OnClick, CanClick);
_mainView.ClickOutput = _clickCount.ToString();//вывод в лейбл
//Кнопка справа
_mainView.TextOK = new TextButtonEventHandler(OnText, CanText);
_mainView.TextOK.Text = "OK";
}
/// <summary>
/// Кнопка слева Доступность
/// </summary>
/// <returns></returns>
private bool CanClick()
{
return _clickCount > 0;
}
/// <summary>
/// Кнопка слева обработка события клик
/// </summary>
private void OnClick()
{
--_clickCount;
_mainView.ClickOutput = _clickCount.ToString();
if (_clickCount == 0)
{
//проверить доступность кнопки, через вызов CanClick()
_mainView.ClickOK.CheckEnabled();
}
}
/// <summary>
/// Доступность кнопки справа
/// </summary>
/// <returns></returns>
private bool CanText()
{
return _clickCountText > 0;
}
/// <summary>
/// Кнопка справа обработчик
/// </summary>
private void OnText()
{
--_clickCountText;
//выводим на кнопку величину
_mainView.TextOK.Text = _clickCountText.ToString();
if (_clickCountText == 0)
{
_mainView.TextOK.Text = "Всё";
_mainView.TextOK.CheckEnabled();
}
}
}
4) Кнопка на ToolStrip
Имеет такую особенность, что у нее изначально нет поддержки привязок. Поэтому нам понадобится такой класс унаследованный класс кнопки
public class ToolStripBindableButton : ToolStripButton, IBindableComponent
{
private ControlBindingsCollection _DataBindings;
public ControlBindingsCollection DataBindings
{
get => _DataBindings = _DataBindings ?? new ControlBindingsCollection(this);
}
private BindingContext _BindingContext;
public BindingContext BindingContext
{
get => _BindingContext = _BindingContext ?? new BindingContext();
set => _BindingContext = value;
}
}
После добавления в проект такого класса и компиляции эту кнопку можно будет добавить к тулбар и работать с ней таким образом
public partial class MainView : Form, IMainView
{
private BindingSource _bsTool = new BindingSource();
public MainView()
{
InitializeComponent();
//привязка кнопки на тулбаре
_bsTool.DataSource = typeof(SimpleButtonEventHandler);
_toolStripBindableButton.DataBindings.Add("Enabled", _bsTool, "Enabled");
this.CenterToScreen();
this.Text = nameof(ToolStripEvent);
}
//кнопка
public SimpleButtonEventHandler ToolButton
{
get => _bsTool.Current as SimpleButtonEventHandler;
set
{
if (_bsTool.Count > 0) return;
_bsTool.Add(value);
_toolStripBindableButton.Click += value.Handler;
}
}
//текстбокс событие изменения
private SimpleEventHandler _InputTextChanged;
public SimpleEventHandler InputTextChanged
{
get => _InputTextChanged;
set
{
if (_InputTextChanged != null) return;
_InputTextChanged = value;
_textBox.TextChanged += value.Handler;
}
}
//текстбокс содержимое
public string InputText
{
get => _textBox.Text;
set => _textBox.Text = value;
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
}
И презентер
public class MainPresenter
{
private readonly IMainView _mainView;
//ctor
public MainPresenter(IMainView mainView)
{
_mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
//кнопка на тулбаре
_mainView.ToolButton = new SimpleButtonEventHandler(OnTool, CanTool);
//изменение в текстбоксе
_mainView.InputTextChanged = new SimpleEventHandler(OnText);
//заставляем выкл. кнопке на тулбаре
_mainView.ToolButton.CheckEnabled();
}
/// <summary>
/// Кнопка на тулбаре
/// </summary>
/// <returns></returns>
private bool CanTool()
{
return !String.IsNullOrEmpty(_mainView.InputText);
}
private void OnTool()
{
var message = $"Вы ввели: {_mainView.InputText}";
_mainView.ShowMessage(message);
}
/// <summary>
/// Событие изменения текста
/// </summary>
private void OnText()
{
//заставляем вкл. или выкл. кнопке на тулбаре
_mainView.ToolButton.CheckEnabled();
}
}
Вот, пожалуй, основные примеры на обработку всевозможных событий. Ответ и так получился слишком объемным, так что пример на работу с несколькими формами вы сможете посмотреть сами скачав решение со всеми этими примерами здесь.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Как можно сделать вот такой запрос к таблице: "/responsephp?que=есть"? У меня работает только с цифрами
Я сделал сайт с мобильной версиейПереход с основного сайта на мобильный субдомен осуществляется через PHP код