Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab3.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
3.22 Mб
Скачать

Реализация форм редактирования данных о точках

Приступим к реализации функционала форм. Начнём с более лёгкой – с формы редактирования данных точки. Откроем код класса формы.

/// <summary>

/// Форма редактирования данных точки ломаной.

/// </summary>

public partial class EditPointDataForm : Form

Добавим туда переменную, содержащую в себе данные о редактируемой точке.

ZigzagPoint point; // Редактируемая точка.

Реализуем несколько различных конструкторов формы.

#region Конструкторы формы

/// <summary> Конструктор формы. </summary>

public EditPointDataForm()

{

InitializeComponent();

point = new ZigzagPoint(0, 0, 0); // Создаём новую точку, инициализированную нулями.

time_textBox.Text = point.Time.ToString(); // Заполняем поля формы

x_textBox.Text = point.X.ToString();

y_textBox.Text = point.Y.ToString();

DialogResult = DialogResult.Cancel; // Присваиваем результат вызова формы по умолчанию.

}

/// <summary>

/// Конструктор формы.

/// </summary>

/// <param name="time">Время создания точки.</param>

/// <param name="xCord">X координата точки.</param>

/// <param name="yCord">Y координата точки.</param>

public EditPointDataForm(long time, int xCord, int yCord)

{

point = new ZigzagPoint(time, xCord, yCord); // Создаём новую точку с указанными данными.

InitializeComponent();

DialogResult = DialogResult.Cancel; // Присваиваем результат вызова формы по умолчанию.

time_textBox.Text = time.ToString(); // Заполняем поля формы

x_textBox.Text = xCord.ToString();

y_textBox.Text = yCord.ToString();

}

/// <summary>

/// Конструктор формы.

/// </summary>

/// <param name="point">Редактируемая точка.</param>

public EditPointDataForm(ZigzagPoint point)

{

this.point = new ZigzagPoint(point.Time, point.X, point.Y); // Копируем данные точки.

InitializeComponent();

DialogResult = DialogResult.Cancel; // Присваиваем результат вызова формы по умолчанию.

time_textBox.Text = point.Time.ToString(); // Заполняем поля формы

x_textBox.Text = point.X.ToString();

y_textBox.Text = point.Y.ToString();

}

#endregion

Реализуем обработчики нажатия на кнопки OK и Отмена.

#region Обработчики нажатия на кнопки

/// <summary> Обработчик нажатия на кнопку ОК. </summary>

private void ok_button_Click(object sender, EventArgs e)

{

DialogResult = DialogResult.OK;

this.Close();

}

/// <summary> Обработчик нажатия на кнопку Cancel. </summary>

private void cancel_button_Click(object sender, EventArgs e)

{

DialogResult = DialogResult.Cancel;

this.Close();

}

#endregion

По свойству DialogResult можно будет судить о том, какую из кнопок нажал пользователь. Теперь реализуем обработчики событий текстовых полей ввода. Я хотел сделать так, чтобы при нажатии на них выделялся весь их текст, чтобы было легче редактировать, заменять его весь целиком новым вводом. Помимо этого, после того, как пользователь завершает ввод, необходимо проверить его корректность. Если пользователь действительно ввёл корректное число, то меняем данные точки, иначе заменяем содержимое поля ввода на последнее известное корректное.

#region Обработчики событий текстовых полей формы

/// <summary> Обработчик нажатия на поле ввода времени. Выделяет весь его текст. </summary>

private void time_textBox_Click(object sender, EventArgs e)

{

time_textBox.SelectionStart = 0;

time_textBox.SelectionLength = time_textBox.Text.Length;

}

/// <summary> Обработчик нажатия на поле ввода координаты X. Выделяет весь его текст. </summary>

private void x_textBox_Click(object sender, EventArgs e)

{

x_textBox.SelectionStart = 0;

x_textBox.SelectionLength = x_textBox.Text.Length;

}

/// <summary> Обработчик нажатия на поле ввода координаты Y. Выделяет весь его текст. </summary>

private void y_textBox_Click(object sender, EventArgs e)

