Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
VC_Lect.doc
Скачиваний:
6
Добавлен:
01.07.2025
Размер:
15 Мб
Скачать
☆

5.3.Документы и их представление

В предыдущем примере мы использовали объект «приложение» и объект окно-рамка (frame window object). Но большинство ваших приложений на базе библиотеки MFC будут гораздо сложнее. Как правило, в них, помимо только что упомянутых классов, будут содержаться еще два класса: «документ» (document) и «вид» или «представление» (view). Такая архитектура «документ-вид» — стержень каркаса приложений; она напоминает классы Model/View/Controller, принятые в среде Smalltalk.

Проще говоря, архитектура «документ-вид» отделяет данные от их представления. Очевидное преимущество этого подхода — возможность представить одни и те же данные по-разному. Предположим, на диске хранится документ с биржевыми сводками за месяц, а данные представлены в виде таблицы и графика. Мы изменяем значения в окне табличного представления, и содержимое окна графического представления тоже изменяется, потому что оба окна отображают одну и ту же информацию (но представленную в разных видах).

В библиотеке MFC документам и их представлениям сопоставляются экземпляры (instances) классов C++. Например, на рис. 5.1 показаны три объекта класса CStockDoc, соответствующие фирмам: AT&T, IBM и GM. Все три документа связаны с табличным представлением, а один из них — еще и с графическим. Как видите, здесь четыре объекта «вид» (они в правой части рис. 5.1): три объекта класса CStockListView и один — класса CStockChartView.

Код базового класса «документ» взаимодействует с командами Open и Save меню File; действительное чтение и запись данных объекта «документ» реализуют производные классы «документ». (Каркас приложений берет на себя большую часть работы по выводу на экран диалоговых окон File и File Save, а также по открытию, закрытию, чтению и записи файлов.) Базовому классу «вид» сопоставлено окно, содержащееся внутри окна-рамки; производный класс «вид» взаимодействует со своим, сопоставленным ему классом «документ» и отвечает за вывод информации на экран и принтер. Производный класс «вид» и его базовые классы обрабатывают сообщения Windows. Библиотека MFC «дирижирует» всеми взаимосвязями между документами, их представлениями, окнами и объектом «приложение» в основном за счет виртуальных функций.

Не подумайте, что объект «документ» должен быть связан с дисковым файлом, целиком считываемым в память. Если, например, «документ» — на самом деле база данных, Вы можете переопределить выбранные функции-члены класса «документ», и тогда команда Open меню File будет выдавать вместо списка файлов список баз данных.

Рис. 5.1. Взаимосвязь документов и их представления

5.4.Функции взаимодействия «документ-вид»

Вы уже знаете, что объект «документ» содержит данные, а объект «вид» представляет их на экране и позволяет редактировать. В SDI-приложении есть класс «документ», производный от CDocument и один или несколько классов «вид», каждый из которых, в конечном счете происходит от CView. Документ, окно представления и остальные элементы каркаса приложений взаимодействуют весьма тесно. Чтобы в этом разобраться, надо познакомиться с пятью важными функциями-членами классов «документ» и «вид». Две из них — невиртуальные функции базового класса, вызываемые производными классами, остальные — виртуальные функции, часто переопределяемые в производных классах. Рассмотрим их по порядку.

Функция CView::GetDocument

С объектом «вид» связан один и только один объект «документ». Функция GetDocument() позволяет выяснить, какому документу сопоставлено данное окно представления. Допустим, объект «вид» получил сообщение о том, что пользователь ввел новые данные в поле ввода. Он должен уведомить об этом документ, чтобы тот обновил свои внутренние данные. GetDocument возвращает указатель на документ; через этот указатель можно обращаться к функциям-членам или открытым переменным-членам класса «документ».

ПРИМЕЧАНИЕ Функция CDocument::GetNextView() позволяет перейти от документа к представлению, но так как у документа может быть несколько представлений, ее надо вызывать в цикле для каждого из них. Впрочем, Вы будете использовать функцию GetNextView редко — каркас приложений предоставляет более эффективный способ перебора представлений документа.

