[C++] Owner drawn listbox: Two items highlighted on first focus (And smooth scrolling)

The main question is this, I have some code for an owner drawn listbox, almost everything is good with it, but for the ODA_FOCUS, when I change the background and the text color, items 0 and x are selected rather than x. If I select 0 and then some other number, or even x and then 0 and then x, it becomes unselected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
case WM_DRAWITEM: {
			
  TCHAR buffer[500]; //50*10
  int itemLength, yPos;
  //HRESULT hr;
  DRAWITEMSTRUCT* pdis = (LPDRAWITEMSTRUCT)lParam;
  //size_t cch;
  TEXTMETRIC tm;

  //If there aren't any items, skip this message
  if(pdis->itemID==-1){
    break;
  }

  //Draw the list items
			
  //Set the text and background color
  if(pdis->itemAction==ODA_FOCUS){
    SetTextColor(pdis->hDC, RGB(255,255,255));
    SetBkColor(pdis->hDC, RGB(51,94,168));
    FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(51,94,168)));
  }
  else{
    SetTextColor(pdis->hDC, RGB(0,0,0));
    SetBkColor(pdis->hDC, RGB(255,255,255));
    FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(255,255,255)));
  }
					
  //Draw the string
  //Get item string from listbox

  SendMessage(pdis->hwndItem, LB_GETTEXT, pdis->itemID, (LPARAM)buffer);
  GetTextMetrics(pdis->hDC, &tm);
  yPos=(pdis->rcItem.bottom+pdis->rcItem.top-tm.tmHeight+4)/2;
  itemLength=_tcslen(buffer); 

  RECT item;
  item.left=0;
  item.top=pdis->rcItem.top;
  item.right=pdis->rcItem.right;
  item.bottom=pdis->rcItem.bottom;
				
  //This allows multiple lines in one item.
  DrawTextEx(pdis->hDC, buffer, itemLength, &item, DT_CENTER|DT_END_ELLIPSIS, 0);
			
  if(pdis->itemState==ODS_DEFAULT){
    MessageBox(0,0,0,0);
  }
			
  if(pdis->itemState==ODS_SELECTED){
    //Draw the focus rectangle
    DrawFocusRect(pdis->hDC, &pdis->rcItem);
    UpdateWindow(glbWin.mainListbox);
  }
  return true;
}


Does anybody know why that gives the unwanted results?


Also, the listboxes scrolling is quite strange, when I scroll down, I can see the selected item scroll to the bottom before it's up one position. If it's how I'm writting my above code that causes it, how can I fix it? If it's not, then how can I write a smooth scroll like the listbox does? (I know how to write my own scroll code, it's the smooth part IDK about.)

Thanks for the help!
Okay, I updated and cleaned my code, and now it's working...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int itemLength;
TCHAR text[500];
				
//Get text and text length
SendMessage(pdis->hwndItem, LB_GETTEXT, pdis->itemID, (LPARAM)text);
itemLength=_tcslen(text);

//Where the new question comes form
if(pdis->itemAction==ODA_FOCUS && pdis->itemState & ODS_FOCUS){
  //Set text background and color
  SetTextColor(pdis->hDC, RGB(255,255,255));
  SetBkColor(pdis->hDC, RGB(51,94,168));
  FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(51,94,168)));
}
else{
  SetTextColor(pdis->hDC, RGB(0,0,0));
  SetBkColor(pdis->hDC, RGB(255,255,255));
  FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(255,255,255)));
}

//Draw Text
DrawTextEx(pdis->hDC, text, itemLength, &pdis->rcItem, DT_CENTER|DT_END_ELLIPSIS, 0);

if(pdis->itemState==ODS_FOCUS){
  DrawFocusRect(pdis->hDC, &pdis->rcItem);
}


My new question is this: Why does pdis->itemState & ODS_FOCUS work, but pdis->itemState==ODS_FOCUS doesn't? I'm a bit confused how that part works.

The smooth scroll issue is fixed as well, (Although the blue part disappears during the move) but I'd still like to know how to make one do that. My guess is that you use a loop and move it 1px at a time, but I'm not sure the proper way to pause between each move. Sleep()?
The itemState is used as a set of bitfields and can hold 11 (eleven) possible states.
So you need to use the & ( bit field selection) to pick out the bit you want to test.
Ahh, okay.

