Иллюстрированный самоучитель по Visual Studio.Net

         

Диалог для исследования решений


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

Так как диалог будет вызываться по команде меню, откройте в окне редактора ресурс меню IDR_MAINFRAME и приведите его в соответствие со следующей схемой. В меню File должна быть только одна команда Exit, в меню Edit уберите все команды и вставьте одну команду Parameters, индекс (ID_EDIT_PARAMETERS) ей будет присвоен автоматически. Остальные меню оставьте без изменения. С помощью редактора диалогов создайте новое диалоговое окно (форму), которое имеет вид, изображенный на рис. 11.4. Типы элементов управления, размещенных в окне диалога, и их идентификаторы сведены в табл. 11.1. Затем создайте класс для управления диалогом.

  • Вызовите контекстное меню в форме диалога и выберите команду Add Class.

  • В качестве типа класса выберите MFC Class.

  • В окне мастера MFC Class Wizard задайте имя класса CParamDlg, базовый класс CDialog, идентификатор диалога: IDD_PARAM и нажмите кнопку Finish.

    Рис. 11.4. Форма диалога для управления параметрами краевой задачи

    Таблица 11.1. Идентификаторы элементов управления

    Вручную введите изменения в файл с объявлением класса, так чтобы он стал: ftpragma once

    class CParamDlg : public CDialog {

    //===== Будем общаться с окном

    friend class CChildView;

    DECLARE_DYNAMIC(CParamDlg)

    public:



    //===== Будем помнить его адрес

    CChildView *m_pView;

    //===== В конструкторе запросим его адрес

    CParamDlg(CChildView* р) ;

    virtual ~CParamDlg () ;

    // Dialog Data

    enum { IDD = IDD_PARAM );

    protected:

    virtual void DoDataExchange(CDataExchange* pDX) ;

    DECLARE_MESSAGE_MAP() );

    Для всех четырех кнопок на форме диалога создайте обработчики уведомлений, или, используя терминологию Microsoft, Control Event типа BN_CLICKED. Вы помните, что это делается с помощью небольшой кнопки Control Event, которая расположена на панели инструментов окна Properties.
    В это окно надо входить в тот момент, когда фокус находится на соответствующей кнопке. Во всяком случае, именно так это работает в бета-версии Studio.Net.

    Для обмена данными с шестью окнами редактирования (IDC_SOL)RCE, IDC_SOURCE1, IDC_SOURCE2, IDC_PROP, IDC_PROP1, IDC_PROP2) создайте с помощью мастера Add Member Variable Wizard шесть переменных:

    //==== Интенсивность источника поля

    double m_Source;

    // Индекс ячейки сетки, где расположено начало источника

    int m_Src!dl;

    // Индекс ячейки сетки, где расположен конец источника

    int m_Srdd2;

    //==== Значение физического свойства ячейки сетки

    double m_Prop;

    // Индексы начала и конца области со свойством

    m_Prop int m_PropIdl;

    int m_PropId2;

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

    void CParamDlg::DoDataExchange(CDataExchange* pDX) {

    DDX_Text (pDX, IDC_PROP2, m_Prop!d2);

    DDXJText(pDX, IDC_PROP1, m_Prop!dl);

    DDX_Text(pDX, IDC_PROP, m_Prop);

    DDX_Text(pDX, IDC_SOURCE2, m_Srdd2);

    DDX_Text(pDX, IDC_SOURCE1, ra_SrcIdl);

    DDX_Text(pDX, IDC_SOURCE, m_Source);

    //===== Обмен с переменными оконного класса

    DDX_Text(pDX, IDC_NODES,m_pView->m__n);

    DDX_Text(pDX, IDC_DIST, m_pView->m_L);

    DDX_Text(pDX, IDC_DECR, m_pView->m_k);

    DDX_Text(pDX, IDC_LEFTG, m_pView->m_g0);

    DDX_Text(pDX, IDC_LEFTD, ra_pView->m_d0);

    DDX_Text(pDX, IDC_RIGHTG, mj?View->m_gn);

    DDX_Text(pDX, IDC_RIGHTD, m_pView->m_dn);

    CDialog::DoDataExchange(pDX);

    }

    При нажатии на одну из кнопок Add в соответствующем контейнере параметров системы (m_f или m_r) должны произойти замены значений по индексам, определяемым диапазоном (m_Srddl, m_Srdd2) ИЛИ (m_Prop!dl, m_Prop!d2).


    В первом случае вы вводите новые источники поля, а во втором — изменяете свойства среды. В уже существующие заготовки функций обработки нажатия на кнопки введите такие коды:

    void CParamDlg::OnClickedApply(void) {

    //====== Считываем данные из окон

    UpdateDataO ;

    //====== Заново решаем систему и выводим график

    m_jpView->Solve () ; }

    void CParamDlg::OnClickedAddsource(void)

    {

    UpdateData();

    //====== Изменяем контейнер m_f (источников поля)

    for (int i=m_Src!dl; i <= m_Srdd2; i + + ) {

    if (0 <= i && i < m_pView~>m_n)

    m_pView->m_f[i] = -m_Source; )

    m_pView->Solve0; }

    void CParamDlg::OnClickedAddprop(void) { UpdateDataO ;

    //====== Изменяем контейнер m_r (свойств среды)

    for (int i=m_Prop!dl; i <= m_PropId2; i++) {

    if (0 <= i &i i < m_pView->m_n && m_Prop > 0.)

    m_pView->ra_r[i] = m_Prop; }

    m_pView->Solve(); }

    void CParamDlg::OnClickedCancel(void)

    {

    //====== Закрываем немодальный диалог

    m_pView->m_pDlg = 0;

    DestroyWindow(); }

    Измените коды конструктора класса так, чтобы запоминался обратный указатель на объект оконного класса. Заодно сверьте начало файла ParamDlg.h с тем фрагментом, что приведен ниже:

    #include "stdafx.h"

    #include "Heat.h"

    #include"ParamDlg.h"

    IMPLEMENT_DYNAMIC(CParamDlg, CDialog)

    CParamDlg::CParamDlg(CChildView* p)

    : CDialog(CParamDlg::IDD, p)

    {

    m_pView = p;

    //===== Начальное значение свойств среды

    //===== не должно равняться нулю

    m_Prop =1.0;

    m_Prop!dl = 0;

    m_Prop!d2 = 0;

    m_Source =0.0;

    m_Src!dl = 0;

    m_Srdd2 = 0;

    }

    CParamDlg::~CParamDlg()

    {

    }

    Инициализация диалога, как вы помните, должна производиться в обработчике сообщения WM_INITDIALOG. Здесь я опять попадаю в ловушку. В рамках Visual C++ Studio.Net вы не найдете WM_INITDIALOG в списке доступных сообщений, но вместо этого найдете функцию OnlnitDialog в списке виртуальных функций (overrides). Введите в класс CParamDlg эту функцию.


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

    BOOL CParamDlg::OnInitDialog(void) {

    CDialog:rOnlnitDialog();

    CRect r;

    //===== С помощью контекста устройства

    //===== узнаем размеры всего экрана CClientDC dc(this);

    int w = dc.GetDeviceCaps(HORZRES);

    int h = dc.GetDeviceCaps(VERTRES);

    //===== Узнаем размеры окна диалога GetWindowRect(&r);

    //===== Смещаем его вправо и вниз

    r.OffsetRect(w-r.right-10,h-r.bottom-30);

    MoveWindow(Sr);

    return TRUE;

    }

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

    #include "ChildView.h"

    в список директив файла ParamDlg.cpp, а также директиву

    #include "ParamDlg.h"

    в список директив файла ChildView.cpp. После этого исправления вы увидите еще одно сообщение об ошибке, которое напомнит вам о том, что еще не реализована работа с диалогом в немодальном режиме. Для этого надо немного потрудиться. Введите в класс CChildView реакцию на событие выбора пользователем команды меню ID_EDIT_PARAMETERS. Напомним, что это делается с помощью кнопки Events окна Properties. В обработчике мы открываем диалог в немодальном режиме:

    void CChildView::OnEditParameters(void) {

    //===== Если диалог не открыт,

    if (!m_pDlg)

    {

    //== Динамически создаем объект диалогового класса

    m_pDlg = new CParamDlg(this);

    //== и после этого создаем окно диалога

    m_pDlg->Create(IDD_PARAM);

    }

    }

    В окне свойств для формы диалога установим в True свойство visible. В классе cParamDlg следует переопределить виртуальную функцию PostNcDestroy, в теле которой необходимо освободить память, занимаемую объектом диалогового класса:

    void CParamDlg::PostNcDestroy(void)

    {

    delete this;

    }

    После этого диалог должен работать. Задайте точечный источник поля в узле 100, и вы увидите график решения, которое имеет вид, показанный на рис. 11.5.





    Рис. 11.5. Управление параметрами краевой задачи из диалога





    Рис. 11.6 Распределение поля в неоднородной среде при наличии осточнтков




    Содержание раздела