Алекс Jenter - Программирование на Visual C++. Архив рассылки
- Название:Программирование на Visual C++. Архив рассылки
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Алекс Jenter - Программирование на Visual C++. Архив рассылки краткое содержание
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.
Программирование на Visual C++. Архив рассылки - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
class CEMF : public CObject {
// Операции
public:
CEMF();
~CEMF();
BOOL Load(const char *szFileName);
BOOL Draw(CDC* pDC, RECT* pRect);
LPENHMETAHEADER GetEMFHeader() {
return ((m_pEMFHdr) ? m_pEMFHdr : NULL);
}
LPTSTR GetEMFDescString() {
return ((m_pDescStr) ? m_pDescStr : NULL);
}
HENHMETAFILE GetEMFHandle() {
return ((m_hemf) ? m_hemf : NULL);
}
protected:
BOOL GetEMFCoolStuff();
BOOL LoadPalette();
// Данные
protected:
CString m_szPathName;
HENHMETAFILE m_hemf;
LPENHMETAHEADER m_pEMFHdr;
LPTSTR m_pDescStr;
LPPALETTEENTRY m_pPal;
UINT m_palNumEntries;
LPLOGPALETTE m_pLogPal;
LOGPALETTE m_LogPal;
HPALETTE m_hPal;
};
Функция Load подозрительно смотрит на начало файла, как и мой предыдущий код в функции Serialize. Но теперь нет объекта типа CArchive со всеми его преимуществами. Нет проблем. Использование объекта типа CFile позволяет прочитать сигнатуру. Функции GetEMFCoolStuff и LoadPalette взяты из моей программы-примера EMFDCODE в MSDN. Они получают копии заголовка метафайла, строки описания, и палитры внедренной в метафайл. Конечно, они теперь находятся в классе CEMF.
BOOL CEMF::Load(const char *szFileName) {
UINT uiSig;
// Сохранить имя файла.
m_szPathName = szFileName;
// Проверить сигнатуру
CFile cfEMF;
cfEMF.Open(m_szPathName, CFile::modeRead | CFile::shareDenyWrite);
cfEMF.Read(&uiSig, sizeof(UINT));
cfEMF.Close();
// Если это EMF, получить его дескриптор.
if (uiSig == EMR_HEADER) {
m_hemf = GetEnhMetaFile(m_szPathName);
GetEMFCoolStuff();
LoadPalette();
} else m_hemf = NULL;
// Возвращаем результат.
return ((m_hemf) ? TRUE : FALSE);
}
Функция Draw вызывается из функции OnDraw класса представления. Особо интересного там ничего не происходит. Если есть палитра, на что указывает не равное NULL значение переменной-члена m_hPal, палитра выбирается в контекст устройства (DC). Я был сильно озадачен, когда узнал что CDC::SelectPalette требует указатель на объект типа CPalette. Но я был так же заинтригован, когда вдруг обнаружил функцию CPalette::FromHandle. Я мог легко преобразовать дескриптор палитры в объект типа CPalette. Далее это было уже просто делом воспроизведения метафайла с помощью CDC::PlayMetaFile.
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.
Читать дальшеИнтервал:
Закладка: