Класс для просмотра изображений
//====== Класс для демонстрации содержимого документов
class CRightView : public CScrollView {
//====== Упреждающее объявление класса картинок
friend class CWndGeom; protected:
CSize m_szView; // Реальные размеры окна
CSize m_szScroll; // Размеры прокручиваемого окна
CSize m_szltem; // Размеры картинки
CSize m_szMargin; // Размеры полей
CString m_WndClass; // Строка регистрации картинки
CRightView () ;
DECLARE_DYNCREATE(CRightView) public: //====== Контейнер картинок
vector<CWndGeom*> m_pWnds;
CTreeDoc* GetDocument()
{
return dynamic_cast<CTret=Doc*> (m_pDocument) ;
}
virtual -CRightView();
void Show(); // Демонстрация картинок
void Clear();
// Освобождение ресурсов
// Overrides public:
virtual void OnDraw(CDC* pDC) ;
protected:
virtual void OnlnitialUpdate() ;
DECLARE_MESSAGE_MAP() };
Внесите сокращения и изменения в коды реализации класса так, как показано ниже:
IMPLEMENTJDYNCREATE(CRightView, CScrollView)
BEGIN_MESSAGE_MAP(CRightView, CScrollView) END_MESSAGE_MAP()
CRightView::CRightView()() CRightView::-CRightView(){}
void CRightView::OnDraw(CDC* pDC)
{
CTreeDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);
}
Полосы прокрутки автоматически появляются, когда реальные размеры окна (m_szview) становятся меньше размеров прокручиваемого окна (m_szScroll), которые надо задать в качестве аргумента функции SetScrollSizes. Если пользователь увеличил размеры окна и они стали равными или больше тех, что были указаны, то полосы автоматически исчезают. Отсюда следует, что программист должен как-то задать первоначальные размеры m_szScroll, когда еще не известны требования к ним. Обычно это делается в функции OnlnitialUpdate. Просмотрите коды этой функции, и вы увидите, какие размеры прокручиваемого окна (по умолчанию) задал мастер AppWizard. Для слежения за размерами окна представления введите в класс CRightview реакцию на сообщение WM_SI ZE, так же как вы это делали в классе CDrawView. Измените коды этой функции, а также функции OnlnitialUpdate, в которой мы приравниваем начальные размеры прокручиваемого окна к реальным:
void CRightView::OnSize(UINT nType, int ex, int cy)
{ CScrollView::OnSize(nType, ex, cy) ;
if (cx==0 cy==0)
return;
//====== Запоминаем размеры окна представления
m_szView = CSize (ex, cy);
}
void CRightView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
//====== Начальные размеры окна
m_szScroll = m_szView; SetScrollSizes(MM_TEXT, m_szScroll) ;
}
Функция SetScrollSizes одновременно с размерами задает и режим преобразования координат. Самым неприятным и непонятным моментом в наследовании от класса CScrollView является то, что функция SetScrollSizes не позволяет задавать режимы MM_ISOTROPIC и MM_ANISOTROPIC, которые позволяют, как вы помните работать с формулами. Этот недостаток MFC широко дискутировался как в MSDN, так и на одном из самых популярных сайтов для программистов — www. CodeGuru.com. Там же вы можете обнаружить некоторые решения этой проблемы. Измените конструктор класса. В момент своего рождения объект класса CRi'ghtView должен подготовиться к работе с окнами, управляемыми классом CWndGeom. К тому моменту, когда ему понадобится создать серию таких окон, их тип (класс окон в смысле структуры типа WNDCLASS) уже должен быть известен системе.
Прекрасное решение дал Brad Pirtle, и вы можете найти его в одном из разде-лов CodeGuru, включив поиск по имени. Он создал свой класс CZoomView (производный от CScrolLView), в котором заменил функцию SetScrollSizes на другую — SetZoomSizes, а также переопределил (overrode) виртуальную функцию OnPrepareDC, родительская версия которой обнаруживает и запрещает попытку использовать формульные режимы. В своей версии OnPrepareDC он обходит вызов родительской версии, то есть версии CSrollView, и вместо этого вызывает «дедушкину» версию CView::OnPrepareDC, которая терпимо относится к формульным режимам. Этот пример, на мой взгляд, очень убедительно демонстрирует гибкость объектно-ориентированного подхода при разработке достаточно сложных приложений.
CRightView::CRightView() {
m_szltem = CSize (200,150); // Размеры картинки
m_szMargin = CSize (20,20); // Размеры полей
try
{
//====== Попытка зарегистрировать класс окон
m_WndClass=AfxRegisterWndClass(CS_VREDRAWICS_HREDRAW, ::LoadCursor(GetModuleHandle(0),(char*)IDC_MYHAND), (HBRUSH)CreateSolidBrush(GetSysColor(COLOR_INFOBK)));
}
catch (CResourceException* pEx)
{
AfxMessageBox(_T("Класс уже зарегистрирован")); pEx->Delete ();
}
}
В конструкторе класса CRightView происходит попытка зарегистрировать новый класс окон. Обычно отказов здесь не бывает, но технология требует проверить наличие сбоя, поэтому включаем механизм обработки исключений (try-catch). Мы хотим добиться особого поведения окон с картинками, поэтому зададим для них свою форму курсора и свой цвет фона. Цвет фона выбирается из того набора, который предоставляет система (см. справку по функции GetSysColor), а курсор создали сами. Дело в том, что системный курсор, идентифицируемый как i DC_HAND, работает не во всех версиях Windows. Если вы работаете в среде Windows 2000, то можете заменить в параметре функции LoadCur sor вызов GetModuleHandle (0) на 0, а идентификатор IDC_MYHAND на IDC_HAND и работать с системным курсором. В этом случае ресурс курсора IDC_MYHAND окажется лишним и его можно удалить.
В данный момент мы предполагаем, что в классе документа уже создан динамический контейнер m_Shapes объектов класса CPolygon, каждый элемент которого соответствует данным, полученным в результате чтения документов, обнаруженных в текущем каталоге. Теперь приступим к разработке самой сложной функции в составе класса CRightView, которая должна:
Дальше события развиваются автоматически. После создания окна cwndGeom система пошлет ему сообщение WM_PAINT, в обработке которого надо создать и настроить контекст устройства мини-окна, а затем вызвать функцию Draw для того полигона из контейнера m_Shapes, индекс которого соответствует индексу окна CWndGeom. Каждый полигон рисует себя сам в заданном ему в качестве параметра контексте устройства. Введите в файл реализации класса CRightView следующий код:
void CRightView::Show()
{
CTreeDoc *pDoc = GetDocument0;
//====== Количество картинок
int nPoly = pDoc->m_Shapes.size();
//=== Вычисление шага, с которым выводятся картинки
int dx = m_szltem.cx + m_szMargin.ex,
dy = m_szltem.cy + m_szMargin.cy,
nCols = m_szView.cx/dx; // Количество колонок
//====== Коррекция
if (nCols < 1)nCols = 1;
if (nCols > nPoly)nCols = nPoly;
//====== Количество рядов
int nRows = ceil(double(nPoly)/nCols);
//=== Вычисление и установка размеров окна прокрутки
m_szScroll = CSize(nCols*dx, nRows*dy);
SetScrollSizes(MM_TEXT, m_szScroll);
//====== Координаты и размеры первой картинки
CRect r (CPoint(0,0), m_szltem);
r.OffsetRect (15,15);
//====== Стиль окна картинки
DWORD style = WS_CHILD | WS_BORDER | WS_VISIBLE;
//====== Цикл прохода по рядам (n - счетчик картинок)
for (int 1=0, n=0; i<nRows; i++)
{
//====== Цикл прохода по столбцам
for (int j=0; j<nCols && rKnPoly; j++, n++)
{
//====== Создаем класс окна картинки
CWndGeora *pWnd = new CWndGeom(this, n);
//====== Запоминаем его в контейнере
m_pWnds.push_back(pWnd);
//====== Создаем Windows-окно
pWnd->Create (m_WndClass, 0, style, r, this, 0);
//====== Сдвигаем позицию окна вправо
r.OffsetRect (dx, 0);
}
//=== Начинаем новый ряд картинок (сдвиг влево-вниз)
r.OffsetRect (-nCols*dx, dy);
}
}
Существенным моментом в алгоритме является то, что размер прокручиваемого окна (m_szScroll) зависит от количества картинок. Поэтому сколько бы их не было в текущей папке — все будут доступны с помощью полос прокрутки. Расположение и размеры картинок определяются с помощью объекта класса CRect. Метод Of f setRect этого класса позволяет сдвигать прямоугольник окна в нужном нам направлении.
Обслуживание контейнера m_pWnds дочерних окон типа cwndGeom сопряжено с необходимостью следить за освобождением памяти, занимаемой окнами, в те моменты, когда происходит переход от папки к папке в окне CLef tview. Для этой цели служит вспомогательная функция Clear, которую надо вызывать как в отмеченные выше моменты, так и при закрытии окна. Последний случай сопровождается автоматическим вызовом деструктора класса CRightview. С учетом сказанного введите такие добавки в файл RightView.cpp:
void CRightview::Clear()
{
//====== Цикл прохода по всем адресам контейнера
for (UINT i=0; Km_pWnds. size () ; i++)
{
//====== Уничтожение Windows-окна
m_pWnds[i]->DestroyWindow();
// Освобождение памяти, занимаемой объектом
delete m_pWnds[ i ] ;
}
//===== Освобождение памяти, занимаемой контейнером m_pWnds.clear();
}
//===== Деструктор класса вызывает
Clear CRightview::~CRightview()
{
Clear () ;
}