[MFC] CDailogXML - диалог на основе XML-"шаблона"
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 10.01.05 12:50
Оценка: 92 (9)
Краткое описание.
Класс CDailogXML является потомком MFC-шного класса CDialog и предназначен для создания диалоговых окон на основе XML-файлов, описывающих как само диалоговое окно, так и расположенные на нем элементы управления. Каждому диалоговому окну соответствует один XML-файл следующей структуры:

<?xml version="1.0" encoding="Windows-1251"?>
<Dialog
    Style="0x80C800C0" ExStyle="0x00000000"
    Left="0" Top="0" Width="260" Height="90"
    Caption="Test Dialog"
    FontFace="Tahoma" FontSize="8">
    <Controls
        Count="8">
        <Control
            Class="Static"
            Style="0x50020202" ExStyle="0x00000000"
            Left="4" Top="6" Width="44" Height="12"
            Caption="Text edit:"
            ID="-1"/>
        <Control
            Class="Edit"
            Style="0x50030080" ExStyle="0x00000200"
            Left="50" Top="6" Width="206" Height="12"
            Caption=""
            ID="1001"/>
        ...
    </Controls>
</Dialog>

Использование класса CDialogXML в приложении аналогично CDialog — необходимо отнаследоваться от него, при необходимости перекрыв в потомке виртуальные методы OnInitDialog() и DoDataExchange(), после чего создать экземпляр объекта на стеке и вызвать метод DoModal():

class CTestDialog: public CDialogXML { ... };
...
void CMainFrame::OnDialogTest(void)
{
    CTestDialog dlgTest(this);
    if (dlgTest.DoModal() == IDOK) {
        // используем введенные пользователем данные
        ...
    }
}

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

Средства реализации и библиотеки:

Демонстрационный проект:

"Ключевые" файлы:

Детали реализации и советы по использованию.

Конструктор класса CDialogXML имеет вид:
CDialogXML(LPCTSTR pszDialogName, CWnd* pParentWnd = NULL);

Через параметр pszDialogName необходимо передать "имя" диалогового окна, определяющее имя XML-файла, на основании которого необходимо построить данный диалог. Заметим, что это имя не должно содержать расширения ".xml" и должно быть допустимым именем файла. Потомкам CDialogXML целесообразно передавать имя диалога в вызов конструктора базового класса:

CTestDialog::CTestDialog(CWnd* pParentWnd):
CDialogXML(_T("TestDialog"), pParentWnd)
{
...
}

Построение в памяти шаблона диалгового окна (то есть, инициализацию структур DLGTEMPLATE и DLGITEMTEMPLATE) выполняет публичный статический метод
static const DLGTEMPLATE* CreateDlgTemplate(LPCTSTR pszFileXML);

Он возвращает адрес области памяти, в которой находится полностью подготовленный шаблон. Через параметр pszFileXML необходимо передать полное имя XML-файла, описывающиего диалоговое окно. Если данный файл содержит ошибки не может быть "разобран" парсером, метод возвращает значение NULL.

Поскольку данный метод является статическим, им можно воспользоваться, даже если вы не хотите наследовать свои диалоги от класса CDialogXML:

class CMyDialog: public CDialog
{
public:
    CMyDialog(void);
...
};

...

CMyDialog::CMyDialog(void):
CDialog()    // вызываем защищенный конструктор по умолчанию
{
...
}

...

void CMainFrame::foo(void)
{
    CMyDialog dlgMy;
    DLGTEMPLATE* pDlgTemplate = CDialogXML::CreateDlgTemplate(_T("C:\\MyDialog.xml"));
    dlgMy.InitModalIndirect(pDlgTemplate);
    if (dlgMy.DoModal() == IDOK) {
    ...
    }
}

Путь, по которому экземпляры CDialogXML и его потомков ищут XML-файлы определяется виртуальным методом
virtual void GetXMLpath(CString& strDest);

"По умолчанию" этот путь конструируется следующим образом:
%AppData% \ AfxGetApp()->m_pszRegistryKey \ AfxGetAppName() \

Обратите внимание, что данная реализация GetXMLpath() предполагает, что в методе InitInstance() объекта-приложения был выполнен вызов
SetRegistryKey(_T("VendorName_Or_CompanyName"));

Метод GetXMLpath() может быть перекрыт, например, для реализации поддержки многоязычности в приложении:

class CLocDialogXML: public CDialogXML { ... };
...
void CLocDialogXML::GetXMLpath(CString& strDest)
{
    CDialogXML::GetXMLpath(strDest);
    strDest += _T(имя_текущего_языка_приложения);
    strDest += _T('\\');
}

Реализация перекрытого метода CDialogXML::DoModal() очень проста:
int CDialogXML::DoModal(void)
{
    CString strFileXML;

    ASSERT(m_lpszTemplateName == NULL);
    ASSERT(m_lpDialogTemplate == NULL);

    GetXMLpath(strFileXML);
    strFileXML += m_strDialogName + _T(".xml");
    m_lpDialogTemplate = CreateDlgTemplate(strFileXML);
    m_lpDialogInit = NULL;

    return (m_lpDialogTemplate != NULL ? CDialog::DoModal() : -1);
}

Заключительные замечания.

Для запуска демонстрационного приложения необходимо создать папку %AppData%\Elijah Zarezky\XMLDialogs и скопировать в нее файл TestDialog.xml из папки XMLDialogs\AppData прилагающегося архива.

Из STL в данном коде используется std::auto_ptr<>; кроме того, XML парсер использует std::ostream. Я использовал для компиляции несколько модифицированный вариант SGI STL 3.3, исходные тексты которого можно найти здесь; если при использовании STLport или "родной" STL VC++ у вас возникнут проблемы — comments and suggestions are welcome.
[ posted via RSDN@Home 1.1.4 beta 3 r241, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.