{

y_textBox.SelectionStart = 0;

y_textBox.SelectionLength = y_textBox.Text.Length;

}

/// <summary> Обработчик потери фокуса полем ввода времени. Проверяет корректность ввода и сохраняет данные в структуру Point. </summary>

private void time_textBox_Leave(object sender, EventArgs e)

{

long result;

try

{

result = Convert.ToInt64(time_textBox.Text);

if (result > 0)

point.Time = result;

}

catch

{

time_textBox.Text = point.Time.ToString();

}

}

/// <summary> Обработчик потери фокуса полем ввода координаты X. Проверяет корректность ввода и сохраняет данные в структуру Point. </summary>

private void x_textBox_Leave(object sender, EventArgs e)

{

int result;

try

{

result = Convert.ToInt32(x_textBox.Text);

point.X = result;

}

catch

{

x_textBox.Text = point.X.ToString();

}

}

/// <summary> Обработчик потери фокуса полем ввода координаты Y. Проверяет корректность ввода и сохраняет данные в структуру Point. </summary>

private void y_textBox_Leave(object sender, EventArgs e)

{

int result;

try

{

result = Convert.ToInt32(y_textBox.Text);

point.Y = result;

}

catch

{

y_textBox.Text = point.Y.ToString();

}

}

#endregion

Далее, реализуем возможность получения отредактированных данных о точке – как по отдельным полям, так и по всем вместе.

#region Публичные свойства формы, значения её полей ввода.

/// <summary> Получает итоговое введённое пользователем время создания точки в мс от запуска сервера. </summary>

public long Time

{

get { return point.Time; }

}

/// <summary> Получает итоговую введённую пользователем координату точки по оси X. </summary>

public int PointX

{

get { return point.X; }

}

/// <summary> Получает итоговую введённую пользователем координату точки по оси Y. </summary>

public int PointY

{

get { return point.Y; }

}

/// <summary> Получает итоговые введённые пользователем данные о точке. </summary>

public ZigzagPoint Point

{

get

{ return point; }

}

#endregion

Эта форма готова. Теперь реализуем форму ZigzagEditorForm.cs. Тут всё будет чуть сложнее.

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

/// <summary> Сервер. </summary>

ZigzagServer.IZigzagControl zigzagControl;

/// <summary> Упорядоченный внутренний список точек. </summary>

List<ZigzagPoint> pointList;

Добавим вспомогательный метод для отображения исключений.

/// <summary>

/// Метод для отображения сообщения об исключении.

/// </summary>

/// <param name="exc">Возникшее исключение.</param>

void ShowExceptionMessage(Exception exc)

{

MessageBox.Show(this, exc.Message + "\r\n" + exc.StackTrace, "Exception occuredin plugin3");

}

Добавим метод, загружающий спиоск точек с сервера в наш внутренний список. Логика работа в нём точно такая же, как и в клиенте. По сути плагин – такой же клиент. Разница лишь в том, что клиенты подключаются по своей «воле», а плагины – по «воле» сервера. Последовательность действий такая же точь в точь: получаем монопольный доступ, получаем список точек, в цикле копируем данные и освобождаем ненужные экземпляры, сохраняем данные, освобождаем монопольный доступ.

/// <summary> Метод, загружающий спиоск точек с сервера во внутренний список. </summary>

private void FillPointsList()

{

pointList = new List<ZigzagPoint>();

ZigzagServer.ILocker locker = (ZigzagServer.ILocker)zigzagControl; // Получаем блокировщик.

locker.Lock(); // Получаем монопольный доступ к данным сервера.

try

{

ZigzagServer.IList list = zigzagControl.TimePointsPairs; // Получаем список пар "время-точка" с сервера

int count = list.Count; // Получаем кол-во точек

for (int i = 0; i < count; i++) // В цикле парсим точки

{

ZigzagServer.IPair pair = (ZigzagServer.IPair)list.GetElementAt(i); // Берём пару по индексу.

long time = (long)pair.Left; // Левый элемент пары - время создания точки в мс от статрта сервера.

ZigzagServer.IPoint p = (ZigzagServer.IPoint)pair.Right; // Правый элемент пары - точка.

pointList.Add(new ZigzagPoint(time, p.X, p.Y)); // Складываем эти данные в наш собственный список.

((IDisposable)p).Dispose(); // Подчищаем за собой. Это необходимо для корректного учёта выданных COM-объектов.

((IDisposable)pair).Dispose();

}

}

catch (Exception exc)

{

ShowExceptionMessage(exc);

}

finally

{

locker.ReleaseLock(); // Освобождаем монопольный доступ к данным сервера.

}

}

