Поставим задачу расширения дизайна этих компонентов. Мы не будем менять их код. Мы создадим на форме три кнопки, DriveComboBox, FileListBox сделаем невидимыми, в заменим их
ComboBox и ListBox компонентами соответсвенно.
Сначала проиницилизируем ComboBox:
| #define USER_PATH DriveComboBox1->Items->Count |
Стандартные папки:
| AnsiString SystemDir, WindowsDir, TempDir; AnsiString MyDocumentsFolder ,Desktop; ... char specfolder[MAX_PATH]; GetSystemDirectory(specfolder,100); SystemDir = specfolder; GetWindowsDirectory(specfolder,100); WindowsDir=specfolder; TempDir = WindowsDir+"\\Temp"; SHGetSpecialFolderPath(Handle, specfolder, 0x0005, false); MyDocumentsFolder = specfolder; SHGetSpecialFolderPath(Handle, specfolder, 0x0000, false); Desktop = specfolder; |
Теперь мы знаем местоположение и буквы дисков компьютера. Осталось заполнить полученными данными ComboBox.
Напишем специальную структуру, и создадим массив на её базе:
| enum TPathType {ptDestkop, ptNetworkDrive , ptHardDisk, ptCdrom, ptFloppy, ptDisk, ptMyDocuments, ptWinSystem, ptWindows, ptWinTemp, ptUserDefined }; struct TPath { TPathType PathType; //Тип пути AnsiString DefDir; //Каталог пути AnsiString Name; //Имя пути }StdPath[40]; |
Для определения типа пути по его каталогу и размещения полученных данных в массиве напишем функцию:
| void AddUserPath(AnsiString DefDir, AnsiString Name, int PathID) { StdPath[PathID].Name = Name; StdPath[PathID].DefDir = DefDir; Form1->ComboBox1->Items->Add(DefDir+" "+Name+""); if(DefDir.Length()==2) { switch(GetDriveType(DefDir.c_str())) { case DRIVE_CDROM: StdPath[PathID].PathType = ptCdrom; return; case DRIVE_REMOTE: StdPath[PathID].PathType = ptNetworkDrive; return; case DRIVE_FIXED: StdPath[PathID].PathType = ptHardDisk; return; case DRIVE_REMOVABLE: StdPath[PathID].PathType = ptFloppy; return; default: StdPath[PathID].PathType = ptDisk; return; } } if(DefDir==MyDocumentsFolder) { StdPath[PathID].PathType = ptMyDocuments; return; } if(DefDir==SystemDir) { StdPath[PathID].PathType = ptWinSystem; return; } if(DefDir==WindowsDir) { StdPath[PathID].PathType = ptWindows; return; } if(DefDir==TempDir) { StdPath[PathID].PathType = ptWinTemp; return; } if(DefDir==Desktop) { StdPath[PathID].PathType = ptDestkop; return; } StdPath[PathID].PathType = ptUserDefined; } |
Благодаря функции GetDriveType мы узнаём тип данного диска.
Теперь мы можем проинициализировать ComboBox:
| for(int i=0; i<DriveComboBox1->Items->Count; i++) { AddUserPath(DriveComboBox1->Items->Strings[i].SubString(1,2),DriveComboBox1->Items->Strings[i].c_str()+3,i); } AddUserPath(SystemDir, "Системная папка", USER_PATH); AddUserPath(WindowsDir, "Папка Windows", USER_PATH+1); AddUserPath(TempDir, "Временные файлы", USER_PATH+2); AddUserPath(MyDocumentsFolder, "Мои документы", USER_PATH+3); AddUserPath(Desktop, "Рабочий стол", USER_PATH+4); |
Для каждого типа пути нужна своя картинка. Проинициализируем её.
Мы разместим все картинки в папочке Interface. Каждому типу пути поставим в соответствие свою картинку:
| AnsiString PathIconNames[] = { "Destkop.bmp", "NetDisk.bmp", "HardDisk.bmp", "CDDisk.bmp", "Floppy.bmp", "Disk.bmp", "Mydocs.bmp", "Winsys.bmp", "Windows.bmp", "WinTemp.bmp", "User.bmp" }; Graphics::TBitmap* PathIcons[ptUserDefined+1]; |
Проинициализируем их:
| for(int i=0; i<ptUserDefined+1; i++) { PathIcons[i] = new Graphics::TBitmap(); PathIcons[i]->LoadFromFile("interface\\" + PathIconNames[i]); PathIcons[i]->Transparent = true; } |
И удалим:
| void __fastcall TForm1::FormDestroy(TObject *Sender) { for(int i=0; i<ptUserDefined+1; i++) { delete PathIcons[i]; } } |
Теперь самое интересное - отрисовка элементов ComboBox. Для этого свойство Style надо установить в csOwnerDrawFixed (только отрисовка), выбрать ItemHeight по размерам картинок (у меня было значение 24 для картинок 16 на 16), а событие OnDrawItem заполнить следующим кодом:
| void __fastcall TForm1::ComboBox1DrawItem(TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State) { ComboBox1->Canvas->FillRect(Rect); ComboBox1->Canvas->Draw(Rect.Left+4,Rect.Top+4, PathIcons[StdPath[Index].PathType]); if(StdPath[Index].PathType>=ptNetworkDrive && StdPath[Index].PathType<=ptDisk) { ComboBox1->Canvas->TextOut(Rect.Left+48, Rect.Top+6, StdPath[Index].Name); ComboBox1->Canvas->TextOut(Rect.Left+28, Rect.Top+6, StdPath[Index].DefDir); } else { ComboBox1->Canvas->TextOut(Rect.Left+28, Rect.Top+6, StdPath[Index].Name); } } |
Половина дела сделанa. Теперь на очереди FileListBox.
Так как картинки из этого компонента нам не подходят, а TIcon не может менять размеры значка, мы опять всё пишем руками

Сначала напишем функцию SetPath. Онабудет вызываться при изменении текущей папки.
| void SetPath(AnsiString NewPath) { if(DirectoryExists(NewPath)) { Path = NewPath; Form1->FileListBox1->Directory=Path; if(Form1->DirectoryListBox1->Directory!=Path) Form1->DirectoryListBox1->Directory=Path; Form1->ListBox1->Clear(); Form1->ListBox1->Items->AddStrings(Form1->FileListBox1->Items); } } |
В конструктор добавится следующая строка: SetPath(MyDocumentsFolder); Теперь при стрте приложения текущей папкой будет "мои документы".
Наполнится кодом OnChange ComboBox'а:
| void __fastcall TForm1::ComboBox1Change(TObject *Sender) { SetPath(StdPath[ComboBox1->ItemIndex].DefDir); } |
Теперь осталось написать отрисовку элемента в ListBox. Установим Для этого свойство Style надо установить в lbOwnerDrawVariable (отрисовка и подготовка к отрисовке). Выполним подготовительные действия для значков: установим папку со значками и их размер:
| int IconSize=16; AnsiString IconsDir; ... IconsDir = ExtractFilePath(Application->ExeName)+"\\interface\\files16\\"; |
Сама отрисовка:
| void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index, TRect &Rect, TOwnerDrawState State) { HICON hTypeIcon; ListBox1->Canvas->Brush->Style = bsSolid; ListBox1->Canvas->Rectangle(Rect); ListBox1->Canvas->Brush->Style = bsClear; ListBox1->Canvas->TextOut(Rect.Left+IconSize+4,Rect.Top+Rect.Height()/2-10,ListBox1->Items->Strings[Index]); if(FileExists(IconsDir+AnsiLowerCase(ExtractFileExt(ListBox1->Items->Strings[Index]).SubString(2,3))+".ico")) { //Если есть значок для этого типа файлов грузим его, если нет то грузим default.ico. hTypeIcon = LoadImage(NULL, (IconsDir+AnsiLowerCase(ExtractFileExt(ListBox1->Items->Strings[Index]).SubString(2,3))+".ico").c_str(), IMAGE_ICON, IconSize, IconSize, LR_LOADFROMFILE); } else { hTypeIcon = LoadImage(NULL, "interface\\files16\\default.ico", IMAGE_ICON, IconSize, IconSize, LR_LOADFROMFILE); } DrawIconEx(ListBox1->Canvas->Handle, Rect.Left+2, Rect.Top+2, hTypeIcon, IconSize,IconSize, 0, NULL, DI_NORMAL); DestroyIcon(hTypeIcon); } //--------------------------------------------------------------------------- void __fastcall TForm1::ListBox1MeasureItem(TWinControl *Control, int Index, int &Height) { //Подготока к отрисовке ListBox1->Canvas->Pen->Color = 0xFAFAFA; if(ListBox1->ItemIndex == Index) { //Если этот элемент текущий ListBox1->Canvas->Brush->Color=clNavy; } else { ListBox1->Canvas->Brush->Color=clWhite; } Height = IconSize+4; //Высота элемента } |
Мы использовали функцию LoadImage. Это универсальная функция загрузки иконок, курсоров, битмапов из ресурса. Если использовать флаг LR_LOADFROMFILE, загрузка происходит из файла. Первыц параметр - Имя ресурса или путь к файлу. Второй параметр - тип картинки. Третий и четвёртый - размеры.
И, наконец, последний штрих, выбор размера значков. Для этого нам нужны три кнопки с заговками 16, 32 и 48
| void __fastcall TForm1::Button1Click(TObject *Sender) { IconSize=16; ListBox1->Repaint(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { IconSize=32; ListBox1->Repaint(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button3Click(TObject *Sender) { IconSize=48; ListBox1->Repaint(); } |
Файл-броузер готов!