kvv's home

Форма входа

Категории раздела

Лекции [14]
Разное [4]
Статьи на тему WM, не попавшие в лекции по разным причинам.

Поиск

Каталог статей

Главная » Статьи » Программирование под Windows Mobile » Разное

Пишем свой FileOpenDlg

Интро
Рано или поздно, но любой WinCE программер (впрочем как и программер пишущий под другие ОС) сталкивается с необходимостью использования диалога для выбора файлов. И вот тут начинаются проблемы. Стандартный диалог открытия файла в WM, мягко говоря, неудобный. нет, если вам надо выбирать файлы, лежащие только в стандартных папках My Pictures, My Documents и т.д., то подойдет и стандартный диалог, но если нужно дать пользователю возможность выбора файлов из любой папки с переходом по подкаталогам, то стандартный диалог нам в этом не товарищ. Как выходят из этой ситуации? Очень просто - пишут свой диалог выбора файлов. =) Вот чем-то подобным мы сейчас и займемся.

Как там с этим у Windows?
Стандартный диалог вызывается следующим образом:

 TCHAR szFileName[MAX_PATH] ={0};
 OPENFILENAME ofn = {0};
 ofn.lStructSize = sizeof(ofn);
 ofn.hInstance = g_hInst;
 ofn.hwndOwner = hWnd;
 ofn.lpstrFile = szFileName;
 ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0\0");
 ofn.nMaxFile = MAX_PATH;
 ofn.lpstrInitialDir = L"Windows\\";
 GetOpenFileName(&ofn);

в результате получаем следующее:

красиво, но не удобно. =)
Попробуем создать что-то более правильное.

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

typedef struct tagFSD {
 DWORD cbSize;
 HWND hwndOwner;
 TCHAR szFileName[MAX_PATH];
 TCHAR szTitle[32];
 TCHAR szInitialDir[MAX_PATH];
 DWORD Flags;
} FILESYSTEMDLGW, *LPFILESYSTEMDLG;
    Рассмотрим парамеры структуры:
  • cbSize - размер структуры (для будущего использования)
  • hwndOwner - хендл родительского окна (нужен для правильной организации диалога)
  • szFileName - буфер полученного имени файла
  • szTitle - заголовок окна (для будущего использования)
  • szInitialDir - начальный каталог сканирования (для будущего использования, пока ищем от корня)
  • Flags - флаги всякие (для будущего использования)

Перейдем непосредствено к диалогу. Для первой версии диалога нам достаточно двух дочерних контролов EDIT и LISTVIEW. Поскольку у нас стоит условие не использования лишних файлов, то придется отказаться от файла ресурсов. Само окно будем создавать с помощью WinAPI функции DialogBoxIndirect, структуру описания диалога для которой можно собрать руками в памяти. С помощью этой функции можно создавать диалоги с любым количеством дочерних контролов, но мы опишем только родительское окно, а дочерние контролы создадим с помощью CreateWindow. Это проще, чем разбираться с достаточно сложной структурой описания контролов.
Проделаем это.
1. Создадим буфер в памяти
 HGLOBAL membuf = GlobalAlloc(GMEM_ZEROINIT,1024);
 if (!membuf) return FALSE;
2. Заполним буфер данными
 DLGTEMPLATE* dt = (DLGTEMPLATE*)GlobalLock(membuf);
 dt->cdit = 0;
 dt->style = WS_POPUP | DS_MODALFRAME | DS_SETFOREGROUND ; // window styles
 dt->cx = dt->cy = 200; // window size
 LPWORD p = (LPWORD)(dt+1);
 *p++=0; // end of data
 *p++=0;
3. Запустим диалог
 GlobalUnlock(membuf);
 BOOL bResult = FALSE; 
 if (IDOK == DialogBoxIndirect(GetModuleHandle(NULL),(LPDLGTEMPLATE)membuf,g_pFSData->hwndOwner,WndProc))
 {
 lstrcpy(Data->szFileName, g_szSelectedPath);
 bResult = TRUE;
 } 
 GlobalFree(membuf); 
 return bResult;

