KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Роджер Джек - Исчерпывающее руководство по написанию всплывающих подсказок

Роджер Джек - Исчерпывающее руководство по написанию всплывающих подсказок

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Роджер Джек, "Исчерпывающее руководство по написанию всплывающих подсказок" бесплатно, без регистрации.
Назад 1 ... 3 4 5 6 7 ... 9 Вперед
Перейти на страницу:

Рис.10. Диаграмма классов для примера использования элементов ToolTip

Класс CTitleTip представляет окно подсказки (см. рис.11). В статической переменной CTitleTip::m_pszWndClass хранится зарегистрированное имя класса окна. Имя хранится в статической переменной, потому что класс окна нужно зарегистрировать только один раз для всех экземпляров CTitleTip. CTitleTip::m_nItemIndex – это индекс строки в списке, для которой в данный момент выводится подсказка. Эта переменная может принимать значение константы CTitleTip::m_nNoIndex, если подсказка не выводится ни для одной из строк. CTitleTip::m_pListBox хранит указатель на родительское окно элемента TitleTip. Родительское окно должно быть элементом "список", чтобы я смог взять оттуда информацию для подсказки.

Рис.11. CTitleTip

/////////////////////////////////////////////////////////////////////////////

// CTitleTip window

class CTitleTip : public CWnd {

public:

 CTitleTip();

 virtual BOOL Create(CListBox* pParentWnd);

 virtual void Show(CRect DisplayRect, int nItemIndex);

 virtual void Hide();

// Overrides

 // ClassWizard generated virtual function overrides

 //{{AFX_VIRTUAL(CTitleTip)

 //}}AFX_VIRTUAL

 // Implementation

public:

 virtual ~CTitleTip();

protected:

 const int m_nNoIndex; // Пустой индекс

 static LPCSTR m_pszWndClass; // Имя зарегистрированного класса

 int m_nItemIndex; // Индекс строки, для которой показывается подсказка

 CListBox* m_pListBox; // Родительское окно

 BOOL IsListBoxOwnerDraw();

 // Generated message map functions

protected:

 //{{AFX_MSG(CTitleTip)

 afx_msg void OnPaint();

 //}}AFX_MSG

 DECLARE_MESSAGE_MAP()

};


/////////////////////////////////////////////////////////////////////////////

// TitleTip.cpp : implementation file //

#include "stdafx.h"

#include "TitleTip.h"


#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif


/////////////////////////////////////////////////////////////////////////////

// CTitleTip

LPCSTR CTitleTip::m_pszWndClass = NULL;

CTitleTip::CTitleTip() : m_nNoIndex(-1) {

 // Зарегистрировать класс окна, если он еще не зарегистрирован

 // другим экземпляром CTitleTip.

 if (m_pszWndClass == NULL) {

  m_pszWndClass = AfxRegisterWndClass(CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW);

 }

 m_nItemIndex = m_nNoIndex;

 m_pListBox = NULL;

}


CTitleTip::~CTitleTip() { }


BOOL CTitleTip::Create(CListBox* pParentWnd) {

 ASSERT_VALID(pParentWnd);

 m_pListBox = pParentWnd;

 // Не рисовать рамку для обычных элементов "список", так как

 // строки с пользовательской отрисовкой добавляют рамку автоматически.

 DWORD dwStyle = WS_POPUP;

 if (!IsListBoxOwnerDraw()) {

  dwStyle |= WS_BORDER;

 }

 return CreateEx(0, m_pszWndClass, NULL, dwStyle, 0, 0, 0, 0, pParentWnd->GetSafeHwnd(), NULL, NULL);

}


BOOL CTitleTip::IsListBoxOwnerDraw() {

 ASSERT_VALID(m_pListBox);

 DWORD dwStyle = m_pListBox->GetStyle();

 return (dwStyle & LBS_OWNERDRAWFIXED) || (dwStyle & LBS_OWNERDRAWVARIABLE);

}


void CTitleTip::Show(CRect DisplayRect, int nItemIndex) {

 ASSERT_VALID(m_pListBox);

 ASSERT(nItemIndex < m_pListBox->GetCount());

 ASSERT(nItemIndex >= 0);

 ASSERT(::IsWindow(m_hWnd));

 ASSERT(!DisplayRect.IsRectEmpty());

 // Пометить для обновления, если новая строка.

 if (m_nItemIndex != nItemIndex) {

  m_nItemIndex = nItemIndex;

  InvalidateRect(NULL);

 }

 // Установить позицию и видимость окна.

 CRect WindowRect;

 GetWindowRect(WindowRect);

 int nSWPFlags = SWP_SHOWWINDOW | SWP_NOACTIVATE;

 if (WindowRect == DisplayRect) {

  nSWPFlags |= SWP_NOMOVE | SWP_NOSIZE;

 }

 VERIFY(SetWindowPos(&wndTopMost, DisplayRect.left, DisplayRect.top, DisplayRect.Width(), DisplayRect.Height(), nSWPFlags));

}


void CTitleTip::Hide() {

 ASSERT(::IsWindow(m_hWnd));

 ShowWindow(SW_HIDE);

}


BEGIN_MESSAGE_MAP(CTitleTip, CWnd)

 //{{AFX_MSG_MAP(CTitleTip)

 ON_WM_PAINT()

 //}}AFX_MSG_MAP

END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////

// CTitleTip message handlers

