Опубликован: 10.03.2009 | Доступ: свободный | Студентов: 2296 / 280 | Оценка: 4.31 / 4.07 | Длительность: 09:23:00
Лекция 5:

Разработка полноценных Windows-приложений

3. Ввод начальных данных

Рассмотрим два способа ввода начальных данных: ввод точных данных с клавиатуры и визуальное редактирование таблицы узлов посредством мыши. Первый подход реализован с помощью создания и подключения к приложению диалогового окна с необходимыми элементами управления, а второй с помощью обработки двойного нажатия левой кнопки мыши. Также необходимо организовать хранение информации об узлах. Ввод данных с клавиатуры. Создадим ресурс диалогового окна с помощью редактора ресурсов. В диалоговом окне должны присутствовать 2 текстовых поля ввода (координаты x и y), 4 кнопки (Add, Delete, Ok, Cancel) и список для отображения таблицы узлов. После создания ресурса, добавим класс диалогового окна на его базе. Создадим подпункт меню Add/Delete Initial Points в пункте Tools и аналогичную кнопку на панели управления, при нажатии на которые будет появляться диалоговое окно. Прототип класса диалогового окна для ввода узлов:

#pragma once
#include "afxwin.h"
// CAddPointDlg dialog
class CAddPointDlg : public CDialog
{
  DECLARE_DYNAMIC(CAddPointDlg)
public:
  CAddPointDlg(CWnd* pParent = NULL);   // standard constructor
  virtual ~CAddPointDlg();
// Dialog Data
  enum { IDD = IDD_ADDPOINT_DIALOG };
protected:
  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  DECLARE_MESSAGE_MAP()
public: //переменные связанные с элементами управления добавлены с помощью мастера
  CListBox PointList;  //Переменная связанная со списком
  double ValX;  //Переменная связанная с первым текстовым полем
  double ValY;  //Переменная связанная со вторым текстовым полем
  vector<SDPoint> ListedPointsVec;  //Вектор узлов интерполирования (добавлена вручную)
public:  //Обработчики и переопределенные виртуальные функции добавлены с помощью мастера
  afx_msg void OnBnClickedButtonAdd();  //Обработчик кнопки Add
  virtual BOOL OnInitDialog();
  afx_msg void OnBnClickedButtonDelete();  //Обработчик кнопки Delete
};

Обработчик кнопки Add на диалоговом окне:

void CAddPointDlg::OnBnClickedButtonAdd()
{
  CString str;  //Вспомогательная строка для вывода узла в список
  SDPoint temp;  //Вспомогательная точка
  UpdateData(true);  //Обмен данными
  temp.x = ValX;  //Инициализация вспомогательной точки
  temp.y = ValY;
  for(size_t i = 0; i < ListedPointsVec.size(); i++)  //Перебор всех узлов интерполяции
    if(ListedPointsVec[i].x == ValX && ListedPointsVec[i].y == ValY)  //Если один из узлов совпал с вводимым то...
      return;  //выход из функции
  ListedPointsVec.push_back(temp);  //Добавление нового узла в вектор (проверка сделана, одинаковых нет)
  str.Format("(%f;%f)",ValX,ValY);  //Формирование строки для добавления в список
  PointList.AddString(str);  //Добавление строки в конец списка
}

Функция для инициализации переменных диалогового окна:

BOOL CAddPointDlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  CString str;  //Вспомогательная строка для формирования списка
  for(size_t i = 0; i < ListedPointsVec.size(); i++)  //Перебор по всему вектору узлов
  {
  str.Format("(%f;%f)",ListedPointsVec[i].x,ListedPointsVec[i].y);  //Формирование строки
  PointList.AddString(str);  //Добавление ее в список
  }
  return TRUE; 
}

Обработчик кнопки Delete на диалоговом окне:

void CAddPointDlg::OnBnClickedButtonDelete()
{
  int n = PointList.GetCurSel();  //Получение номера выделенной строки списка
  if(n == LB_ERR) return;  //Если ничего не выделено - выход
  if(n == ListedPointsVec.size())  //Если выделена последняя строка, то...
    ListedPointsVec.pop_back();  //удаление последнего элемента из таблицы
  else //иначе...
  {
  for(size_t i = n; i < ListedPointsVec.size()-1; i++)  //перебор узлов начиная с n-ого и до конца
  {
  //Смещение узлов в векторе
  ListedPointsVec[i].x = ListedPointsVec[i+1].x;  
  ListedPointsVec[i].y = ListedPointsVec[i+1].y;
  }
  ListedPointsVec.pop_back();  //Удаление последнего элемента
  }
  PointList.DeleteString(n);  //Удаление ненужней строки списка
}