Теперь нужно разобраться с диалоговой функцией окна (у нас она зовется WndProc).
Берем станндартный шаблон диалоговой функции и добавляем реакцию на WM_INITDIALOG:
 // Create a Done button and size it. 
 SHINITDLGINFO shidi;
 shidi.dwMask = SHIDIM_FLAGS;
 shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
 shidi.hDlg = hDlg;
 SHInitDialog(&shidi);
 // Create controls
 g_hwndInfo = CreateWindow(L"STATIC",0,WS_CHILD | WS_VISIBLE | SS_LEFT | WS_BORDER,0,0,100,10,hDlg,0,GetModuleHandle(NULL),NULL);
 ::SetWindowText(g_hwndInfo,L"Select file");
 g_szCurrentPath[0] = 0;
 g_szSelectedPath[0] = 0;
 g_hwndList = CreateWindow(L"LISTBOX",0,WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | WS_BORDER,0,10,100,10,hDlg,0,GetModuleHandle(NULL),NULL);//::GetDlgItem(hDlg,8001);
 BuildFilesList(g_hwndList);

и на WM_SIZE:
 RECT rt;
 ::GetClientRect(g_hwndInfo,&rt);
 int nHeight = 16;
 ::GetClientRect(hDlg,&rt);
 ::SetWindowPos(g_hwndInfo,NULL,0,0,rt.right-rt.left,nHeight,SWP_NOZORDER);
 ::SetWindowPos(g_hwndList,NULL,0,nHeight,rt.right-rt.left,rt.bottom-rt.top-nHeight,SWP_NOZORDER);

