tab control und linker
-
Ich beschäftige mich zur zeit das erste mal mit tap controls. bei meinem ersten gehversuch kommt allerdings schon folgender fehler. Kann mir vielleicht einer einen verständlichen codeausschnitt schicken, indem ersichtlich wird was zu tun ist????
hier mein code:
HWND CreateTabControl(HINSTANCE hInstance) { /*get the client window*/ RECT rect; TCITEM tc; GetClientRect (hWindow, &rect); /*make a new instance of a common dialog initialsation structure*/ INITCOMMONCONTROLSEX cc; memset (&cc, 0 , sizeof (INITCOMMONCONTROLSEX)); cc.dwSize = sizeof (INITCOMMONCONTROLSEX); cc.dwICC = ICC_TAB_CLASSES | ICC_WIN95_CLASSES; /*ensure that the common dialog dll is loaded*/ InitCommonControlsEx (&cc); /*create Child window*/ hTc = CreateWindowEx (NULL, WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, rect.right, rect.bottom, hWindow, NULL, hGlobInstance, NULL); if (hTc == NULL) { MessageBox (hWindow, "Kann Reiter nicht erstellen", "Abacus Calculator V.1.0", MB_OK | MB_ICONINFORMATION); } return hTc; }
und hier meine fehler:
--------------------Configuration: Abacus - Win32 Debug--------------------
Compiling...
main.cpp
D:\Programme\Microsoft Visual Studio\MyProjects\Abacus\main.cpp(99) : warning C4101: 'tc' : unreferenced local variable
D:\Programme\Microsoft Visual Studio\MyProjects\Abacus\main.cpp(249) : warning C4715: 'MessageHandler' : not all control paths return a value
Linking...
main.obj : error LNK2001: unresolved external symbol __imp__InitCommonControlsEx@4
Debug/Abacus.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.Abacus.exe - 2 error(s), 2 warning(s)
schonmal danke für eure hilfe
-
Hast du auch die Lib für die CommonControls gelink?
-
ja das habe ich.... muss ich ja auch sonst würde er ja COMMONCONTROLSEX und die funktionen der common controls nicht erkennen und es würden noch mehr fehler entstehen. Initialisieren tue ich sie ja auch. Aber es hat wohl irgendwas in der Richtung damit zu tun...
-
Original erstellt von Parapiler:
ja das habe ich.... muss ich ja auch sonst würde er ja COMMONCONTROLSEX und die funktionen der common controls nicht erkennen und es würden noch mehr fehler entstehen.Diese Folgerung ist falsch.
Ob der Compiler die Namen erkennt, und ob der Linker die zugehörigen Symbole auflösen kann, hat nichts miteinander zu tun.
Prüf bitte genau, ob du comctl32.lib linkst.
-
Ja ich habe grade noch einmal nachgeschaut die ist eingebunden. Wird sie ja standartmäßig. Sie steht aber drin. Ich kann sie ja über nen pragma comment nochmal einbinden aber das wird nix bringen.
Andere Vorschläge? Kann eventuell jemand mir mal sagen wie man so ne Registerkarte macht? Dann kann ich eventuell auch selber nachvollziehen was ich falsch mache
-
hast du dich vielleicht verguckt und da steht nur comdlg32.dll drin?
-
ja ich hatte mich wirklich verguckt aber wie mache ich denn jetzt meine Registerkarte?! was ist denn dieses TCITEM? kann mir das einer mal erklären?! Ich kapier das in der MSDN nicht, weiss nur dass ich mit CreateWindowEx das Steuerelement erstellen muss, das TCITEM eine Struktur ist und man an das Steuerelement mit SendMessage Nachrichten Senden muss, wobei man als wParam den Index des Elementes und als lParam die Adresse der Struktur angibt. Aber wie funktioniert das jetzt genau?!
-
Bsp. Source:
TC_ITEM tci; tci.mask=TCIF_TEXT; tci.pszText="Registerkarte 1"; SendMessage(hWndTab, TCM_INSERTITEM, 0, (LPARAM) (const LPTCITEM) &tci); tci.pszText="Registerkarte 2; SendMessage(hWndTab, TCM_INSERTITEM, 0, (LPARAM) (const LPTCITEM) &tci);
über WM_NOTIFY bekommst du dann eine TCN_SELCHANGE Benachrichtigung, wenn eine Registerkarte gewechselt wird. Dann solltest du einen anderen Dialog (mit Sreuerelementflag) auf dem TabControl anzeigen.
-
da kommen bei mir mehrere Fragen auf:
1. Du legst ja nur eine TCITEM struktur an, schreibst aber in das zweite Element für jede Karte einen String. Normalerweise müsste der erste Eintrag dann doch übrerschrieben werden oder? müsste man nicht eigendlich einen array machen?!
2. Was ist eine WM_NOTIFY nachricht?!
3. Wieso gibst du bei SendMessage als wParam 0 an?
4. was ist TCIF_TEXT
-
OK, ich geh mal davon aus, dass du die MSDN nicht installiert hast.
1. Nein, man muss kein Array anlegen, weil sich das TabControl (TC) den Inhalt aus der Strucktur "nimmt" und ihn intern speichert. Damit es dies tut wird die Nachricht TCM_INSERTITEM an das TC gesendet. Wie der Name schon sagt soll also ein Item eingefügt werden. TCM steht für Tab Control Message. Da die Daten nun vom TC kopiert wurden kann man sie überschreiben um noch eine Nachricht an das TC zu senden, z.B. für eine zweite Registerkarte.
(Siehe MSDN: http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/tab/messages/tcm_insertitem.asp?frame=true))2. WM_NOTIFY ist eine ganz normale Nachricht, wie z.B. WM_PAINT. Viele Commoncontrols senden WM_NOTIFY um das Elternfenster über verschiedene Dinge zu benachrichtigen. Was genau passiert ist teilt das Control in einer Struktur mit, deren Zeiger in lParam von WM_NOTIFY steht. (siehe MSDN: http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/tab/notifications/tcn_selchange.asp?frame=true))
3. aus MSDN
lResult = SendMessage( // returns LRESULT in lResult (HWND) hWndControl, // handle to destination control (UINT) TCM_INSERTITEM, // message ID (WPARAM) wParam, // = (WPARAM) (int) iItem; (LPARAM) lParam // = const (LPARAM) (LPTCITEM) pitem; );
Wie man sieht gibt wParam den Index an, an dem das neue Item eingefügt werden soll. Im Beispiel immer als erstes Item (Index 0). (siehe erster Link)
4. TCIF_TEXT setzt irgenein Bit im Attribut mask. Dieses gesetzte Bit signaliesiert, dass das Attribut pszText gültig ist.
(siehe MSDN: http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/tab/structures/tcitem.asp?frame=true))Alles klar?
[edit]Code formatiert hoffentlich funzen die Links noch[/edit]
[ Dieser Beitrag wurde am 03.06.2003 um 12:46 Uhr von D@niel $chumann editiert. ]
-
ja das hat sehr viele Fragen beantwortet.... allerdings habe ich mir die TCITEM struktur mal angeschaut. Du nutzt ja nur 2 elemente was ist denn mit dem rest?!
-
Du must natürlich nur die Attribute benutzen, die du brauchst:
- dwState -> wird in TCM_INSERTITEM ingnoriert
- dwStateMask -> ebenso
- lpReserved1 -> wird nicht benutzt
- lpReserved2 -> ebenso
- cchTextMax -> wird nur genutzt wenn Informationen vom TC geholt werden
- iImage -> für ein Bildchen (Optional) muss vorher mit dem Flag TCIF_IMAGE in mask gültig gemacht werden
- lParam -> brauchen wir im Bsp. nicht, nimmt irgendeinen 4-Byte-Wert auf, z.B. Zeiger, ID, etc..., der für jede Reguisterkarte gespeichert wird
[ Dieser Beitrag wurde am 03.06.2003 um 13:24 Uhr von D@niel $chumann editiert. ]
-
ich habe jetzt mal nur einen Reiter erstellt aber alles was man sieht ist ein großes graues steuerelement, ohne den angegebenen string, und sonst ist auch nix da....woran liegt das?!
-
Zeig mal deinen Code. Ich habe es eben sicherheitshalber mal so probiert und es ging:
InitCommonControls(); TCITEM tci; tci.mask=TCIF_TEXT; tci.pszText="Registerkarte 1"; SendMessage(CreateWindowEx(NULL, WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 200, 200, hwnd, 0, ((LPCREATESTRUCT)lParam)->hInstance, NULL), TCM_INSERTITEM, 0, (LPARAM) (const LPTCITEM) &tci);
-
HWND CreateTabControl(HINSTANCE hInstance) { /*get the client window*/ RECT rect; TCITEM tc; memset (&tc, 0, sizeof (TCITEM)); GetClientRect (hWindow, &rect); /*make a new instance of a common dialog initialsation structure*/ INITCOMMONCONTROLSEX cc; memset (&cc, 0 , sizeof (INITCOMMONCONTROLSEX)); cc.dwSize = sizeof (INITCOMMONCONTROLSEX); cc.dwICC = ICC_TAB_CLASSES | ICC_WIN95_CLASSES; /*ensure that the common dialog dll is loaded*/ InitCommonControlsEx (&cc); /*create Child window*/ hTc = CreateWindowEx (NULL, WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 10, 300, 100, 100, hWindow, NULL, hGlobInstance, NULL); if (hTc == NULL) { MessageBox (hWindow, "Kann Reiter nicht erstellen", "Abacus Calculator V.1.0", MB_OK | MB_ICONINFORMATION); return 0; } else { tc.mask=TCIF_TEXT; tc.pszText = "Allgemein"; int z = SendMessage (hWindow, TCM_INSERTITEM, 0, (LPARAM) (TCITEM*) &tc); tc.pszText ="wissenschaftlich"; int x = SendMessage (hWindow, TCM_INSERTITEM, 1, (LPARAM) (TCITEM*) &tc); return hTc; } } obwohl eine sache macht mich stutzig: normalerweise kommt ja die fensterklasse in "" also "BUTTON" oder "EDIT" oder "LISTBOX".... wenn ich WS_TABCONTROL in "" setze klappt gar nix mehr...
-
Das mit dem Klassenname ist schon richtig. In der Headerdatei CommCtrl.h steht volgendes:
#define WC_TABCONTROL "SysTabControl32"
es wird dort also vom Preprozessor ein String eingefügt.
Aber die beiden Nachrichten:
int z = SendMessage (hWindow, TCM_INSERTITEM, 0, (LPARAM) (TCITEM*) &tc); tc.pszText ="wissenschaftlich"; int x = SendMessage (hWindow, TCM_INSERTITEM, 1, (LPARAM) (TCITEM*) &tc);
schickst du an das falsche Fenster. Du darfst sie nicht dem Elternfenster schcken, das kann damit nichts anfangen. Die Nachrichten mit den entsprechenden Parametern versteht nur das Steuerelement. Ersetz in den beiden Zeilen einfach hWindow durch hTc und es wird funktionieren.
[ Dieser Beitrag wurde am 03.06.2003 um 23:17 Uhr von D@niel $chumann editiert. ]
-
cool das funktioniert jetzt ja wirklich... wie kann ich denn nun die Steuerelemente in die Registerkarten eintragen? also mit dem hwnd geht das ja net hat ja nicht jede karte nen eigenes hwnd was man als elternfenster angeben könnte. wie wird das denn gemanged?
-
Jetzt erstellst du ein paar Dialoge. Für jede Registerkarte einen. Den Dialogen gibst du keinen Rand und machst sie Untergeordnet (rechte Maustaste auf den Dialog->Eigenschaften->Formate->Stil = "Untergeordnet" und ->Rand = "Kein").
Ich hoffe du hast VC++ und hast schon mal mit Dialogen bzw. dem Resourceneditor gearbeitet.
Immer wenn jetzt eine Registerkarte gewechselt wird (du bekommst eine Benachrichtung, siehe MSDN) musst du den entsprechenden Dialog anzeigen und den alten verstecken. Du kannst entweder beim Start alle Dialoge vorladen oder lädst ihn erst wenn er auch gebraucht wird und hältst ihn dann im Speicher, falls er nochmal gebraucht wird, oder wie auch immer. Als Parent bekommen die Dialoge das TC.Du musst das natürlich nicht über Dialoge machen, ist aber wohl am einfachsten bzw. am wenigsten umständlich ;).
-
ja ich habe vc++ , das heisst ich kann die Dialoge im Resourceneditor erstellen. Damit kenne ich mich auch ganz gut aus. Aber ich muss doch sicher dann irgendwo die Dialoge zuordnen oder? oder geht das wieder über Messages?!
-
Ich hab das mal so gelöst:
//innerhalb der WndProc des Elternfensters (in diesem Fall ein Dialog) INT_PTR CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static HWND dialog[4]; static unsigned char visible=0; static bool autoanswer=false; switch(message){ case WM_INITDIALOG: dialog[0]=0; dialog[1]=0; dialog[2]=0; dialog[3]=0; //------------ Code -------------- NMHDR nmhdr; nmhdr.code=TCN_SELCHANGE; nmhdr.hwndFrom=0; nmhdr.idFrom=0; SendMessage(hwnd, WM_NOTIFY, 0, (long)&nmhdr); return 1; //------------ Code -------------- case WM_NOTIFY: switch(LPNMHDR(lParam)->code){ case TCN_SELCHANGE: DWORD dwSelected; dwSelected=SendDlgItemMessage(hwnd,IDC_TAB1,TCM_GETCURSEL,0,0); if(dwSelected==-1)break; if(dialog[visible])ShowWindow(dialog[visible], SW_HIDE); if(!dialog[dwSelected]){ switch(dwSelected){ case 0: dialog[0]=CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DialogProc1, 0); break; case 1: dialog[1]=CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG2), hwnd, DialogProc2, 0); break; case 2: dialog[2]=CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG3), hwnd, DialogProc3, 0); break; case 3: dialog[3]=CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DIALOG4), hwnd, DialogProc4, 0); break; } RECT r, r2; GetWindowRect(GetDlgItem(hwnd, IDC_TAB1), &r); GetWindowRect(hwnd, &r2); r2.bottom=r.bottom-r2.bottom; r2.left=r.left-r2.left; r2.right=r.right-r2.right; r2.top=r.top-r2.top; GetWindowRect(dialog[dwSelected], &r); MoveWindow(dialog[dwSelected], r2.left, r2.top, r.right-r.left, r.bottom-r.top, true); } visible=dwSelected; ShowWindow(dialog[visible], SW_SHOW); break; } return 1; //------------ Code -------------- //...
Ich hoffe ich hab keinen wichtigen Code rausgenommen. Müsste aber so funktionieren. Ist übrigens für 4 Registerkarten. In der Resourcendatei sind die 4 Dialoge definiert (IDD_DIALOG1 ... IDD_DIALOG4). Natürlich brauchst du auch noch für jeden Dialog eine WndProc.