BOOL CEMF::Draw(CDC *pdc, RECT *pRect) {
ASSERT(m_hemf);
BOOL fRet = FALSE;
CRect crect;
CPalette cpalOld = NULL;
if (m_hemf) {
if (m_hPal) {
CPalette cpal;
if ((cpalOld = pdc->SelectPalette(cpal.FromHandle(m_hPal), FALSE))) pdc->RealizePalette();
}
fRet = pdc->PlayMetaFile(m_hemf, pRect);
if (cpalOld) pdc->SelectPalette(cpalOld, FALSE);
}
return (fRet);
}
Что же еще находится в классе CEMF? Как я упоминал выше, есть две закрытых (private) функции, которые управляются с палитрой и заголовком. Я их не буду обсуждать в этой статье, кому интересно см. статьи "Enhanced Metafiles in Win32" и "EMFDCODE.EXE: An Enhanced Metafile Decoding Utility", обе доступны в MSDN. Конечно, вы вероятно захотите также взглянуть на файлы CEMF.CPP и CEMF.H программы METAVIEW, приложенной к статье! Если вы захотите сделать из этого класса что-либо серьезное, предлагаю добавить следующую дополнительную функциональность: возможность делать нумерованые последовательности метафайлов и работать с метафайлами, содержащимися в буфере обмена. Опять же, эти темы описываются в вышеупомянутых статьях.
Отображение документа
Если я понял Найджела правильно, задача состояла в том, чтобы отображать документ тремя различными способами. Во-первых, как картинку в дочернем окне; потом, в виде текста описывающего заголовок метафайла (тоже в дочернем окне), и, наконец, как картинку в окне предварительного просмотра. К тому же, два представления в дочерних окнах нужно было реализовать через архитектуру многодокументного интерфейса (MDI) предоставленную MFC. Рассмотрим каждую из этих задач по отдельности.
Вывод изображения в дочернем окне
Здесь никаких проблем. Надо просто вызвать функцию-член Draw из класса CEMF. Посмотрите поближе на функцию OnDraw в файле METAVVW.CPP.
void CMetavw1View::OnDraw(CDC* pDC) {
CMetavw1Doc* pDoc = GetDocument();
// Флаг для предотвращения рисования во время
// изменения размера окна, см. OnSize() и FullDragOn() в этом модуле.
if (m_fDraw) {
// Если мы печатаем или находимся в режиме предварительного
// просмотра, рабочий прямоугольник узнается из CPrintInfo в OnPreparePrinting
if (pDC->IsPrinting()) {
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
} else {
GetClientRect(&m_rectDraw);
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
}
}
}
Этот код организован вокруг двух условных операторов о которых стоит сказать отдельно. Первое условие является тестом типа "все-или-ничего". Если m_fDraw равно FALSE, не делается никакой попытки что-то нарисовать. Так что же означает m_fDraw? Ну, это мой хитрый прием №2, и я скоро к нему вернусь. Второе условие проверяет ведется ли отрисовка на принтер (или предварительный просмотр) или в дочернее окно. Член-функция IsPrinting класса CDC – это встроенная (inline) функция, которая возвращает public-переменную CDC::m_bPrinting. Раньше, прежде чем воспользоваться этой функцией, я проверял m_bPrinting напрямую. Когда я обнаружил функцию IsPrinting, это меня озадачило. Ведь эта функция просто возвращала значение m_bPrinting и все. Но похоже это больше в духе C++. Если название переменной m_bPrinting в будущем изменилось бы, мой код перестал бы работать. Но это все еще меня немного беспокоит. Как-никак, а я бывал достаточно сообразителен, чтобы при необходимости залезать в отладчик, прослеживать за несколькими переменными и затем придумывать способ получения желаемого результата. И это приводит меня к моей первой (и возможно последней) гипотезе: инкапсуляция и сокрытие данных могут мстить за чрезмерный энтузиазм.
Ладно, хватит о гипотезах. Вернемся к программе. Мы обсуждаем отрисовку документа в дочернем окне. Клиентскую область окна мы получили с помощью GetClientRect и поместили в m_rectDraw.
if (pDC->IsPrinting()) {
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
} else {
GetClientRect(&m_rectDraw);
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
}
Затем вызывается функция Draw и пуф! Появляется картинка.
Вывод изображения в окно предварительного просмотра и на принтер
Да, я схитрил. Я здесь сразу буду заниматься двумя представлениями, в окне предварительного просмотра и на принтере. Но я это делаю исключетельно потому, что для MFC между ними очень мало разницы.
Если IsPrinting возвращает TRUE, вызывается функция Draw с тем, что поначалу кажется неинициализированным m_rectDraw.
if (pDC->IsPrinting()) {
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
} else {
GetClientRect(&m_rectDraw);
pDoc->m_cemf.Draw(pDC, &m_rectDraw);
}
К счастью, это все-таки не так. Библиотека опять приходит на помощь. Когда мы печатаем или находимся в режиме предварительного просмотра, несколько функций вызываются до OnDraw. Эти функции можно перекрыть своими. В данном случае я перекрыл функцию OnPrint (которая в конце концов вызывает OnDraw). В эту функцию передается указатель на объект типа CPrintInfo. Один из переменных-членов этого класса – объект типа CRect, определяющий доступную для печати область. Этот прямоугольник просто копируется в m_rectDraw до вызова OnDraw.
Читать дальше