void CTitleTip::OnPaint() {

 ASSERT(m_nItemIndex != m_nNoIndex);

 CPaintDC DC(this);

 int nSavedDC = DC.SaveDC();

 CRect ClientRect;

 GetClientRect(ClientRect);

 if (IsListBoxOwnerDraw()) {

  // Доверим рисование элементу "список".

  DRAWITEMSTRUCT DrawItemStruct;

  DrawItemStruct.CtlType = ODT_LISTBOX;

  DrawItemStruct.CtlID = m_pListBox->GetDlgCtrlID();

  DrawItemStruct.itemID = m_nItemIndex;

  DrawItemStruct.itemAction = ODA_DRAWENTIRE;

  DrawItemStruct.hwndItem = m_pListBox->GetSafeHwnd();

  DrawItemStruct.hDC = DC.GetSafeHdc();

  DrawItemStruct.rcItem = ClientRect;

  DrawItemStruct.itemData = m_pListBox->GetItemData(m_nItemIndex);

  DrawItemStruct.itemState = (m_pListBox->GetSel(m_nItemIndex) > 0 ? ODS_SELECTED : 0);

  if (m_pListBox->GetStyle() & LBS_MULTIPLESEL) {

   if (m_pListBox->GetCaretIndex() == m_nItemIndex) {

    DrawItemStruct.itemState |= ODS_FOCUS;

   }

  } else {

   DrawItemStruct.itemState |= ODS_FOCUS;

  }

  m_pListBox->DrawItem(&DrawItemStruct);

 } else {

  // Рисуем самостоятельно

  CFont* pFont = m_pListBox->GetFont();

  ASSERT_VALID(pFont);

  DC.SelectObject(pFont);

  COLORREF clrBackground = RGB(255, 255, 255);

  if (m_pListBox->GetSel(m_nItemIndex) > 0) {

   DC.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));

   clrBackground = ::GetSysColor(COLOR_HIGHLIGHT);

  }

  // Рисуем фон

  DC.FillSolidRect(ClientRect, clrBackground);

  // Рисуем текст строки

  CString strItem;

  m_pListBox->GetText(m_nItemIndex, strItem);

  ASSERT(!strItem.IsEmpty());

  DC.SetBkMode(TRANSPARENT);

  DC.TextOut(1, –1, strItem);

 }

 DC.RestoreDC(nSavedDC);

 // Не вызываем CWnd::OnPaint() для сообщений отрисовки

}

CTitleTip::CTitleTip регистрирует класс окна вызовом функции AfxRegisterWndClass и сохраняет имя класса в переменной CTitleTip::m_pszWndClass. Я использую функцию AfxRegisterWndClass, чтобы иметь возможность зарегистрировать класс окна с установленным стилем CS_SAVEBITS. Флаг CS_SAVEBITS используется для оптимизации – Windows сохраняет кусок окна, заслоненного элементом TitleTip, как картинку. В результате, этому окну не нужно посылать сообщение WM_PAINT, когда подсказка убирается с экрана. CTitleTip::Create создает подсказку в виде popup-окна. К окну подсказки рамка добавляется только если элемент "список" является обычным, так как Windows автоматически добавляет рамку к элементам "список" с пользовательской отрисовкой перед посылкой сообщения WM_DRAWITEM. Обратите внимание, что значение переменной CTitleTip::m_pszWndClass передается в качестве имени класса окна в функцию CWnd::CreateEx. CTitleTip::IsListBoxOwnerDraw возвращает TRUE, если родительский элемент "список" является элементом с пользовательской отрисовкой. Функция узнает об этом по стилю элемента "список".

Функция CTitleTip::Show отвечает за показ элемента TitleTip. Ее параметр DisplayRect указывает на координаты и размеры подсказки в клиентской системе координат родительского окна. Параметр nItemIndex указывает индекс отображаемой строки в списке. Я оптимизировал функцию, чтобы она только помечала для отрисовки и устанавливала координаты и размеры подсказки только если она изменилась. Для изменения размеров подсказки используется функция CWnd::SetWindowPos. В качестве ее первого параметра используется wndTopMost, чтобы окно подсказки располагалось поверх всех остальных окон. Чтобы предотвратить получение фокуса ввода этим окном (окну подсказки в любом случае не нужен клавиатурный ввод), используется флаг SWP_NOACTIVATE. Функция CTitleTip::Hide прячет TitleTip вызовом функции CWnd::ShowWindow с параметром SW_HIDE.

CTitleTip::OnPaint по-разному рисует подсказку в зависимости от вида элемента управления "список". Если родительский элемент "список" реализует пользовательскую отрисовку, функция создает и инициализирует структуру DrawItemStruct подобно тому, как это проделывает Windows перед отправкой сообщения WM_DRAWITEM. Разница лишь в том, что вместо того, чтобы установить поле hDC этой структуры равным хэндлу контекста устройства элемента "список", CTitleTip::OnPaint инициализирует это поле значением хэндла контекста устройства окна подсказки. После этого вызывается функция m_pListBox->DrawItem, которой передается адрес заполненной структуры DrawItemStruct. Результатом всех этих действий является то, что элемент "список" рисует одну из своих строк в окне подсказки. Очень умно! Вот в чем преимущество объектно-ориентированного программирования и хорошо продуманных интерфейсов. Элемент управления "список" не знает – или не хочет знать – где он рисует строку, он знает только, как ее нужно рисовать. CTitleTip не умеет рисовать строку списка с пользовательской отрисовкой, но он знает как инициализировать DrawItemStruct и вызвать CListBox::DrawItem. С другой стороны, если родительский список является обычным элементом "список", класс CTitleTip рисует все сам. К счастью, это не так сложно. Функция отрисовки получает нужный текст и шрифт от родительского элемента "список", устанавливает контекст устройства, заполняет фон и рисует текст.

Назад 1 ... 3 4 5 6 7 ... 9 Вперед
Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*