Каждый раз, когда мы получаем COM-объект, мы вызываем для него Dispose в тот момент, когда он перестал быть нужен. Это касется объектов, которые хранятся в реестре как LocalServer32, а именно Pair, Point, ZigzagControl. У .NET отчего-то плохо с .exe серверами, потому учёт количества приходится вести вручную. Все остальные объекты, которые InprocServer32 (PluginAction и PluginConnectionInfo), можно не очищать, за ними среда исполнения следит сама.

Реализуем конструктор формы, который копирует объект ZigzagControl и загружает точки с сервера.

public ZigzagEditorForm(ZigzagServer.IZigzagControl zigzagControl)

{

this.zigzagControl = zigzagControl;

InitializeComponent();

FillPointsList(); // Получаем список точек с сервера

}

Напишем метод, который будет заполнять DataGridView данными точек.

/// <summary> Метод обновления данных в таблице точек. </summary>

private void RefreshGridData()

{

points_dataGridView.Rows.Clear();

for (int i = 0; i < pointList.Count; i++)

points_dataGridView.Rows.Add(pointList[i].Time, pointList[i].X, pointList[i].Y);

}

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

#region Методы редактирования списка точек

#endregion

Реализуем метод редактирования i-той точки в списке. Принцип его работы такой: у нас есть список точек с сервера, в них мы находим точку с нужным индексом, вызываем для неё форму редактирования данных точки. Далее, если пользователь нажал ОК, проверяем, что упорядоченность списка точек по времени не нарушилась. Ведь пользователь мог ввести время создания, которое меньше времени создания предыдущей точки. В этом случае можно было бы отреагировать двумя способами – упорядочить список снова или выдать сообщение об ошибке. Я выбрал самый лёгкий путь, хоть и не самый удобный для конечного пользователя – вывожу сообщение с просьбой ввести время, попадающее в интервал между соседними двумя точками. Если время удовлетворяет условиям, то заменяем точку в списке на отредактированную и обновляем данные в таблице. Итак, сам метод:

/// <summary>

/// Метод, который редактирует точку с заданным индексом и обновляет данные о ней в списке точек.

/// </summary>

/// <param name="index">Индекс редактируемой точки во внутреннем списке точек.</param>

private void EditPointAtIndex(int index)

{

ZigzagPoint point = pointList[index]; // Получим данные о точке с заданным индексом.

EditPointDataForm editForm = new EditPointDataForm(point); // Вызываем форму редактирования точки. В конструкторе передадим форме данные редактируемой точки.

if (editForm.ShowDialog() == DialogResult.OK) // Если пользователь нажал кнопку "ОК"

{

ZigzagPoint newValue = editForm.Point;

bool isOk = true;

// Проверим, что новое время момента создания точки попадает во временной интервал между моментами создания соседних двух точек, что упорядоченность точек по времени не нарушилась.

// {t1, t_index, t2} => t1 < ti < t2

isOk = (index == 0) || (newValue.Time > pointList[index - 1].Time); // Проверим, что либо точка первая в списке, либо новое время момента её создания больше времени момента создания предыдущей.

isOk = isOk && ((index == pointList.Count - 1) || (newValue.Time < pointList[index + 1].Time)); // Аналогично проверим, что либо точка последняя в списке, либо новое время её создания меньше времени создания последующей.

if (isOk)

{

pointList.RemoveAt(index); // Заменим старую точку на новую

pointList.Insert(index, editForm.Point);

RefreshGridData(); // И обновим данные формы.

}

else

MessageBox.Show(this, "Время создания точки должно находится в интервале времён создания двух соседник точек.", "Ошибка"); // Покажем окошко с сообщением об ошибке.

}

}

