Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Добавление "закладок" в окно документа MDI приложения (способ 1).


Информационный Канал Subscribe.Ru

Visual C++ - расширенное программирование

Выпуск      : 19
Подписчиков : 5977
Cайт        : SoftMaker.com.ru
Архив       : Visual C++ - расширенное программирование (архив)
В этом выпуске
Архитектура .NET и программирование на Visual C++

Архитектура .NET и программирование на Visual C++

От автора

Здравствуйте уважаемые подписчики !

Как всегда, рад приветствовть вас на страницах этой рассылки.

Как обычно, напомню, что если Вы хотите:

  • - опубликовать здесь статью
  • - создать и вести какой либо раздел в этой рассылке
  • - предложить интересную идею, тему для публикации, и.т.д.
просто напишите мне.

Я также буду рад любым пожеланиям, замечаниям, предложениям и сообщениям об ошибках по поводу рассылки и сайта. Их можно отправлять по тому же адресу.

И, как всегда, вы можете задать свои вопросы по программированию в форуме.
Или обсудить их в дискуссионном листе "Программирование. Форум !!!".

Многим может быть также интересна рассылка: C/C++ Вопрос-Ответ, формируемая из писем читателей. Здесь Вы можете задать вопрос по программированию на C/C++ и ответить на вопросы других подписчиков.

С уважением, Вахтуров Виктор.

Статьи

Добавление "закладок" в окно документа MDI приложения (способ 1)

Вступление

Наверно, каждому не раз приходилось работать с приложениями, предоставляющими многодокументный (MDI) интерфейс. Часто такие приложения имеют возможность отображения редактируемого документа в виде нескольких различных представлений. Например, визуальные HTML редакторы (такие как Front Page) позволяют осуществлять WYSIWIG-редактирование, редактирование текста и предпросмотр документа. Часто переключение между представлениями осуществляется с помощью "закладок", расположенных непосредственно в окне редактирования документа. Ярким примером может служить приложение Microsoft Visual InterDev 6.0. Скриншот окна редактирования HTML документов этого приложения приведен ниже:



Рисунок 1.


Как видно, пользователь имеет возможность быстро переключаться между режимом визуального редактирования, редактирования HTML кода и просмотром документа.

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

Ниже будет рассмотрено, как быстро и достаточно просто реализовать подобный интерфейс в приложении, обойдясь "малой кровью" - стандартными средствами Windows (ну и, конечно же, библиотекой MFC).

Способы реализации

Способ 1

Итак, безусловно, нам хотелось бы иметь достаточно простое и эффективное решение. Здесь уместно вспомнить, что в Windows уже реализован механизм переключения между несколькими окнами с помощью закладок. Он представлен элементом интерфейса Property Sheet - "таблицей свойств", состоящей из нескольких страниц- окон (Property Pages).

Property Sheet создается при помощи API функции:

int PropertySheet(LPCPROPSHEETHEADER lppsph);

Библиотека MFC предоставляет соответствующий класс-обертку CPropertySheet, инкапсулирующий структуру PROPSHEETHEADER.

Обычно Property Sheet используется как модальное окно, однако ничто не мешает создать его немодальным, сделав дочерним по отношению к любому другому окну.

Этим и стоит воспользоваться.

Итак, основная идея состоит в следующем:

  • В созданном проекте мультидокументного MFC-приложения в дочернем окне-рамке надо заменить View на немодальное окно Property Sheet.
  • Далее необходимо добавить в Property Sheet страницы и создать на них окна требуемых View (можно конечно и не только View, и даже совсем не View, но для более академичного изложения ограничимся именно таким вариантом).
  • Заставить все работать как надо.
    Дело в том, что закладки (окно класса SysTabControl32, расположенное в окне Property Sheet-а), а также окна View не будут сами позиционироваться по размерам клиентской области дочернего окна-рамки MDI-интерфейса - механизм их перерасположения придется реализовывать самостоятельно. Также есть некоторые нюансы в реализации механизмов создания и уничтожения View (обусловленные архитектурой MFC), а также создания Property Sheet, которые и будут рассмотрены ниже.

