Учебник по Visual C++ .Net

         

Класс для просмотра изображений


//====== Класс для демонстрации содержимого документов

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, которая должна:

  • Пройти по всему перечню объектов m_shapes класса CPolygon.

  • Вычислить исходя из текущего размера окна количество рядов и колонок мини-окон с изображениями полигонов.

  • Создать для каждого из них окно, управляемое классом CWndGeom.


  • Дальше события развиваются автоматически. После создания окна 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 () ;

    }


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