Do you have any ideas on creating a smooth scroller for a listbox? (I'm having trouble finding anything on manually scrolling a listbox.)
Sorry - not at this moment in time.
I have done an owner draw listbox - but the scrolling was done by windows - I just did the drawing of the items.
Whenever you get your code working, I'd appreciate it if you could post it here because I am going to try to make an owner-draw listbox in the near future and I have no idea how to do it.
I still have mine (but it is an MFC project)
That's where I'm at, I have it all drawn, but the scrolling is annoying me, so if anybody knows how to modify that part, it would be apppreciated.

@Lamblion:

Here's the code I'm using, keep in mind though that I have mine set up so I can have multiple heights depending on the list item, so you may also want to take a look at: http://msdn.microsoft.com/en-us/library/bb775148(v=vs.85).aspx#ownerdrawn_listbox which doesn't use some of the things you don't need if you're just going line by line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
long fnListSubClass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
	switch(msg){
		case WM_NCCALCSIZE: {
			
			CallWindowProc(listboxProc, hwnd, msg, wParam, lParam);
			
			RECT* pRect = (RECT*)lParam;
			pRect->left+=4;
			pRect->top+=23;
			pRect->bottom+=23;
			
			return 0;
		}
		case WM_NCPAINT: {

			HBITMAP brush;
			HBRUSH newBrush;
			HGDIOBJ oldBrush;
			HDC hdc;
			RECT rect;
			
			hdc=GetWindowDC(hwnd);
			
			GetClientRect(hwnd, &rect);
			SetRect(&rect, 0, 0, 4, rect.bottom);
			
			brush=LoadBitmap(GetModuleHandle(0), MAKEINTRESOURCE(IDB_BORDER));
			newBrush=CreatePatternBrush(brush);
			oldBrush=SelectObject(hdc, newBrush);

			FillRect(hdc, &rect, newBrush);
			
			SelectObject(hdc, oldBrush);
			DeleteObject(newBrush);
			DeleteObject(oldBrush);
			DeleteObject(brush);

			UpdateWindow(hwnd);
			ReleaseDC(hwnd, hdc);

			break;
		}
		
	}
	return CallWindowProc(listboxProc, hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
	
	switch(msg){
		case WM_MEASUREITEM: {

			int breaks=1; //Amount of line breaks; there will always be one.
			MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lParam;
			TEXTMETRIC tm;
			TCHAR buffer[500];

			//Get Text
			SendMessage(glbWin.mainListbox, LB_GETTEXT, pmis->itemID, (LPARAM)buffer);			

			//Calculate height of text
			GetTextMetrics(GetWindowDC(glbWin.mainListbox), &tm);

			//Scan for line breaks
			for(int i=0; i<500 && buffer[i]!='\0'; i++){
				if(buffer[i]=='\n'){
					breaks++;
				}
			}
			

			//Set height based on text line-height and number of line breaks
			pmis->itemHeight=(tm.tmHeight*breaks)+6;

			break;
		}
		case WM_DRAWITEM: {

			DRAWITEMSTRUCT* pdis = (LPDRAWITEMSTRUCT)lParam;
			//If there aren't any items, skip this message
			if(pdis->itemID==-1){
				break;
			}
				
			int itemLength;
			TCHAR text[500];
				
			//Get text and text length
			SendMessage(pdis->hwndItem, LB_GETTEXT, pdis->itemID, (LPARAM)text);
			itemLength=_tcslen(text);

			if(pdis->itemAction==ODA_FOCUS && pdis->itemState & ODS_FOCUS){
				//Set text background and color
				SetTextColor(pdis->hDC, RGB(255,255,255));
				SetBkColor(pdis->hDC, RGB(51,94,168));
				FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(51,94,168)));
			}
			else{
				SetTextColor(pdis->hDC, RGB(0,0,0));
				SetBkColor(pdis->hDC, RGB(255,255,255));
				FillRect(pdis->hDC, &pdis->rcItem, CreateSolidBrush(RGB(255,255,255)));
			}

			//Draw Text
			DrawTextEx(pdis->hDC, text, itemLength, &pdis->rcItem, DT_CENTER|DT_END_ELLIPSIS, 0);

			if(pdis->itemState==ODS_FOCUS){
				DrawFocusRect(pdis->hDC, &pdis->rcItem);
			}
	
			return true;
		}
		case WM_CREATE: {
			HFONT tahoma16=CreateFont(18,0,0,0,400,FALSE,FALSE,FALSE,ANSI_CHARSET,
				OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,
				_T("Tahoma"));
			HWND listbox;
			RECT rect;

			GetClientRect(hwnd, &rect);
			listbox=CreateWindowExA(0, "listbox", "", 
				WS_CHILD|LBS_NOTIFY|WS_VSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWVARIABLE|LBS_HASSTRINGS, 
				rect.right-200, 0, 200, rect.bottom, hwnd, 0, GetModuleHandle(0), 0);
				listboxProc=(WNDPROC)SetWindowLong(listbox, GWL_WNDPROC, (long)fnListSubClass);
				SendMessage(listbox, WM_SETFONT, (WPARAM)tahoma16, true);
						
			TCHAR msg[]=_T("Testing\nTesting");
			TCHAR msg2[]=_T("Testing\nTesting\nTesting Testing Testing Testing Testing Testing");
			for(int i=0; i<20; i++){
				SendMessage(listbox, LB_ADDSTRING, (WPARAM)i, LPARAM(msg));
				SendMessage(listbox, LB_ADDSTRING, (WPARAM)++i, LPARAM(msg2));
			}

			SetWindowPos(listbox, 0, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

			ShowWindow(listbox, SW_SHOW);

/*Complete Window procedure*/
//... 
Thanks a bunch. I'll be inspecting your code when I start mine.
Topic archived. No new replies allowed.