Способ 2

Второй способ состоит в том, чтобы создать в окне документа MDI приложения панель инструментов на базе диалогового окна (класс CDialogBar MFC) и поместить в нее элемент управления "закладки ". Затем можно создать несколько View и сделать одно из них видимым.

В такой реализации необходимо будет обрабатывать сообщение TCN_SELCHANGE, посылаемое TAB-контролом при выборе пользователем одной из "закладок" с тем, чтобы в обработчике производить "подмену " видимого пользователем View.

Данный способ будет рассмотрен в следующем номере рассылки.


Реализация (способ 1)

Для данной статьи, было написано демонстрационное приложение. Так как дальнейшее изложение будет иллюстрироваться примерами исходного кода этого приложения, то, чтобы быть последовательным, опишу созданный проект.

Проект был создан при помощи обычного мастера для создания приложений MFC. Он был создан как MDI-приложение на базе архитектуры документ-облик. Я назвал проект TabViewDemo.

Визард сгенерировал классы:

CMainFrame       - класс главного окна рамки
CChildFrame      - класс дочернего окна-рамки MDI-приложения
CTabViewDemoDoc  - класс документа
CTabViewDemoView - класс View
CTabViewDemoApp  - класс приложения
Думаю, здесь больше пояснений не требуется.
Теперь рассмотрим, собственно, аспекты реализации.


Меняем View на Property Sheet

Сгенерированное мастером приложение имеет обычную для MDI-приложения структуру - в каждом дочернем окне-рамке создается окно View. Его (View) идентификатор равен AFX_IDW_PANE_FIRST, вследствие чего оно "растягивается" движком MFC на всю клиентскую область родительского окна, не занятую панелями инструментов.

Мы хотим заменить View на Property Sheet и установить идентификатор его окна равным AFX_IDW_PANE_FIRST. В этом случае наш Property Sheet также будет "автоматически растягиваться" на всю клиентскую область.

Так как нам необходимо избавиться от View (и желательно как можно более простым способом), рассмотрим сначала процесс создания View (а заодно и документа с окном-рамкой).

В MFC MDI приложениях за создание объектов документа и окна-рамки обычно "отвечает" объект класса CMultiDocTemplate. Его метод OpenDocumentFile вызывается из метода OpenDocumentFile объекта менеджера документов (CDocManager) при создании новых и загрузке существующих документов приложением.

Объект (или объекты) класса CMultiDocTemplate обычно создаются в виртуальной функции InitInstance объекта класса приложения (для нашего случая - в CTabViewDemoApp::InitInstance).

В только что созданном проекте это выглядело так:

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(
   IDR_TABVIETYPE,
   RUNTIME_CLASS(CTabViewDemoDoc),
   RUNTIME_CLASS(CChildFrame),
   RUNTIME_CLASS(CTabViewDemoView));

Как видно, в конструктор CMultiDocTemplate передаются указатели на структуры CRuntimeClass, инкапсулируемые классами CTabViewDemoDoc, CChildFrame и CTabViewDemoView. Конструктор сохраняет значения этих указателей в переменных-членах класса CDocTemplate m_pDocClass, m_pFrameClass и m_pViewClass.

Структура CRuntimeClass содержит информацию, необходимую для динамического создания объектов классов, унаследованных от CObject, во время исполнения приложения. Объекты создаются путем вызова метода CRuntimeClass::CreateObject.

Объект (и окно) View создается в методе CFrameWnd::CreateView вызываемом из функции CFrameWnd::OnCreateClient при обработке сообщения WM_CREATE дочерним окном-рамкой MDI-интерфейса. Указатель на объект структуры CRuntimeClass для динамического создания View передается в OnCreateClient в поле m_pNewViewClass структуры CCreateContext.

Код CFrameWnd::OnCreateClient выглядит так:

BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT,
                               CCreateContext* pContext)
{
   // default create client will create a view if asked for it
   if (pContext != NULL && pContext->m_pNewViewClass != NULL)
   {
      if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
         return FALSE;
   }
   return TRUE;
}