Метод удаления точки будет значительно проще. Всё, что нам требуется сделать, это уточнить серьёзность намерений пользователя. Дальше аналогично – удаляем из списка, обновляем таблицу.

/// <summary>

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

/// </summary>

/// <param name="index">Индекс удаляемой точки.</param>

private void DeletePointAtIndex(int index)

{

if (index < pointList.Count) // Проверяем, что такой индекс в списке существует (...похоже у меня паранойя)

if (MessageBox.Show(this, "Изволите сию же минуту удалить вышеуказанную точку?", "Подтвердите удаление", MessageBoxButtons.YesNo) == DialogResult.Yes) // Удостоверяемся в серьёзности намерений пользователья.

{

pointList.RemoveAt(index); // Если пользователь серьезен, то удаляем точку

RefreshGridData(); // и обновляем данные на форме.

}

}

Метод вставки новой точки в заданное место списка во многом похож на метод редактирования. Разве что мы не берём данные о точке из внутриплагинного списка точек, а создаём новую. Время создания новой точки берется либо как среднее арифметическое времени создания двух соседних точек (если новая точка вставляется в середину списка), либо как время создания последней точки плюс 1000мс.

/// <summary>

/// Метод, вставляющий точку в заданную позицию во внутреннем списке.

/// </summary>

/// <param name="index">Индекс в списке для вставки.</param>

private void InsertPointAtIndex(int index)

{

long time;

// Время создания точки надо инициализировать каким-то числом.

if (pointList.Count != 0)

{

if (index == pointList.Count) // Если мы вставляем в конец списка

time = pointList[pointList.Count - 1].Time + 1000; // То инициализируем временем создания последней точки + 1000мс.

else

time = (pointList[index - 1].Time + pointList[index].Time) / 2; // Если мы вставляем точку в центр списка, то инициализируем средним арифметическим соседних точек.

}

else

time = 1000; // Если список ещё не содержит точек, то инициализируем время значением в 1000мс.

ZigzagPoint newPoint = new ZigzagPoint(time, 0, 0);

EditPointDataForm editForm = new EditPointDataForm(newPoint);

if (editForm.ShowDialog() == DialogResult.OK) // Покажем пользователю форму редактирования с данными о создаваемой точке.

{

if (pointList.Count > 0) // Если список не пустой, то надо проверить, попадает ли новая точка во временные интервалы между соседними точками.

{

long newTime = editForm.Point.Time;

if (index == pointList.Count) // Если мы добавляем точку в конец

{

if (newTime > pointList[index - 1].Time) // то проверим, что время момента создания новой точки выше времени момента создания последней точки списка.

pointList.Add(editForm.Point);

else

MessageBox.Show(this, "Время создания точки должно находится в интервале времён создания двух соседник точек.", "Ошибка"); // Покажем окошко с сообщением об ошибке.

}

else

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

// {t1, t_index, t2} => t1 < ti < t2

if ((newTime > pointList[index - 1].Time) && (newTime < pointList[index].Time))

pointList.Insert(index, editForm.Point);

else

MessageBox.Show(this, "Время создания точки должно находится в интервале времён создания двух соседник точек.", "Ошибка"); // Покажем окошко с сообщением об ошибке.

}

else

pointList.Add(editForm.Point);

RefreshGridData();

}

}

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

#region Обработчики нажатия на кнопки редактирования списка точек

/// <summary>

/// Обработчик нажатия на кнопку вставки, который вставляет точку во внутренний список.

/// Положение для вставки выбирается в зависимости от текущего выбранного элемента в DataGridView.

/// </summary>

private void add_button_Click(object sender, EventArgs e)