Для корректной работы последней функции, в списке должна быть отключена сортировка. Когда диалоговый класс реализован, необходимо подключить его к программе. Для этого используем соответствующий пункт меню и кнопку на панели управления - Add/Delete Initial Points. Также необходимо передать введенные данные в основное хранилище информации (объект класса документа). Обработчик пункта меню Add/Delete Initial Points и связанной с ним кнопки на панели инструментов:

void CGpView::OnToolsAddpoint()
{
  CAddPointDlg dlg;  //Создаем экземпляр класса диалогового 
  //Стандартная процедура получения указателя на документ и его проверки на   //корректность
  CGpDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;
  pol_ready = false; //Сигнализируем о том что построенный полином устарел (сейчас будет меняться таблица)
  Invalidate();  //Перерисовываем
  dlg.ListedPointsVec = pDoc->InitPoints;  //переписываем копию узлов в диалоговую переменную
  switch(dlg.DoModal())  //Ждем реультата работы диалога
  {
  case -1: AfxMessageBox("AddPoiintDialog creation error"); return;  //Если диалог не запустился
  case IDOK: pDoc->InitPoints = dlg.ListedPointsVec; Invalidate(); break;  //Если нажали Ok то переписываем 
  новую таблицу в документ и перерисовываем
  case IDCANCEL: break;  //Если Cancel ничего не делаем
  }
}

Этот обработчик осуществляет связь между механизмом изменения (диалоговое окно) узлов и механизмом их хранения (объект документа). Прежде чем переходить к реализации визуального редактирования, рассмотрим механизм хранения узлов и интерполяционного полинома. Объект документа полностью соответствует требованиям хранилища необходимых программе данных. Прототип класса документа:

// GpDoc.h : interface of the CGpDoc class
//
#pragma once
#include "TPolinom.h"
class CGpDoc : public CDocument
{
protected: // create from serialization only
  CGpDoc();
  DECLARE_DYNCREATE(CGpDoc)
// Attributes
public:
// Operations
public:
// Overrides
public:
  virtual BOOL OnNewDocument();
  virtual void Serialize(CArchive& ar);
// Implementation
public:
  virtual ~CGpDoc();
#ifdef _DEBUG
  virtual void AssertValid() const;
  virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
  DECLARE_MESSAGE_MAP()
public:  //Необходимые переменные
  vector<SDPoint> InitPoints;  //Таблица узлов реализована с помощью вектора 
   вещественных точек плоскости
  TPolinom<double> InterPol;  //Интерполяционный полином
  bool pol_ready;  //Флаг сигнализирующий о том что полином построен
public:
  bool BuildPolinom(void);  //Функция для построения полинома
  afx_msg void OnToolsBuilddrawpolinom();
};

Важным для данного пункта является объект InitPoints класса vector SDPoint . В него записываются и хранятся на протяжении работы программы узлы интерполирования.

Визуальное редактирование таблицы узлов. Создадим подпункт Mark initial points в меню и соответствующую кнопку, которые будут переводить программу в режим ожидания редактирования (двойного клика по левой кнопке мыши) и выводить из него. Добавим обработчик сообщения WM_LBTNDBLCLICK Обработчик нажатия пункта меню Mark initial points и связанной с ним кнопки на панели инструментов:

void CGpView::OnToolsMarkinitialpoints()
{
  parallel_shift = false;  //Если в режиме ожидания переноса то выход из него
  mark_points = !mark_points;  //Вход или выход в(из) режима визуального редактирования узлов
}

Обработчик изменения внешнего вида пункта меню Mark initial points и связанной с ним кнопки на панели инструментов:

void CGpView::OnUpdateToolsMarkinitialpoints(CCmdUI *pCmdUI)//Обрабатываем изменение внешнего вида кнопки (пункта меню)
{
  pCmdUI->SetCheck(mark_points);  //Смена статуса (активна или не активна) в зависимости от флага
}

Обработчик двойного клика по левой кнопке мыши:

void CGpView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
  if(mark_points)  //Если в режиме визуального редактирования, то...
  {
    //Получение указателя на документ
    CGpDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
      return;
    SDPoint help = SysToLog(point);  //Вспомогательная переменная для проверки (вводится ли уже существующий узел)
    for(size_t i = 0; i < pDoc->InitPoints.size(); i++)  //Перебор по всем узлам и проверка...
      if(help.x == pDoc->InitPoints[i].x && help.y == pDoc->InitPoints[i].y)  //Совпадает с вводимым хотя бы оди узел таблицы
        return;  //Если да, то выход из функции без добавления вводимого узла в таблицу
    pDoc->InitPoints.push_back(help);  //Добавление вводимого узла в таблицу (проверка сделана, повторов нет)
    Invalidate();  //Обновление клиентской области окна
  }
  CView::OnLButtonDblClk(nFlags, point);
}