Рейтинговые книги
Читем онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 58 59 60 61 62 63 64 65 66 ... 156

CComPtr<IXMLDOMNode> spXMLChildNode;

hr = spXMLDOM->createNode(CComVariant(NODE_ELEMENT), CComBSTR("xmlchildnode"), NULL, &spXMLChildNode);

if (FAILED(hr)) throw "Unable to create 'xmlchildnode' XML node";

if (spXMLChildNode.p == NULL) throw "Unable to create 'xmlchildnode' XML node";

Если парсеру удалось создать новый узел, следующий шаг – разместить его в дереве XML. Метод IXMLDOMNode::appendChild() – как раз то, что нам нужно.

CComPtr<IXMLDOMNode> spInsertedNode;

hr = spXMLNode->appendChild(spXMLChildNode, &spInsertedNode);

if (FAILED(hr)) throw "Unable to move 'xmlchildnode' XML node";

if (spInsertedNode.p == NULL) throw "Unable to move 'xmlchildnode' XML node";

Если родительский узел принял только что созданный узел в качестве дочернего, он вернёт вам ещё один экземпляр IXMLDOMNode, который представляет новый узел. На самом деле, этот новый узел и узел, который вы передали в appendChild(), в точности совпадают. Тем не менее, проверка указателя на добавленный дочерний узел может быть полезной, так как в случае ошибки он примет значение NULL.

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

xml="fun"

Сделать это не сложно, но вам придётся переключиться с IXMLDOMNode на IXMLDOMElement, чтобы поработать с узлом как с элементом. На практике это означает, что вам придётся запросить у интерфейса IXMLDOMNode связанный с ним интерфейс IXMLDOMElement, а потом, получив его, вызвать IXMLDOMElement::setAttribute():

CComQIPtr<IXMLDOMElement> spXMLChildElement;

spXMLChildElement = spInsertedNode;

if (spXMLChildElement.p == NULL)

 throw "Unable to query for 'xmlchildnode' XML element interface";

hr = spXMLChildElement->setAttribute(CComBSTR(L"xml"), CComVariant(L"fun"));

if (FAILED(hr)) throw "Unable to insert new attribute";

Ну вот, мы модифицировали исходное XML-дерево, как нам этого хотелось. Приложение уже может сохранить документ на диск, но может сделать и что-нибудь ещё. Например, разыскать ещё один узел и отобразить на экране содержащийся в нём текст. Поскольку искать узлы мы уже умеем, перейдём прямо к извлечению данных.

Для извлечение данных предназначен метод IXMLDOMNode::get_nodeTypedValue(). Данные, которые содержит узел, можно задавать с использованием схемы типов фирмы Microsoft, поэтому вы без труда можете сохранять числа с плавающей точкой, целые числа, строки или любые другие поддерживаемые схемой данные. Тип данных задаётся с использованием атрибута dt:type, например:

<model dt:type="string">SL-2</model>

<year dt:type="int">1992</year>

Если некоторый узел содержит данные заданного типа, вы сможете извлечь их в нужном формате, используя get_nodeTypedValue().  Если тип не задан, по умолчанию он считается текстовым, и парсер вернёт вам VARIANT с содержащимся в нём BSTR. В нашем случае этого достаточно, поскольку узел, который мы ищем, является текстовым и действительно содержит строку. Если нужно, мы всегда сможем отконвертировать её в другое представление, используя средства типа atoi(). А пока просто извлечём строку и отобразим её.

CComVariant varValue(VT_EMPTY);

hr = spXMLNode->get_nodeTypedValue(&varValue);

if (FAILED(hr)) throw "Unable to retrieve 'xmltext' text";

if (varValue.vt == VT_BSTR) {

 // Display the results... since we're not using the

 // wide version of the STL, we need to convert the

 // BSTR to ANSI text for display...

 USES_CONVERSION;

 LPTSTR lpstrMsg = W2T(varValue.bstrVal);

 std::cout << lpstrMsg << std::endl;

} else {

 // Some error

 throw "Unable to retrieve 'xmltext' text";

}

Если нам удалось извлечь значение, связанное с узлом, и если оно оказалось именно того типа, который мы ожидаем (BSTR), мы выводим текст на экран. В противном случае просто выводится сообщение об ошибке. Но вы, в зависимости от ситуации, можете предпринять и другие действия.

Наша последняя задача – сохранить обновлённое XML-дерево на диск, что мы и делаем, используя IXMLDOMDocument::save():

hr = spXMLDOM->save(CComVariant("updatedxml.xml"));

if (FAILED(hr)) throw "Unable to save updated XML document";

Сохранив документ, программа выдаёт на экран короткое сообщение и завершается.