Как видно, в нем выполняется проверка указателя m_pNewViewClass на неравенство NULL (в случае, если m_pNewViewClass равен NULL, View просто не создается). Это позволяет очень просто предотвратить создание View - достаточно передать NULL в качестве указателя на объект CRuntimeClass для View в конструктор CMultiDocTemplate.

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(
   IDR_TABVIETYPE,
   RUNTIME_CLASS(CTabViewDemoDoc),
   RUNTIME_CLASS(CChildFrame),
   NULL);

Таким образом мы "избавились" от View. Теперь необходимо поместить на его место Property Sheet. Займемся этим.

Я создал класс CChildSheet, наследованный от CPropertySheet (подробнее о его реализации расскажу позже). Объект класса CChildSheet m_wndChildSheet был добавлен в декларацию класса CChildFrame:

class CChildFrame : public CMDIChildWnd
{
   …
   CChildSheet   m_wndChildSheet;
   …
};

В функции CChildFrame::OnCreateClient создается само окно Property Sheet:

BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs,
                                 CCreateContext *pContext)
{
   if(!CMDIChildWnd::OnCreateClient(lpcs, pContext))
      return FALSE;

   BOOL bSuccess = FALSE;

   CChildSheet::SetCreateContext(pContext);

   if(m_wndChildSheet.Create(this, WS_CHILD | WS_VISIBLE |
                             WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
                             DS_SETFONT, WS_EX_CLIENTEDGE))
   {
      SetWindowLong(m_wndChildSheet.GetSafeHwnd(), GWL_ID,
                    AFX_IDW_PANE_FIRST);

      int nCurSel = m_wndChildSheet.GetActiveIndex();
      int nPageCount = m_wndChildSheet.GetPageCount();

      for(int i = 0; i < nPageCount; i++)
      {
         if(!m_wndChildSheet.SetActivePage(i))
         {
            TRACE("Failed to create property page %d\n", i);
            break;
         }

         if(!::IsWindow(
                  m_wndChildSheet.GetPage(i)->GetSafeHwnd()))
         {
            TRACE("Failed to create property page %d\n", i);
            break;
         }
      }

      if(i == nPageCount)
      {
         m_wndChildSheet.SetActivePage(nCurSel);
         bSuccess = TRUE;
      }
   }
   else
      TRACE0("Failed to create sheet window\n");

   CChildSheet::SetCreateContext(NULL);

   return bSuccess;
}

Здесь надо отметить следующее:

  • После создания окна Property Sheet, ему устанавливается идентификатор AFX_IDW_PANE_FIRST (о необходимости этого уже говорилось); кстати, сразу создать Property Sheet с заданным идентификатором нельзя.
  • Также после создания Property Sheet последовательно активизируются все страницы свойств (с последующей проверкой успешности их инициализации).
    Дело в том, что Property Sheet создает сразу только одну страницу (страницу, указанную в поле nStartPage (pStartPage) структуры PROPSHEETHEADER). И впоследствии, когда пользователь будет получать доступ к другим страницам, они могут быть не созданы из за ошибок создания их дочерних окон. В этом случае (если окно Property Page не создано), закладка просто пропадает. Согласитесь, неожиданность для пользователя.
Готово. Теперь можно создать несколько Property Pages (страниц свойств), поместить "на них" View разных типов, и связать View с объектом документа.

Реализуем "страницы свойств"

Для того чтобы иметь возможность унифицировано позиционировать все страницы (об этом также чуть позже) был создан класс CBasePage, унаследованный от CPropertyPage. От него и будем наследовать все остальные классы страниц.

Итак, создаем 3 шаблона диалога. С помощью ClassWizard-а создаем 3 класса, наследованные от CPropertyPage (я назвал их просто CPage1, CPage2, CPage3), заменяем их базовый класс на CBasePage.

В декларацию CChildFrame добавляем объекты этих классов:

CPage1 m_wndPage1;
CPage2 m_wndPage2;
CPage3 m_wndPage3;

В конструкторе CChildFrame добавим страницы в Property Sheet:

CChildFrame::CChildFrame()
{
   m_wndChildSheet.AddPage(m_wndPage1);
   m_wndChildSheet.AddPage(m_wndPage2);
   m_wndChildSheet.AddPage(m_wndPage3);
}

Создаем View

Для наглядности я решил разместить на всех вкладках View разного типа (на первой - редактор, на второй - список, а на третьей - обычное View, в котором будет что либо нарисовано). Для этого создал 3 класса View: CDemoView_1, CDemoView_2, CDemoView_3, родительскими классами которых являются: CEditView, CListView и CView соответственно.

Рассмотрим некоторые аспекты реализации этих классов.

Пожалуй, самым важным моментом здесь является то, что объекты перечисленных выше классов View теперь не будут создаваться динамически при помощи CRuntimeClass::CreateObject. Мы просто добавим объекты этих классов в декларации CPage1, CPage2 и CPage3. Для того, чтобы это стало возможным, необходимо сделать следующее:

  • Конструкторы и деструкторы всех View объявить public.
    Дело в том, что ClassWizard генерирует классы View с конструкторами и деструкторами, объявленными как protected, в расчете только на динамическое создание объектов View.
  • Реализовать в каждом классе View виртуальную функцию PostNcDestroy. Ее код будет одинаков для CDemoView_1, CDemoView_2, CDemoView_3:

    void CDemoView_1::PostNcDestroy()
    {
       if(m_pDocument != NULL)
          m_pDocument->RemoveView(this);
    }
    

    Для чего это нужно ?
    Дело в том, что все View в MFC по умолчанию уничтожают сами себя (удаляют объект из памяти при уничтожении окна View). Это происходит в функции CView::PostNcDestroy:

    // self destruction
    void CView::PostNcDestroy()
    {
       // default for views is to allocate them on the heap
       //  the default post-cleanup is to 'delete this'.
       //  never explicitly call 'delete' on a view
       delete this;
    }
    

    Функция CView::PostNcDestroy вызывается из обработчика сообщения WM_NCDESTROY CWnd::OnNcDestroy (это сделано именно в расчете на создание объектов View с помощью CRuntimeClass::CreateObject).

    Так как мы создаем объекты View "статически", то при уничтожении окна View (в момент закрытия окна документа) будет "вылетать" исключение.

    В Debug-конфигурации - это будет происходить в методе CDocument::UpdateFrameCounts из за невыполнения условия в макросе ASSERT:

    void CDocument::UpdateFrameCounts()
        // assumes 1 doc per frame
    {
       ...
          ASSERT(::IsWindow(pView->m_hWnd));
       ...
    
    

    В конфигурации Release исключение будет генерироваться библиотекой RTL при попытке удаления блока памяти не существующего в куче с помощью delete.

Теперь необходимо создать окна View в соответствующих страницах свойств.

В классы CPage1, CPage2, CPage3 добавим обработчики сообщения WM_CREATE, в которых будут создаваться View:

int CPage1::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   if(CBasePage::OnCreate(lpCreateStruct) == -1)
      return -1;

   if(!m_wndDemoView_1.Create(NULL, NULL, WS_CHILD | WS_VISIBLE,
                           CRect(0, 0, 0, 0), this, IDD_VIEW_1,
                           CChildSheet::GetCreateContext()))
   {
      TRACE0("Failed to create m_wndDemoView_1\n");
      return -1;
   }

   return 0;
}

Здесь функция CChildSheet::GetCreateContext возвращает указатель на структуру CCreateContext, передаваемый каркасом приложения MFC в виртуальную функцию CChildFrame::OnCreateClient (этот указатель потом передается в обработчик сообщения WM_CREATE окна View через поле lpCreateParams структуры LPCREATESTRUCT, и используется для связывания объекта View с объектом документа):

