Listbox mit mehreren Zeilen im String

Rene Albrecht

Erfahrenes Mitglied
Hi,

ich möchte in C per Win32-API eine Listbox mit Strings befüllen, die mehrere Zeilen haben. Pro Feld in der Listbox wird aber immer nur eine Zeile angezeigt. Wie bekomme ich das in den Griff?

Welche Möglichkeiten habe ich mit einer OwnerDraw-Listbox in C?

Gruß
René
 
Zuletzt bearbeitet:
Entweder zerlegt du deine Strings in Einzelzeilen oder du gehst wie schon gedacht über Ownerdraw. Stelle zusätzlich auf variable Höhe ein. Du bekommst dann für jedes Item ein WM_MEASUREITEM und ein WM_DRAWITEM. Die Textanzeige selbst lässt sich ja einfach per DrawText erledigen (das macht auch den Zeilenumbruch für dich).
 
Das ist eigentlich nicht wirklich schwer, die MSDN ist da die beste Hilfe.

Hier mal ein Beispiel. Das Darstellen ist etwas aufwendiger, ich habe noch die Selektion berücksichtigt. Das ist jetzt aus einem MFC-Projekt, das Handling bleibt aber gleich.

Code:
void CDlgPToolLayer::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{

  if ( lpMeasureItemStruct->CtlType == ODT_LISTBOX )
  {
    lpMeasureItemStruct->itemHeight = LAYER_ITEM_HEIGHT;
    return;
  }
	
  CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

void CDlgPToolLayer::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) 
{

  if ( lpDrawItemStruct->CtlType == ODT_LISTBOX )
  {
    FillRect( lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, GetSysColorBrush( COLOR_WINDOW ) );

    char      szBuffer[MAX_PATH];
    if ( lpDrawItemStruct->itemID != -1 )
    {
      if ( lpDrawItemStruct->itemState & ODS_SELECTED )
      {
        FillRect( lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, GetSysColorBrush( COLOR_HIGHLIGHT ) );
        SetTextColor( lpDrawItemStruct->hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
      }
      else
      {
        FillRect( lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, GetSysColorBrush( COLOR_WINDOW ) );
        SetTextColor( lpDrawItemStruct->hDC, GetSysColor( COLOR_WINDOWTEXT ) );
      }

      // Layer-Name
      char szBuffer[260];
      SetBkMode( lpDrawItemStruct->hDC, TRANSPARENT );
      m_ListLayers.GetText( lpDrawItemStruct->itemID, szBuffer );
      DrawText( lpDrawItemStruct->hDC, szBuffer, (int)strlen( szBuffer ), &lpDrawItemStruct->rcItem, DT_VCENTER | DT_SINGLELINE );

    }
    return;
  }
	
  CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);

}


F*gg Tabs. Erschossen und verboten gehören die.
 
Das mit der Listbox (owner draw) funktioniert jetzt grds.! Allerdings zeichnet er nicht nur das aktuelle Feld, sondern die komplette Listbox mit dem Wert neu, den ich hinzufügen will!

Wenn ich z.B. eine Listbox mit einem Wert
Code:
1
habe und ein ListBox_AddString(hwndList, "2") absetze sieht die Listbox anschließend so aus:
Code:
2
2
Die Message WM_DRAWITEM verwerte ich wie folgt:
Code:
case WM_DRAWITEM:
{
   DRAWITEMSTRUCT *ds = (DRAWITEMSTRUCT *)lParam;
   if(ds->CtlType == ODT_LISTBOX) 
   {
      SetTextColor (ds->hDC, RGB (0, 0, 0));
      DrawText(ds->hDC, (const char *)ds->itemData, lstrlen((const char *)ds->itemData), &(ds->rcItem), DT_LEFT);
   }
   return TRUE;
}
 
Äh, hast du den Text des Items (bzw. einen Pointer) in das ItemData gesteckt? Und dabei zufällig einen temporären Pointer benutzt? (sprich der Pointer zeigt nach dem Einsetzen des 1er Items auf den Text "1" und nach dem Einsetzen des 2er Items auf den Text "2")
Hol dir lieber über GetText den Text des Items.
 