Генерируя класс, производный от CView, мастер создает специальную версию функции GetDocument() для безопасного приведения типов; она возвращает указатель не на CDocument, a на объект производного класса. Эта подставляемая (inline) функция выглядит примерно так:

CMyDoc * GetDocument()

{

return (CMyDoc*) m_pDocument;

}

Встретив в коде вашего класса «вид» вызов GetDocument(), компилятор использует версию из производного класса, а не из CDocument, поэтому Вам не надо приводить указатель к производному классу документа.

Функция CDocument::UpdateAllViews

Если содержание документа по какой-либо причине изменилось, надо уведомить об этом все объекты «вид», чтобы те обновили представление данных. При вызове UpdateAllViews() из функции-члена производного класса документа ее первый параметр pSender равен NULL. Если же она вызывается посредством функции-члена производного класса «вид», параметр pSender указывает на текущий объект «вид»:

GetDocument()->UpdateAllViews(this);

Значение параметра, отличное от NULL, позволяет каркасу приложений не уведомлять текущий объект «вид» — предполагается, что он уже обновился сам.

У этой функции есть необязательные параметры, через которые можно передавать объекту специфичную для приложения информацию о том, какие части представления следует обновить. Как именно уведомляется объект при вызове UpdateAllViews()? Для ответа на этот вопрос сначала рассмотрим другую функцию — OnUpdate.

Функция CView::OnUpdate

Это виртуальная функция, вызываемая каркасом приложений в ответ на вызов вашей программой функции UpdateAllViews(). Конечно, Вы можете вызывать ее прямо в своем классе, производном от CView. Обычно функция OnUpdate() из производного класса обращается к документу и, получив его обновленные данные, либо обновляет переменные-члены класса или соответствующие элементы управления, либо объявляет часть представления недействительной, что заставляет функцию OnDraw() перерисовать часть окна по данным документа. Реализация функции OnUpdate() может выглядеть примерно так:

void CMyView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)

{

CMyDocument* pMyDoc = GetDocument();

CString lastName = pMyDoc->GetLastName();

m_pNameStatic->SetWindowText(lastName); // m_pNameStatic является членом

// класса CMyView

}

Вспомогательная информация передается (последние два параметра) в эту функцию функцией UpdateAllViews(). Исходная реализация OnUpdate() объявляет недействительным все окно представления. В переопределенной версии Вы можете объявить недействительным прямоугольник меньшего размера — в соответствии с информацией, полученной от UpdateAllViews (через последние два параметра).

Если CDocument-функция UpdateAllViews() вызывается с параметром pSender, указывающим на конкретный объект «вид», то OnUpdtae() вызывается для всех представлений документа, кроме заданного в параметре.

Функция CView::OnInitialUpdate

Эта виртуальная функция класса CView вызывается при запуске приложения, а также при выборе команд New или Open меню File. Версия OnInitialUpdate() в базовом классе CView просто вызывает OnUpdate. Если Вы переопределили ее в своем производном классе, убедитесь, что она вызывает OnInitialUpdate() базового класса или OnUpdate() производного класса.

Функция OnInitialUpdate() производного класса пригодна для инициализации объекта «вид». При запуске программы каркас приложений вызывает OnInitialUpdate() сразу после вызова OnCreate(), если Вы задействовали OnCreate() в своем производном классе «вид». Функция OnCreate() вызывается только один раз, но OnInitialUpdate() можно вызывать сколько угодно раз.

Функция CDocument::OnNewDocument

Эту виртуальную функцию каркас приложений вызывает после создания объекта «вид» или выбора в меню File SDI-приложения команды New. Именно здесь удобнее всего инициализировать переменные-члены вашего класса «документ». Для производного класса документа мастер генерирует переопределенную функцию OnNewDocument(). Обязательно оставьте в ней вызов функции базового класса.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]