Пытливый взгляд заметит неопознанную функцию BuildFilesList. Ага, как видно из названия, функция строит список файлов.
Поглядим, что она делает:
void BuildFilesList(HWND hItem, LPTSTR szInitDir = NULL)
{
 ::SendMessage(hItem,LB_RESETCONTENT,0,0);
 TCHAR szDir[MAX_PATH] = {0};
 lstrcpy(szDir,g_szCurrentPath);
 if (NULL != szInitDir)
 {
 lstrcat(szDir,szInitDir);
 } 
 if (szDir[lstrlen(szDir)] != L'\\')
 {
 lstrcat(szDir,L"\\"); 
 }
 lstrcpy(g_szCurrentPath,szDir);
 lstrcat(szDir,L"*.*");
 WIN32_FIND_DATA fd ={0};
 HANDLE hFind = FindFirstFile(szDir,&fd);
 if (INVALID_HANDLE_VALUE != hFind)
 {
 do 
 {
 TCHAR szFile[MAX_PATH] = {0};
 lstrcpy(szFile,fd.cFileName);
 if (0 != (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
 {
 ::SendMessage(hItem,LB_INSERTSTRING,0,(LPARAM)szFile);
 ::SendMessage(hItem,LB_SETITEMDATA,0,1);
 } else
 {
 ::SendMessage(hItem,LB_ADDSTRING,0,(LPARAM)szFile);
 int cnt = (int)::SendMessage(hItem,LB_GETCOUNT,0,0);
 if (cnt>0)::SendMessage(hItem,LB_SETITEMDATA,cnt-1,0);
 }
 }while (FindNextFile(hFind,&fd)>0);
 }
 FindClose(hFind);
 if (lstrcmp(g_szCurrentPath,L"\\"))
 {
 ::SendMessage(hItem,LB_INSERTSTRING,0,(LPARAM)L"..");
 ::SendMessage(hItem,LB_SETITEMDATA,0,2);
 }
 ::SendMessage(hItem,LB_SETCURSEL,0,0);
}
Вобщем-то ничего особенного, стандартный поиск файлов с заполнением ListBox. В будущем расширим эту функцию для поска только каталогов или поиска файлов по маске, пока же будет так, как есть.
Теперь осталось сделать навигацию по каталогам и вывести информацию о текущем файле.
Снова идем в WndProc и добавляем обработку событий ListBox
case WM_COMMAND:
 // select OK button
 if (LOWORD(wParam) == IDOK)
 {
 EndDialog(hDlg, LOWORD(wParam));
 return (INT_PTR)TRUE;
 }
 // select file by stylus 
 if (LBN_DBLCLK == HIWORD(wParam))
 {
 HWND hList = (HWND)lParam;
 TCHAR szFileName[MAX_PATH] = {0};
 int idx = (int)::SendMessage(hList,LB_GETCURSEL,0,0);
 ::SendMessage(hList,LB_GETTEXT,idx,(LPARAM)szFileName);
 int Data = ::SendMessage(hList,LB_GETITEMDATA,idx,0);
 if (Data == 2)
 {
 OneDirUp(g_szCurrentPath);
 BuildFilesList(hList);
 } else if (Data == 1) BuildFilesList(hList,szFileName);
 SetFileInfo(g_hwndInfo);
 }
 // select file by keyboard 
 if (LBN_SELCHANGE == HIWORD(wParam))
 {
 HWND hList = (HWND)lParam;
 TCHAR szFileName[MAX_PATH] = {0};
 int idx = (int)::SendMessage(hList,LB_GETCURSEL,0,0);
 ::SendMessage(hList,LB_GETTEXT,idx,(LPARAM)szFileName);
 lstrcpy(g_szSelectedPath,g_szCurrentPath);
 lstrcat(g_szSelectedPath,szFileName);
 SetFileInfo(g_hwndInfo);
 }
 break;
Текущий каталог мы храним в глобальной переменной g_szCurrentPath. Функция OneDirUp предназначена для перехода в родительский каталог:
void OneDirUp(LPTSTR szPath)
{
 int MaxCnt = lstrlen(szPath);
 TCHAR *p = szPath + MaxCnt-2;
 while ((--MaxCnt >0)&&(*p != L'\\'))p--;
 *p = 0;
}
Функция SetFileInfo заполняет StaticText информацией о выбранном файле/каталоге:
void SetFileInfo(HWND hInfo = NULL)
{
 TCHAR szFileInfo[300] = {0};
 HANDLE hFile = CreateFile(g_szSelectedPath,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
 DWORD dwSize = 0;
 DWORD dwAttr = GetFileAttributes(g_szSelectedPath);
 if (INVALID_HANDLE_VALUE != hFile)
 {
 BY_HANDLE_FILE_INFORMATION hfi = {0};
 if (GetFileInformationByHandle(hFile,&hfi))
 {
 dwSize = hfi.nFileSizeLow;
 } else
 {
 dwSize = GetFileSize(hFile,NULL);
 }
 }
 CloseHandle(hFile);
 TCHAR pref = L' ';
 if (dwSize>1000)
 {
 dwSize = dwSize / 1024;
 pref = L'K';
 }
 if (dwSize>1000)
 {
 dwSize = dwSize / 1024;
 pref = L'M';
 }
 TCHAR *p = g_szSelectedPath + lstrlen(g_szSelectedPath)-1;
 while ((p > g_szSelectedPath)&&(*p!=L'\\')) p--;
 p++;
 if (0 == (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
 {
 wsprintf(szFileInfo,L"%s %d%cb",p,dwSize,pref);
 } else
 { if (!lstrcmp(p,L".."))
 lstrcpy(szFileInfo,g_szCurrentPath);
 else
 wsprintf(szFileInfo,L"\\%s",p);
 }
 if (NULL != hInfo)
 ::SetWindowText(hInfo,szFileInfo);
 else
 ::MessageBox(0,szFileInfo,L"File info",MB_ICONINFORMATION | MB_OK);
}
Ну вот, простой диалог выбора файла готов. Теперь можно подумать над расширением функционала диалога. Для начала, выводить расширенную информацию об объектах.

продолжение следует...

Категория: Разное | Добавил: kvv (14.12.2007) | Автор: Владимир Кошелев E W
Просмотров: 10763 | Комментарии: 2 | Рейтинг: 5.0/2
Всего комментариев: 2
2 SashaTravel  
0
Всегда перед командировкой или отпуском встаёт вопрос покупки билетов на самолет или Ж\д билетов. Поделитесь кто и как покупает http://xn--80acmldkekdf7c.xn--p1ai/ - авиабилеты дешево или ж\д билеты? Покупаете ли вы http://xn--80acmldkekdf7c.xn--p1ai/ - билеты на самолет онлайн или пользуетесь кассами у метро?

1 XroM  
0
А вы не могли бы выложить проект с этим диалогом, и еще был бы рад увидеть продолжение статьи, спасибо.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Друзья сайта

  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Облако тэгов