Mein Problem liegt woanders. Hab mal kurz einen Quellcode gebastelt, der ein Window mit einem Edit-Feld, einem Button und einer Listbox erstellt. Bei Druck auf den Button wird der Inhalt des Edit-Felds ausgelesen und in die Listbox eingetragen.

Ich hab keine Ahnung, warum er alle Felder auf einen gleichen Wert setzt. :(

Code:
#include <windows.h>
#include <windowsx.h>

HWND hwndList = NULL;
HWND hwndEdit = NULL;
HWND hwndButton = NULL;
char edit_text[100];

LRESULT WINAPI MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}

		case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hwnd, &ps);
			EndPaint(hwnd, &ps);
			return 0;
		}

		case WM_DRAWITEM:
		{
			DRAWITEMSTRUCT *ds = (DRAWITEMSTRUCT *)lParam;
			if(ds->CtlType != ODT_LISTBOX) return 0;
			const char *txt = (const char *)ds->itemData;
			DrawText(ds->hDC, txt, lstrlen(txt), &(ds->rcItem), DT_LEFT);
			return 0;
		}

		case WM_KEYDOWN:
		{
			if(wParam == VK_ESCAPE) SendMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;

		case WM_COMMAND:
		{
			if ((HWND) lParam == hwndButton)
			{
				GetWindowText(hwndEdit, edit_text, 100);
				SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) edit_text);
			}
			return 0;
		}

		}
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

int PASCAL WinMain(HINSTANCE hI, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) 
{
	const char *ClassName="CTest";
	const char *WinName="Test";

	WNDCLASSEX wc = {
		sizeof(WNDCLASSEX), 
		CS_CLASSDC, 
		MsgProc, 
		0, 
		0, 
		hI, 
		NULL, 
		(HCURSOR)LoadCursor(NULL,IDC_ARROW), 
		(HBRUSH)GetStockObject(WHITE_BRUSH), 
		NULL, 
		ClassName, 
		NULL};
	if(!RegisterClassEx(&wc)) return -1;

	HWND hwnd = CreateWindow(ClassName, WinName, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, hI, NULL); 
	if(!hwnd) return -1;

	hwndList = CreateWindow("listbox", NULL, WS_CHILD|WS_VISIBLE|LBS_NOSEL|LBS_OWNERDRAWFIXED|LBS_NOTIFY|WS_VSCROLL|WS_BORDER, 2, 50, 288, 225, hwnd, (HMENU)1, hI, NULL);
	hwndEdit = CreateWindow ("edit", NULL, WS_CHILD|WS_VISIBLE|WS_BORDER,  5, 5, 140, 25, hwnd, (HMENU)1, hI, NULL) ;
	hwndButton = CreateWindow ("button", "LB_ADDSTRING", WS_CHILD|WS_VISIBLE,  150, 5, 139, 25, hwnd, (HMENU)1, hI, NULL) ;
	ShowWindow(hwnd, SW_SHOWDEFAULT);
	UpdateWindow(hwnd);

	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
     return 0;
}
 
Das Itemdata ist NICHT der Text. Das ist ein LPARAM-Parameter, den man an jedes Item packen kann.
Benutze das so:

char szTemp[260]; // Achtung, Länge prüfen

SendMessage( hwndList, LB_GETTEXT, ds->itemID, (LPARAM)&szTemp[0] );

Und benutze szTemp in DrawText.
 
Code:
case WM_DRAWITEM:
{
   DRAWITEMSTRUCT *ds = (DRAWITEMSTRUCT *)lParam;
   if(ds->CtlType != ODT_LISTBOX) return 0;
   char szTemp[260];
   SendMessage(hwndList, LB_GETTEXT, ds->itemID, (LPARAM)&szTemp[0]);
   DrawText(ds->hDC, szTemp, lstrlen(szTemp), &(ds->rcItem), DT_LEFT);
   return 0;
}
ergibt (angefangen mit test, test2, ... im Edit-Feld) das hier
 
Zurück