15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Скрещиваем элемент управления Edit и кнопку просмотра


Автор: PJ Arends

Sample Image - FileEditCtrl.jpg

Описание

Обычно, для того, чтобы позволить пользователю выбрать файл и директорию, создаётся два элемента управления: Edit и кнопка просмотра, которая вызывает обычный диалог выбора файла. Здесь же представлен класс, совмещающий эти два контрола.

Возможности

  1. Элемент управления наследуется от класса CEdit. Все функции-члены CEdit естевственно доступны в CFileEditCtrl. Создавать его можно со всеми возможными стилями ES_*. Команды EM_* и сообщения EN_* работают точно так же как и в обычном контроле Edit.
  2. Элипсы кнопки рисуются в неклиентской области контрола. Кнопку можно размещать как справа так и слева от текстового блока.
  3. Элемент управления имеет свои собственные DDX_FileEditCtrl и DDV_FileEditCtrl функции обмена данными. Настройка контрола довольно проста.
  4. Используя функцию Create() , можно создать данный элемент управления в любом окне, т.е. не только в диалоге или форме.
  5. Основная цель контрола - это просмотр списка выбираемых файлов или директорий. Когда открывается CFileDialog или SHBrowseForFolder то текущей становится та директория, которая введа в данном контроле.
  6. Элемент управления позволяет использовать относительные пути, т.е. пользователи могут вводить "..\..\любая папка" и контрол вернёт абсолютный путь относительно текущей рабочей директории. Если ввести "." , то получим текущую рабочую директорию.
  7. Элемент управления поддерживает технологию Drag and Drop файлов и директорий. Для этого его достаточно создать с расширенным стилем WS_EX_ACCEPTFILES.
  8. Размеры контрола можно изменять. Кнопка всегда сохраняет свою высоту относительно общей высоты элемента управления.
  9. При нажатии на кнопку, элемент управления посылает сообщение WM_NOTIFY родительскому окну, давая возможность родительскому окну закрыть SHBrowseForFolder или CFileDialog . Комбинация <Ctrl><.> равносильна нажатию на кнопку.

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

Чтобы ключить данный элемент управления в своё приложение, добавьте в проект файлы FileEditCtrl.h и FileEditCtrl.cpp. Затем рекомендуется добавить следующие строки как ресурсы в таблицу строк с идентификаторами FEC_IDS_*.

// FEC_IDS_ALLFILES будет определён в resource.h если будет находиться
// в ресурсе таблицы строк
#if !defined FEC_IDS_ALLFILES
    #define FEC_NORESOURCESTRINGS so this class knows how to handle these strings
    #define FEC_IDS_ALLFILES        _T"All Files (*.*)|*.*||")
    #define FEC_IDS_BUTTONTIP       _T("Browse")
    #define FEC_IDS_FILEDIALOGTITLE _T("Browse for File")
    #define FEC_IDS_SEPERATOR       _T(";")
    #define FEC_IDS_NOFILE          _T("Enter an existing file.")
    #define FEC_IDS_NOTEXIST        _T("%s does not exist.")
    #define FEC_IDS_NOTFILE         _T("%s is not a file.")
    #define FEC_IDS_NOTFOLDER       _T("%s is not a folder.")
    #define FEC_IDS_OKBUTTON        _T("OK")
#endif

Чтобы использовать контрол в диалоговом окне с установками поумолчанию, создайте улемент управления edit в шаблоне диалога, добавьте переменную CString в класс диалога и в DoDataExchange() добавьте функции DDX_FileEditCtrl() и DDV_FileEditCtrl(). В данном примере используется: для контрола edit идентификатор IDC_EDIT1 и CString m_String.

void CFileEditDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CFileEditDlg)
    //}}AFX_DATA_MAP
    DDX_FileEditCtrl(pDX, IDC_EDIT1, m_String, FEC_FOLDER | 
                                      FEC_TRAILINGSLASH | FEC_BUTTONTIP);
    DDV_FileEditCtrl(pDX, IDC_EDIT1);
    ...
}

Поскольку эти функции не поддерживаются класс визардом, то они должны быть помещены за пределами блока AFX_DATA_MAP.

Если Вам потребуется разместить несколько таких контролов в диалоге (например для выбора нескольких файлов), то добавьте переменную CFileEditCtrl в Ваш диалоговый класс. В DoDataExchange() добавьте вторую функцию DDX_FileEditCtrl(). Вот как это будет выглядеть:

void CFileEditDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CFileEditDlg)
    //}}AFX_DATA_MAP
    ...
    DDX_FileEditCtrl(pDX, IDC_EDIT2, m_FileEditCtrl, 
               FEC_FILE | FEC_BUTTONLEFT | FEC_BUTTONTIP | FEC_CLIENTTIP);
    DDV_FileEditCtrl(pDX, IDC_EDIT2);
}

Затем в OnInitDialog(), получаем указатель на структуры OPENFILENAME либо BROWSEINFO, и устанавливаем их соответствующим образом. В демо-проекте установлена m_FileEditCtrl для обеспечения выбора нескольких файлов.

BOOL CFileEditDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    ...
    // Изменяем контрол для выбора нескольких файлов
    if (OPENFILENAME *ofn = m_FileEditCtrl.GetOpenFileName())
    {
        ofn->Flags |= OFN_ALLOWMULTISELECT;
        buffer = new TCHAR[2000];
        ::ZeroMemory(buffer, 2000);
        ofn->lpstrFile = buffer;
        ofn->nMaxFile = 2000;
    }
    m_FileEditCtrl.SetClientTipText("Files");
    return TRUE;  // возвращаем TRUE, если на контроле не установлен фокус
}

Для получения имён файлов из контрола, используем функции-члены GetStartPosition() и GetNextPathName(). В демо-проекте это сделано в функции InitInstance(), а TRACE позволяет просмотреть, какой файл был выбран пользователем.

BOOL CFileEditApp::InitInstance()
{
    ...
    CFileEditDlg dlg;
    m_pMainWnd = &dlg
    int nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: Place code here to handle when the dialog is
        //  dismissed with OK
        int n = 1;
        TRACE ("\nFiles entered :\n");
        POSITION pos = dlg.m_FileEditCtrl.GetStartPosition();
        while (pos)
        {
            CString x = dlg.m_FileEditCtrl.GetNextPathName(pos);
            TRACE ("%03d - %s\n",n, x);
            n++;
        }
        TRACE ("Directory entered :\n  %s\n\n",dlg.m_String);
    }
    ...
    return FALSE;
}

Элемент управления посылает сообщение WM_NOTIFY с уведомляющим кодом FEC_NM_POSTBROWSE  после того как диалог вернёт управление и текст в окне контрола обновится. Указатель NMHDR* указывает на структуру FEC_NOTIFY , но параметр LRESULT будет игнорироваться.

typedef struct tagFEC_NOTIFY {
    NMHDR hdr;
    CFileEditCtrl* pFEC;                    // указатель на контрол, который
                                            // отсылает уведомляющее сообщение
    tagFEC_NOTIFY (CFileEditCtrl *FEC, UINT code);
} FEC_NOTIFY;

#define FEC_NM_PREBROWSE  1                 // уведомляющий код, отправляемый
                                            // до появления диалога
#define FEC_NM_POSTBROWSE 2                 // уведомляющий код, отправляемый
                                            // после появления диалога

 

Постоянное обновление данного контрола доступно сдесь.