{

if (points_dataGridView.SelectedCells.Count != 0)

{

int selectedPointId = points_dataGridView.SelectedCells[0].RowIndex; // Находим индекс выбранной строки.

InsertPointAtIndex(selectedPointId + 1); // Вставляем точку после выбранной строки.

}

else

InsertPointAtIndex(pointList.Count - 1); // Если ничего не выбрано, вставляем в конец.

}

/// <summary> Обработчик нажатия на кнопку редактирования. </summary>

private void edit_button_Click(object sender, EventArgs e)

{

if (points_dataGridView.SelectedCells.Count != 0) // Если какой-то из рядов датагрида выбран

{

int selectedPointId = points_dataGridView.SelectedCells[0].RowIndex;

EditPointAtIndex(selectedPointId); // то вызываем форму редактирования для точки, находящеся по индексу выбранного ряда.

}

}

/// <summary> Обработчик нажатия на кнопку удаления. </summary>

private void delete_button_Click(object sender, EventArgs e)

{

if (points_dataGridView.SelectedCells.Count != 0) // Если в таблице точек есть выбранная строка

{

int selectedPointId = points_dataGridView.SelectedCells[0].RowIndex;

DeletePointAtIndex(selectedPointId); // предложить пользователю удалить точку, содержащуюся в этой строке.

}

}

#endregion

Как вы видите, здесь мы везде узнаём сначала выбранную в таблице точек строку. Закодим обработчики события открытия и закрытия формы. Они совершенно простые и не нуждаются в дополнительных комментариях.

#region Обработчики события появления и закрытия формы

/// <summary> Обработчик события закрытия формы, скрывает её вместо уничтожения. </summary>

private void ZigzagEditorForm_FormClosing(object sender, FormClosingEventArgs e)

{

e.Cancel = true;

this.Hide();

}

/// <summary> Обработчик изменения состояния видимости формы. </summary>

private void ZigzagEditorForm_VisibleChanged(object sender, EventArgs e)

{

if (Visible) // Каждый раз, когда форма становится видимой, открывается,

{

FillPointsList(); // загружаем данные с сервера

RefreshGridData();// и заполняем ими таблицу.

}

}

#endregion

Осталось только загружать и передавать данные на сервер. Данный плагин не требует привязки к событиям. Представьте себе такой сценарий: вы отредактировали данные, добавили вручную много точек, а тут один из клиентов добавил ещё одну к серверу, сервер сгенерировал событие, наш плагин его обработал и загрузил новые данные с сервера и затёр все ваши изменения. Такой образ работы непременно привёл бы к накоплению ЗЛОТСТИ в вашем организме. Конечно, тут можно предусмотреть многие тонкости: не грузить новые данные с сервера, если пользователь уже редактировал текущие; сообщать каким-либо образом, что данные на сервере изменились, и так далее. Это всё уже на ваших плечах. Я поступил по-босяцки и добавил только две кнопки – обработчик нажатия первой запрашивает данные с сервера, обработчик второй – отправляет на сервер.

#region Обработчики нажатия на кнопки взаимодействия с сервером.

/// <summary> Метод обновления данных о точках с сервера. Загружает данные о точках с сервера и перезаписывает ими внутренний список. Обновляет таблицу точек на форме. </summary>

private void refreshData_button_Click(object sender, EventArgs e)

{

FillPointsList(); // Соответственно, загружает данные о точках с сервера и перезаписывает ими внутренний список

RefreshGridData();// и обновляет данные на форме)

}

/// <summary> Метод отправки данных о точках на сервер. Загружает данные о точках, задающих ломаную, на сервер. </summary>

private void pushToServer_button_Click(object sender, EventArgs e)

{

List<ZigzagServer.IPair> list = new List<ZigzagServer.IPair>(pointList.Count); // Создаём список IPair-ов

for (int i = 0; i < pointList.Count; i++)

list.Add(new Pair(pointList[i].Time, new Plugin1.Point(pointList[i].X, pointList[i].Y))); // Заполняем его нашими точками. Левый элемент пары - время, правый - точка, наследник IPoint.

zigzagControl.SetPoints(list.ToArray()); // Задаём точки ломаной на сервере.

}

#endregion

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]