Шаг 249 - Много немодальных диалогов

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

Изменяем предыдущий шаг. Создаваться объект диалога будет не в конструкторе Вида, а при выборе нашего пункта меню:

void CTestView::OnTestdialog() 
{
	m_pTestDial = new CTestDial(this);
	m_pTestDial->Create();	
}

Уничтожаться объект будет здесь:

LONG CTestView::OnEndOfDial(UINT wParam, LONG lParam)
{
	m_pTestDial->DestroyWindow();
	delete m_pTestDial;

	return 0;
}

Соответственно вы должны удалить то, что осталось от прошлого шага, создание и уничтожение объекта из конструктора и деструктора Вида.

Теперь, если мы все это запустим, то приложение будет работать корректно только с одним окном или с последним созданным, т.к. при создании каждого нового окна мы теряем указатель на предыдущий объект диалога. Можно создать массив указателей, но мы сделаем проще. Мы избежим этого созданием глобального указателя на класс приложения, к которому можно будет обращаться из любого места программы в шаге 212 Артем говорил об этом, в Test.cpp добавляем строку:

CTestApp theApp;
CTestApp* NEAR m_gpApp = & theApp;

В stdafx.h вставляем:

//{{AFX_INSERT_LOCATION}}
#include "Test.h"
extern CTestApp* NEAR m_gpApp;

В классе диалога создаем указатель на самого себя:

CTestDial(CView* pView);
	CView* m_pView;
	CTestDial* m_pDial;

И в конструкторе его инициализируем:

CTestDial::CTestDial(CView* pView)
{
	m_pView = pView;
	m_pDial = this;
}

Далее вкладка ClassView -> правый клик по CTestDial -> Add Windows Message Handler... В списке Filter for messages available to class ставим Topmost Frame и перегружаем сообщение WM_ACTIVATE и вставляем код:

void CTestDial::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
{
	CDialog::OnActivate(nState, pWndOther, bMinimized);
	
	m_gpApp = (CTestApp*)m_pDial; 
	m_pView->PostMessage(WM_SETPOINTER);
}

В TestDial.h определяем сообщение WM_SETPOINTER:

#define WM_SETPOINTER WM_USER + 15

В карту сообщений Вида вставляем строку:

ON_MESSAGE(WM_SETPOINTER, OnSetPointer)

В классе Вида создаем обработчик сообщения:

LONG CAnalysatorView::OnSetPointer(UINT wParam, LONG lParam)
{
	m_pTestDial = (CTestDial*)m_gpApp;
	
	return 0;
}  

Поясняю: мы перегрузили сообщение активизации окна, то есть при активизации окна вызывается функция OnActivate(), которая преобразует указатель m_pDial(указывающий на данный объект диалога) к типу глобального указателя на приложение (CTestApp*), посылает сообщение облику об обработке функции OnSetPointer(), в которой мы преобразуем глобальный указатель к указателю на объект диалога, m_pTestDial находится в классе Вида и с помощью его мы закрываем диалог, т.е. теперь мы можем корректно уничтожать диалоги.

И наконец для завершения поставленной задачи создадим кнопку и обработчик для нее, создаем элемент управления CProgressCtrl и переменную m_Progress. В обработчике кнопки мы запускаем поток, вот код кнопки:

void CTestDial::OnButton() 
{
	AfxBeginThread(stream, this);
}

В TestDial.cpp опишем прототип потока:

UINT stream(LPVOID Param);

И сам поток:

UINT stream(LPVOID Param)
{
	CTestDial* pDial = (CTestDial*)m_gpApp;
	int x = 0;
	pDial->m_Progress.SetRange(0, 100);
	pDial->m_Progress.SetPos(x);
	
	for (int i = 0; i < 10; i++)
	{
		pDial->m_Progress.SetPos(x += 10);
		Sleep(1000);
	}

	return 0;
}

И опять с помощью глобального указателя мы реализуем доступ к переменным класса. Запускаем и все работает как часы.

Где это может пригодится ? Например при обработке файлов. Вы можете одновременно обрабатывать хоть сколько файлов !!!

Шаг прислал Panov Denis (stream@yandex.ru).


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 27.10.2000