int CView::OnCreate(LPCREATESTRUCT lpcs)
{
   ...

   CCreateContext* pContext =
                    (CCreateContext*)lpcs->lpCreateParams;

   if (pContext != NULL && pContext->m_pCurrentDoc != NULL)
   {
      pContext->m_pCurrentDoc->AddView(this);

   ...
}

С этого момента можно опробовать приложение в действии.
Но ! На данный момент не будет происходить "растягивания" TAB-контрола, а также самих View по размерам клиентской области дочернего окна-рамки. Менять размеры будет только окно Property Sheet (его будет "растягивать" движок MFC). Для того, чтобы все заработало как надо, необходимо создать свой механизм ресайзинга окон.

Реализуем механизм позиционирования закладок

Как уже было сказано, изменив идентификатор окна Property Sheet, находящегося в дочернем MDI окне-рамке на AFX_IDW_PANE_FIRST мы добились автоматического изменения размеров лишь окна самого Property Sheet. Но расположенный в окне Property Sheet элемент управления TAB Control, его дочерние окна (страницы свойств), а также дочерние окна страниц свойств - View сами по себе перемещаться не будут.

Для начала заставим правильно позиционироваться View в пределах страниц свойств. Это сделать просто. Добавим в классы CPage1, CPage2, CPage2 обработчики WM_SIZE. Их код будет практически одинаков. Код обработчика в CPage1:

void CPage1::OnSize(UINT nType, int cx, int cy)
{
   CBasePage::OnSize(nType, cx, cy);

   if(::IsWindow(m_wndDemoView_1.GetSafeHwnd()))
   {
      m_wndDemoView_1.SetWindowPos(NULL, 0, 0, cx, cy,
                                   SWP_NOACTIVATE | SWP_NOMOVE |
                                   SWP_NOOWNERZORDER |
                                   SWP_NOZORDER);
   }
}

Теперь необходимо написать код позиционирования TAB Control-а и страниц свойств при изменении размеров окна Property Sheet.

С первого взгляда все выглядит достаточно просто. Можно было бы выполнять позиционирование TAB Control-а и активной страницы свойств в обработчике OnSize класса CChildSheet, но, к сожалению, Property Sheet также позиционирует активную страницу. Причем как при изменении размеров TAB Control-а, так и при переключении между закладками.
Отсюда следует, что действуя таким способом придется выполнять сабклассинг TAB Control-а в Property Sheet-е, а также обрабатывать уведомления (WM_NOTIFY) с кодом TCN_SELCHANGE этого элемента управления.

Выберем более простой путь.
Выше говорилось, что для обеспечения возможности унифицированного позиционирования страниц свойств был создан класс CBasePage, от которого наследовались все остальные классы страниц свойств. Воспользуемся этим.

Добавим в него обработчик сообщения WM_WINDOWPOSCHANGING:

void CBasePage::OnWindowPosChanging(WINDOWPOS FAR *lpwndpos)
{
   CPropertyPage::OnWindowPosChanging(lpwndpos);

   if(m_pParentSheet != NULL)
   {
      CRect rcThis;

      if(m_pParentSheet->GetPageRect(this, &rcThis))
      {
         lpwndpos->x      = rcThis.left;
         lpwndpos->y      = rcThis.top;
         lpwndpos->cx   = rcThis.Width();
         lpwndpos->cy   = rcThis.Height();
      }
   }
}

Теперь любая из наших страниц свойств при изменении ее размеров (независимо каким способом) будет "запрашивать" требуемые координаты у объекта класса CChildSheet посредством вызова CChildSheet::GetPageRect и корректировать свое размещение.

Реализуем метод CChildSheet::GetPageRect:

BOOL CChildSheet::GetPageRect(CBasePage *pPage, CRect *prcPage)
{
   ASSERT((pPage != NULL) && (prcPage != NULL));

   if(::IsWindow(pPage->GetSafeHwnd()) && m_nPagesYPos)
   {
      CRect rcWindow, rcClient;

      pPage->GetClientRect(&rcClient);
      pPage->GetWindowRect(&rcWindow);
      pPage->ScreenToClient(&rcWindow);

      GetClientRect(prcPage);

      prcPage->top = m_nPagesYPos;
      prcPage->left = rcWindow.left;
      prcPage->right += rcWindow.right - rcClient.right;
      prcPage->bottom += rcWindow.bottom - rcClient.bottom;

      return TRUE;
   }

   return FALSE;
}

Далее в классе CChildSheet реализуем обработчик сообщения WM_SIZE, в котором будет реализован механизм позиционирования закладок, активной страницы, а также окна View в активной странице. Ниже представлен код обработчика:

void CChildSheet::OnSize(UINT nType, int cx, int cy)
{
   CPropertySheet::OnSize(nType, cx, cy);

   CTabCtrl *pWndTab = GetTabControl();

   if(pWndTab != NULL)
   {
      CRect rcClientOld;

      pWndTab->GetClientRect(&rcClientOld);

      // меняем позицию окон

      pWndTab->SetWindowPos(NULL, 0, 0, cx, cy,
                            SWP_NOACTIVATE | SWP_NOMOVE |
                            SWP_NOOWNERZORDER | SWP_NOZORDER |
                            SWP_NOREDRAW);

      UpdateCurPagePos();

      // перерисовать измененные области

      CRect rcInvalid;

      rcInvalid.top = 0;
      rcInvalid.right = cx;
      rcInvalid.bottom = cy;
      rcInvalid.left = ((rcClientOld.right < rcInvalid.right) ?
                         rcClientOld.right - 1 :
                         rcInvalid.right) -
                        GetSystemMetrics(SM_CXEDGE);

      pWndTab->InvalidateRect(&rcInvalid);

      rcInvalid.left = 0;
      rcInvalid.right = cx;
      rcInvalid.bottom = cy;
      rcInvalid.top = ((rcClientOld.bottom < rcInvalid.bottom) ?
                        rcClientOld.bottom - 1 :
                        rcInvalid.bottom) -
                       GetSystemMetrics(SM_CYEDGE);

      pWndTab->InvalidateRect(&rcInvalid);
   }
}

Как видно, кроме позиционирования окон здесь производится также добавление некоторых областей TAB control-а в его регион обновления, т.к. иначе возникают некоторые проблемы с перерисовкой.

Передача фокуса ввода

Теперь приложение практически готово. Все отлично работает, кроме одной мелочи: при активизации окон документов (дочерних окон-рамок) фокус ввода не устанавливается в текущее View.

Для решения этой проблемы реализуем:

  • Обработчик сообщения WM_SETFOCUS в классе CChildFrame, в котором будем передавать фокус ввода на Property Sheet (который, кстати, тут же передаст фокус активной странице):

    void CChildFrame::OnSetFocus(CWnd *pOldWnd)
    {
       if(m_pViewActive == NULL)
       {
          if(::IsWindow(m_wndChildSheet.GetSafeHwnd()))
          {
             m_wndChildSheet.SetFocus();
             return;
          }
       }
    
       CMDIChildWnd::OnSetFocus(pOldWnd);
    }
    

  • В классах CPage1, CPage2, CPage3 реализуем обработчики сообщений WM_SETFOCUS, а также виртуальные функции OnSetActive в которых будем делать активным View, расположенное на данной странице. Конечно, эти функции можно было бы реализовать и в классе CBasePage, но в общем случае может понадобиться различная обработка передачи фокуса для разных страниц, поэтому напишем практически одинаковый код для каждого класса страницы свойств. Для CPage1 код будет выглядеть так:

    BOOL CPage1::OnSetActive()
    {
       if(::IsWindow(m_wndDemoView_1.GetSafeHwnd()))
          GetParentFrame()->SetActiveView(&m_wndDemoView_1);
    
       return CPropertyPage::OnSetActive();
    }
    
    void CPage1::OnSetFocus(CWnd *pOldWnd)
    {
       CBasePage::OnSetFocus(pOldWnd);
    
       if(::IsWindow(m_wndDemoView_1.GetSafeHwnd()))
          GetParentFrame()->SetActiveView(&m_wndDemoView_1);
    }
    
Наконец-то можно скомпилировать и запустить приложение.

Скриншоты того, что получилось, приведены ниже:



Рисунок 2.



Рисунок 3.

Итоги

Как видите, использовав только лишь стандартные компоненты, можно получить весьма быстро и просто получить достаточно продвинутый и удобный интерфейс.

Исходный код проекта можно скачать по ссылке: TabViewDemo.zip (52кб).

Автор статьи: Вахтуров Виктор.
Подготовка выпуска: Вахтуров Виктор.

Книги по C/C++
SmogDX - объектно-ориентированная графика для Windows (DirectX и Visual C++)
SmogDX - объектно-ориентированная графика для Windows (DirectX и Visual C++)

Автор: В. А. Дебелов, Ю. А. Ткачев

Книга посвящена вопросам программирования динамических графических приложений в среде MS Windows на базе суперсистемы DirectX фирмы Microsoft.

Авторы разработали и представили объектно-ориентированную оболочку для основных графических частей:
  • DirectDraw - двумерной динамической графики и
  • Direct3D - трехмерной динамической графики.

При написании книги авторы избрали конструктивный подход, они вместе с читателем строят новую систему SmogDX и при этом изучают базовое обеспечение - DirectX. Таким образом, читатель не только знакомится с системой SmogDX, но и изучает наиболее существенные функциональные средства DirectX. В связи с этим данную книгу можно рассматривать и как учебное пособие по введению в программирование на DirectDraw и Direct3D.

Книга заполняет нишу литературы и пособий на русском языке по применению DirectX, ориентирована на широкий круг программистов разного уровня подготовки: школьников, студентов, научных работников.

Страница книги на Озоне
Интерактивная компьютерная графика. Вводный курс на базе OpenGL, 2-е изд.
Интерактивная компьютерная графика. Вводный курс на базе OpenGL, 2-е изд.

Автор: Эдвард Энджел

Книга представляет собой вводный курс компьютерной графики, в котором основной упор сделан на вопросах прикладного программирования. Она включает описание структуры графических систем и обсуждение основных концепции формирования изображений трехмерных объектов и сцен. Рассматривается взаимодействие освещения и материалов, также приводятся основные сведения о методах тонирования освещенных поверхностей, принципах иерархической организации графических моделей и новых возможностях современных аппаратных графических средств. В книгу включены те разделы линейной алгебры и геометрии, которые необходимы для понимания основ компьютерной графики. Обсуждаются методы построения кривых и поверхностей, языковые модели, фракталы и системы частиц, а также методика применения графических средств для визуализации результатов научных расчетов.

Весь теоретический материал в книге иллюстрируется программами на OpenGL.

Книга адресована в основном студентам старших курсов и аспирантам первого года обучения, специализирующимся в области информатики и вычислительной техники, но будет также полезна и многим профессионалам.

Страница книги на Озоне
Рассылки и дискуссионные листы компьютерной тематики
Рассылки
C/C++ Вопрос-Ответ

Это - интерактивная рассылка !
Здесь Вы можете задать свой вопрос по программированию на C и C++, а также ответить на вопросы других подписчиков.


Программирование на JavaScript

Все аспекты программирования на JavaScript - нестандартные приемы, ОРИГИНАЛЬНЫЕ скрипты, авторские статьи и наработки. "JavaScript solutions" - в каждом выпуске готовый к применению ИНТЕРЕСНЫЙ скрипт (исходный код с комментариями).

Дискуссионные листы
Программирование. Форум !!!

Самый популярный дискуссионный лист по программированию на subscribe.ru, существующий с момента открытия сервиса дискуссионных листов !

Задайте здесь любой вопрос по программированию - и Вы получите ответ. Участвуйте в оживленных дискуссиях, обсуждайте интересные темы. Давайте ответы сами. Ведь это форум !!! Здесь просто интересно ! Присоединяйтесь !

Вебстроительство. Форум !!!

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

Поисковые системы. Форум !!!

Этот дискуссионный лист посвящен обсуждению поиковых систем, методов индексации сайтов поисковиками, способам оптимизации сайта под поисковые системы.

Хостинг. Обзоры и обсуждения платного и бесплатного хостинга.

Вы ищете хостинг (платный, бесплатный) ? Хотите спросить совета в выборе ? Можете обсудить это здесь. Поделитесь советом, если знаете. Или узнайте больше. Все о хостинге.


Всего доброго. До встречи в следующем номере.

Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.qandacpp
Архив рассылки
Отписаться
Вспомнить пароль

В избранное