Эта демонстрационная программа вряд ли поразит ваше воображение. Вы могли бы сделать ещё очень много, но я надеюсь, что этот простой пример показал вам, как использовать MSXML в программах на языке C++. Сам по себе парсер – сложный продукт, и я настоятельно рекомендую вам использовать MSDN как справочное руководство по нему. Парсер предоставляет множество интерфейсов, каждый из которых обычно содержит большое количество методов. Несмотря на это, я широко использую парсер в своих проектах и теперь, поработав и поэкспериментировав с ним, нахожу его простым и удобным в использовании. Я надеюсь, что и вы найдёте ему, а также XML в целом, множество применений.

ВОПРОС-ОТВЕТ

Как разрешить перетаскивание окна за любую точку?

Автор: Алексей Кирюшкин

Демонстрационное приложение DragWin

Пример – приложение DragWin (диалоговое окошко, MFC) иллюстрирует два способа осуществить перемещение окна с захватом его не только за заголовок, но и за любую точку на клиентской области. Идея первого способа проста – при получении сообщения о перемещении мыши передвигаем наше окно в соответствии с новыми координатами. Второй способ поизящнее, и заключается в некотором "обмане" Windows, после которого она считает, что мышь находится над заголовоком окна, даже если реально это уже клиентсткая часть.

Способ 1

Реализован для главного окна приложения. Заключается в написании собственных обработчиков нажатия (WM_LBUTTONDOWN), перемещения (WM_MOUSEMOVE) и отпускания (WM_LBUTTONUP) левой кнопки мыши. Обработчики на данные события устанавливаются стандартным образом – через MFC ClassWizard.

void CDragWinDlg::OnLButtonDown(UINT nFlags, CPoint point) {

 // выставим флажок – пошло перетаскивание

 m_bMoveWindow = TRUE;

 // все сообщения от мыши - к нашему окну, независимо от координат

 // чтобы мышь не улетала с окна при быстром движении

 SetCapture();

 // сохраняем координаты окна

 GetWindowRect(m_RectDlg);

 // сохраняем положение мышки внутри окна программы

 ClientToScreen(&point);

 m_MouseInDlg = point - m_RectDlg.TopLeft();

 // меняем курсор, чтоб веселее было тащить

 m_hCursor = m_hCursorDown;

 ::SetCursor(m_hCursor);

 // вызываем обработчик по умолчанию

 CDialog::OnLButtonDown(nFlags, point);

}

void CDragWinDlg::OnMouseMove(UINT nFlags, CPoint point) {

 if (m_bMoveWindow) // надо тащить

 {

  // преобразуем координаты мыши в экранные

  // именно они нужны будут для SetWindowPos()

  ClientToScreen(&point);

  // двигаем окно в соответствии с новыми координатами мыши

  SetWindowPos(&wndTop, point.x - m_MouseInDlg.x, point.y - m_MouseInDlg.y,

   m_RectDlg.right - m_RectDlg.left, m_RectDlg.bottom - m_RectDlg.top,

   SWP_SHOWWINDOW);

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

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

  // обратное преобразование ScreenToClient(&point);

  // можно не вызывать

 }

 // вызываем обработчик по умолчанию

 CDialog::OnMouseMove(nFlags, point);

}

void CDragWinDlg::OnLButtonUp(UINT nFlags, CPoint point) {

 // перетаскивание закончилось

 m_bMoveWindow = FALSE;

 // "отпускаем" мышку

 ReleaseCapture();

 // меняем курсор на исходный

 m_hCursor = m_hCursorUp;

 // вызываем обработчик по умолчанию

 CDialog::OnLButtonUp(nFlags, point);

}

BOOL CDragWinDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) {

 // заменяем курсор на свой

 ::SetCursor(m_hCursor);

 return TRUE; // !!! было return CDialog::OnSetCursor(pWnd, nHitTest, message);

}

Замена курсора естесственно не является критичной для собственно перетаскивания, а добавлена исключительно для визуализации процесса захвата окошка.

Способ 2

Реализован для окна About этого же приложения. Заключается в замене обработчика события WM_NCHITTEST, которое информирует об области, над которой в данный момент находится мышка. Обработчик этого сообщения также можно добавить через MFC ClassWizard. Предварительно на закладке ClassInfo для класса CAboutDlg нужно установить для Message Filter значение Window.

Переписываем функцию – обработчик следующим образом:

UINT CAboutDlg::OnNcHitTest(CPoint point) {

 UINT ret = CDialog::OnNcHitTest(point);

 // если обработчик по умолчанию говорит нам что мышка

 // над клиентской областью окна, заменяем возвращаемое

 // значение на HTCAPTION – мышка над заголовком окна,

 // а за заголовок перемещать окно можно!

 if (ret == HTCLIENT) return HTCAPTION;

 return ret;

}

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

1 ... 58 59 60 61 62 63 64 65 66 ... 156
На этой странице вы можете бесплатно читать книгу Программирование на Visual C++. Архив рассылки - Алекс Jenter бесплатно.
Похожие на Программирование на Visual C++. Архив рассылки - Алекс Jenter книги

Оставить комментарий