Le forum (ô combien francophone) des utilisateurs de Powerbuilder.
hello,
Est-ce que quelqu'un aurait connaissance d'un wrapper du treelist de rasdlg.dll pour powerbuilder (classic), ou un équivalent ?
Initialement je pensais utiliser un treedatawindow, mais ça ne me permet pas d'avoir des niveaux variables de groupes, et l'utilisation du treeview ne me permet pas d'avoir plusieurs colonnes en même temps.
Dernière modification par seki (10-06-2014 15:07:10)
Hors ligne
L'idée, c'est de faire ça dans un visual external object :
Hors ligne
Il faut que les colonnes soient redimensionnables ou pas ?
Techniquement en bidouillant un peu on avoir une treeview avec des niveaux différents.
J'avais donné quelques indications un peu à l'arrache dans ce sujet, à voir si c'est applicable à ce qui est souhaité.
Hors ligne
On essaie avec Xlat de faire marcher en PB un bout de code C qui donne le résultat de la capture d'écran : une ListView avec un TreeView qui sont déjà associés dans un contrôle de classe TreeList dans la dll rasdlg.dll. Les colonnes sont redimensionnables et le nombre de fils est variable par branche de l'arbre.
Il suffit de quelques lignes de code C (je peux les publier au besoin) pour faire fonctionner l'exemple et on n'arrive pas à faire de même avec une classe visuelle externe (alors que ça fonctionne trivialement pour on contrôle calendrier de comctl32 par exemple)
Au pire on va essayer de wrapper l'ensemble des appels API dans une classe visuelle mais ce serait tellement plus simple de dire à PB d'utiliser la classe de la dll directement...
Hors ligne
je veux bien voir le code pour ma culture personnelle même si je ne maîtrise pas du tout le sujet.
Hors ligne
L'exemple est une appli windows minimale.
Elle définit des messages utilisables avec le contrôle qui semble basé en même temps sur un TreeView et sur un ListView mais qui n'est pas documenté.
Les points remarquables sont :
- l'appel à une fonction FileIconInit() + Shell_GetImageLists() qui permet de charger une ImageList dont les images sont ensuite réutilisées par le TreeList (cette partie est facultative, si on ne veut pas associer d'image aux items, il suffit de ne pas utiliser LVIF_IMAGE dans lvi.mask plus bas dans le code) un truc bizarre c'est que FileIconInit() est appelé directement par son ordinal dans shell32.dll, MSDN explique que c'est ma façon de faire car le prototype de la fonction a été oublié dans les .h de visual c...
- la boucle d'insertion des colonnes par l'envoi du message TLM_INSERTCOLUMN au contrôle
- la boucle d'insertion des items que j'ai placé dans une fonction pour plus facilement insérer des sous niveaux : insertion d'un item dans l'arbre avec TLM_INSERTITEM et le reste des colonnes avec TLM_SETITEM
Je crois que ce qui peut merder avec PB c'est que lors de la création du contrôle avec CreateWindow() on lui passe comme hInstance le handle de rasdlg.dll qu'on a chargé avec LoadLibrary() alors que la plupart du temps quand on crée un contrôle on lui passe le handle de l'application courante... Enfin c'est juste une intuition.
//https://groups.google.com/d/msg/comp.os.ms-windows.programmer.win32/3Usnxmwo3j4/9j2wFX0PogYJ // compiler avec // cl treetest.cpp /LINK comctl32.lib shell32.lib user32.lib // avec VC express 2010 le /link pose problème, compiler simplement avec // cl treetest.cpp comctl32.lib shell32.lib user32.lib #define UNICODE #define _UNICODE #include <windows.h> #include <tchar.h> #include <commctrl.h> #pragma comment (lib, "comctl32") #include <shlobj.h> // Shell_GetImageLists // Based on TreeView & ListView #define TLM_INSERTITEM WM_USER + 1 #define TLM_DELETEITEM WM_USER + 2 #define TLM_DELETEALLITEMS WM_USER + 3 #define TLM_GETITEM WM_USER + 4 #define TLM_SETITEM WM_USER + 5 #define TLM_GETITEMCOUNT WM_USER + 6 #define TLM_GETNEXTITEM WM_USER + 7 #define TLM_EXPAND WM_USER + 8 #define TLM_SETIMAGELIST WM_USER + 9 #define TLM_GETIMAGELIST WM_USER + 10 #define TLM_INSERTCOLUMN WM_USER + 11 #define TLM_DELETECOLUMN WM_USER + 12 #define TLM_SELECTITEM WM_USER + 13 #define TLM_REDRAWWINDOW WM_USER + 14 #define TLM_ISEXPANDED WM_USER + 15 #define TLM_GETCOLUMNWIDTH WM_USER + 16 #define TLM_SETCOLUMNWIDTH WM_USER + 17 typedef struct tagTL_INSERTSTRUCT { PVOID hParent; PVOID hInsertAfter; LVITEM *plvitem; } TL_INSERTSTRUCT; HINSTANCE hInst; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void fillItems(HWND list, PVOID hParent, int level, int count, BOOL bNext); int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { hInst = hInstance; WNDCLASSEX wcex = { sizeof(WNDCLASSEX), 0, WndProc, 0, 0, hInst, LoadIcon(NULL,IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("TreeListDemo"), NULL, }; if(!RegisterClassEx(&wcex)) return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK); //InitCommonControls(); int nX = (GetSystemMetrics(SM_CXSCREEN) - 860) / 2, nY =(GetSystemMetrics(SM_CYSCREEN) - 600) / 2; HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("TestTreeList"), WS_OVERLAPPEDWINDOW, nX, nY, 860, 600, NULL, NULL, hInst, NULL); if(!hWnd) return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK); ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); MSG msg; while(GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } PVOID pInsert; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ static HWND hWndTL, hWndLV; switch (message){ case WM_CREATE:{ BOOL bRet; HIMAGELIST hImageList; HMODULE hLib = LoadLibrary(TEXT("rasdlg.dll")); hWndTL = CreateWindow( TEXT("TreeList"), TEXT("blah"), WS_VISIBLE |WS_CHILD, 0, 0, 430, 300, hWnd, 0, hLib, NULL ); hWndLV = GetWindow(hWndTL, GW_CHILD); // BOOL FileIconInit( _In_ BOOL fRestoreCache ); //FileIconInit is not included in a header file. You must call it directly from Shell32.dll, using ordinal 660. typedef BOOL (WINAPI*PFII) (BOOL bRestoreCache); HINSTANCE hInstShell32 = LoadLibrary(TEXT("shell32.dll")); if (hInstShell32){ PFII pFII = (PFII)GetProcAddress(hInstShell32, (LPCSTR)MAKEINTRESOURCE(660)); HRESULT hr; if (pFII != NULL) hr = pFII(TRUE); FreeLibrary(hInstShell32); } Shell_GetImageLists(&hImageList, &hImageList); HIMAGELIST hOldIL = (HIMAGELIST)SendMessage(hWndTL,TLM_SETIMAGELIST, 0, (LPARAM)hImageList); //fill the columns LVCOLUMN col={0}; col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH ; col.fmt = LVCFMT_LEFT; col.cx = 170; TCHAR sColumnText[20]; for (int i = 0; i<5; i++){ wsprintf(sColumnText, TEXT("Column %d"), i); col.pszText = sColumnText; i = SendMessage(hWndTL, TLM_INSERTCOLUMN, (WPARAM)i, (LPARAM)&col); } //insert 10 items in the list fillItems(hWndTL, NULL, 0, 10, TRUE); return 0; } break; case WM_SIZE:{ MoveWindow(hWndTL, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); MoveWindow(hWndLV, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); return 0; } break; case WM_DESTROY:{ PostQuitMessage(0); return 0; } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } void fillItems(HWND list, PVOID hParent, int level, int count, BOOL bNext){ //insert some items in the parent BOOL bRet; TCHAR sItemText[50]; TCHAR sLevelText[50]; PVOID pItem, pSubItem; TL_INSERTSTRUCT tlis={0}; LVITEM lvi={0}; sLevelText[0] = 0; for (int i=0; i<level; i++){ wcscat(sLevelText, TEXT("sub")); wcscat(sLevelText, i<count-1?TEXT("-"):TEXT("")); } for (int i = 0; i<count; i++){ tlis.hInsertAfter = TVI_LAST; tlis.hParent = hParent; lvi.mask = LVIF_TEXT | LVIF_IMAGE; wsprintf(sItemText, TEXT("%sItem %02d - Col 0"), sLevelText, i+1); lvi.pszText = sItemText; lvi.iImage = i; tlis.plvitem = &lvi; pItem = (PVOID)SendMessage(list, TLM_INSERTITEM, 0, (LPARAM)&tlis); lvi.iItem = (int) pItem; for (int j = 1; j<=5; j++){ lvi.iSubItem = j; wsprintf(sItemText, TEXT("%sItem %02d - Col %d"), sLevelText, i+1, j); lvi.pszText =sItemText; bRet = SendMessage(list, TLM_SETITEM, 0, (LPARAM)&lvi); } //inject some children ? if(bNext){ if(level == 0 && i < 5) fillItems(list, pItem, level+1, 5, i==2 ? TRUE : FALSE); if(level == 1 && i>1 && i<3) fillItems(list, pItem, level+1, 3, FALSE); } } }
Hors ligne
seki a écrit:
Je crois que ce qui peut merder avec PB c'est que lors de la création du contrôle avec CreateWindow() on lui passe comme hInstance le handle de rasdlg.dll qu'on a chargé avec LoadLibrary() alors que la plupart du temps quand on crée un contrôle on lui passe le handle de l'application courante... Enfin c'est juste une intuition.
Je confirme après avoir révisé mes bases de win32 et fait quelques expériences : le problème vient de la déclaration de la classe TreeList dans la dll rasdlg où RegisterClass() n'utilise pas le style CS_GLOBALCLASS. Ce style est nécessaire pourqu'un contrôle soit utilisable à l'extérieur de son module (= de la dll). Sinon il ne sera pas utilisable par une autre application. En gros ce contrôle n'est pas prévu pour être utilisé en dehors de rasdlg.dll, mais le problème est contourné par un hack du code C qui utilise le handle de la dll comme hInstance (un handle d'instance est équivalent à un handle de module). Par contre PB ne sait pas faire ça et n'arrive pas à utiliser ce contrôle non global directement via une classe external visual.
Hors ligne
Épilogue de l'histoire : comme on ne pouvait pas réutiliser le contrôle MS directement et qu'on avait besoin de pouvoir afficher des données dans un arbre capable d'afficher des colonnes mutiples, et que la datawindow treeview est tout de même assez pourrie (notamment quand on n'a pas le même nombre de niveaux de feuilles dans chaque groupe), et bien on a développé notre propre TreeList
Bon, je n'ai pas réinventé la roue, j'ai repris du code existant qui implémente un contrôle Win32 hybride entre un ListView et un TreeView, j'en ai fait une dll et j'ai wrappé un useroject visuel autour
Le code est dispo sur mon GitHub (avec des contributions d'xlat), actuellement pour PB10.5/11.5 et + (version unicode) mais j'essaie de refaire une version ansi pour PB 9. Comme ce n'est pas une extension PBNI, la version non-unicode devrait pouvoir s'utiliser avec des versions précédentes (je sais que certains contributeurs ici se traînent des vieilleries genre PB6... )
Hors ligne
merci beaucoup
je testerai ça si j'en ai besoin :D
mais là départ pour le nord
Hors ligne