Thread: Problem with Scroll Bar

  1. #1
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584

    Problem with Scroll Bar

    I was just working with the scroll bar functions for a horizontal scroll bar (the functions are SetScrollInfo and GetScrollInfo). I thought I figured it out and I got it to scroll, but I think there is a problem with the WM_PAINT message. Can you take a look at it?

    Also, can somebody please explain the 'max' and 'min' macros? They are used in this example from petzold so I wrote my own scroll test with it, but I don't really know what they do. Here's the code:
    Code:
    /*---------------------------------------------------
    	Example of GetScrollInfo and SetScrollInfo for
    	the scroll bar of the created window
    ---------------------------------------------------*/
    
    #include <windows.h>
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				   PSTR szCmdLine, int nCmdShow)
    {
    	static TCHAR szAppName[] = TEXT("TestApp");
    	WNDCLASS		wndclass;
    	MSG				msg;
    	HWND			hwnd;
    
    	wndclass.style = CS_VREDRAW | CS_HREDRAW;
    	wndclass.lpfnWndProc = WndProc;
    	wndclass.cbClsExtra = 0;
    	wndclass.cbWndExtra = 0;
    	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	wndclass.hInstance = hInstance;
    	wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
    	wndclass.lpszMenuName = NULL;
    	wndclass.lpszClassName = szAppName;
    
    	if (!RegisterClass(&wndclass))
    	{
    		MessageBox(NULL, TEXT("Error registering window class!"),
    			TEXT("ERROR"), MB_OK);
    
    		return 0;
    	}
    
    	hwnd = CreateWindow(szAppName, TEXT("Test Window"),
    						WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT,
    						CW_USEDEFAULT, CW_USEDEFAULT,
    						CW_USEDEFAULT, NULL, NULL,
    						hInstance, NULL);
    
    	ShowWindow(hwnd, nCmdShow);
    	UpdateWindow(hwnd);
    
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HDC				hdc;
    	PAINTSTRUCT		ps;
    	RECT			rect;
    	TEXTMETRIC		tm;
    	static int		cyChar, cyClient;
    	int				nVscrollPos, i, iPaintBeg, iPaintEnd, y;
    	SCROLLINFO		si;
    
    	switch (message)
    	{
    	case WM_CREATE:
    		hdc = GetDC(hwnd);
    		
    		GetTextMetrics(hdc, &tm);
    		cyChar = tm.tmHeight + tm.tmExternalLeading;
    		InvalidateRect(hwnd, NULL, TRUE);
    
    		ReleaseDC(hwnd, hdc);
    		return 0;
    
    	case WM_SIZE:
    		cyClient = HIWORD(lParam);
    
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_RANGE | SIF_PAGE;
    		si.nMin = 0;
    		si.nMax = 30;
    		si.nPage = cyClient / cyChar;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		return 0;
    
    	case WM_VSCROLL:
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_ALL;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		nVscrollPos = si.nPos;
    
    		switch (LOWORD(wParam))
    		{
    		case SB_LINEDOWN:
    			si.nPos += 1;
    			break;
    			
    		case SB_LINEUP:
    			si.nPos -= 1;
    			break;
    		}
    
    		si.fMask = SIF_POS;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    
    		if (nVscrollPos != si.nPos)
    		{
    			ScrollWindow(hwnd, 0, cyChar * (nVscrollPos - si.nPos), NULL, NULL);
    			UpdateWindow(hwnd);
    		}
    		return 0;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_POS;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		nVscrollPos = si.nPos;
    
    		iPaintBeg = max(0, nVscrollPos + ps.rcPaint.top / cyChar);
    		iPaintEnd = min(50, nVscrollPos + ps.rcPaint.bottom / cyChar);
    
    		for (i = iPaintBeg; i <= iPaintEnd; ++i)
    		{
    			y = cyChar * (i - nVscrollPos);
    		TextOut(hdc, 0, y, TEXT("WM_PAINT"), 8);
    		}
    		TextOut(hdc, 0, y, TEXT("TEST"), 4);
    
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    
    		return 0;
    
    	default:
    		break;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    And, I attached the .c file so, if you want to download it and see what I'm talking about (it is hard to explain what is going on with the client area during a scroll).

    Thanks.

    --Garfield
    1978 Silver Anniversary Corvette

  2. #2
    of Zen Hall zen's Avatar
    Join Date
    Aug 2001
    Posts
    1,007
    I don't think you're getting strange results due to what you doing with the scrollbars, but because of the way you're painting. I'm not sure what you're trying to do with -

    Code:
    for (i = iPaintBeg; i <= iPaintEnd; i++)
    		{
    			y = cyChar * (i - nVscrollPos);
    		TextOut(hdc, 0, y, TEXT("WM_PAINT"), 8);
    		}
    		TextOut(hdc, 0, y, TEXT("TEST"), 4);
    but if you want to overwrite the first four characters of WM_PAINT for all y then you could add the tmInternalLeading to the character size in WM_CREATE -

    cyChar = tm.tmInternalLeading+tm.tmHeight + tm.tmExternalLeading;

    The min/max macro's are used to ensure that the scroll values are within allowable values.
    zen

  3. #3
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584
    >>> cyChar = tm.tmInternalLeading+tm.tmHeight + tm.tmExternalLeading;

    So, this will correct my WM_PAINT message? Truthfully, I don't really see how. When it first started up, it didn't even appear to be moving the client area down at all. Then I resized, and it started working.

    >>> but if you want to overwrite the first four characters of WM_PAINT
    I just want it to scroll the client area properly.

    Did you see what I was talking about, though, how screwed up the client area became when you start scrolling?

    Thanks!

    --Garfield
    1978 Silver Anniversary Corvette

  4. #4
    of Zen Hall zen's Avatar
    Join Date
    Aug 2001
    Posts
    1,007
    So, this will correct my WM_PAINT message? Truthfully, I don't really see how.
    No, it probably won't work, I was just messing around with different parts of your code trying different things.

    There's a few things that you may want to change -

    1) You should call GetScrollInfo() after SetScrollInfo() in your VS_SCROLL message handler.

    2)You've set the SCROLLINFO nMax to 30, but are using 50 in your min macro.

    However, I don't know what you're trying to do in your WM_PAINT handler, I still think that's the source of your problems.
    zen

  5. #5
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584
    >>> However, I don't know what you're trying to do in your WM_PAINT handler, I still think that's the source of your problems.

    All I'm trying to do in WM_PAINT is just a little Text output to see if it is scrolling correctly. And then the text becomes screwed up. That's all I want to do in WM_PAINT. You can limit all that output down to just 1 line to see if it scrolls. And then you will see.

    So, is the problem my WM_PAINT message?

    Thanks.

    --Garfield
    1978 Silver Anniversary Corvette

  6. #6
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584
    >>> 2)You've set the SCROLLINFO nMax to 30, but are using 50 in your min macro.

    Oh, and I'm still really confused about max and min. Could you supply some syntax, or something?

    Thanks.

    --Garfield
    1978 Silver Anniversary Corvette

  7. #7
    of Zen Hall zen's Avatar
    Join Date
    Aug 2001
    Posts
    1,007
    OK, they only things that I've changed are what I've mentioned above and your paint handler and your code appears to work fine. Here's the wndproc -

    Code:
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HDC				hdc;
    	PAINTSTRUCT		ps;
    	RECT			rect;
    	TEXTMETRIC		tm;
    	static int		cyChar, cyClient;
    	int				nVscrollPos, i, iPaintBeg, iPaintEnd, y;
    	SCROLLINFO		si;
    
    	TCHAR output [50];
    
    	switch (message)
    	{
    	case WM_CREATE:
    		hdc = GetDC(hwnd);
    
    		
    		
    		GetTextMetrics(hdc, &tm);
    		cyChar = tm.tmHeight + tm.tmExternalLeading + tm.tmOverhang;
    		InvalidateRect(hwnd, NULL, TRUE);
    
    		ReleaseDC(hwnd, hdc);
    		return 0;
    
    	case WM_SIZE:
    		cyClient = HIWORD(lParam);
    
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_RANGE | SIF_PAGE;
    		si.nMin = 0;
    		si.nMax = 50-1;
    		si.nPage = cyClient / cyChar;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		return 0;
    
    	case WM_VSCROLL:
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_ALL;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		nVscrollPos = si.nPos;
    
    		switch (LOWORD(wParam))
    		{
    		case SB_LINEDOWN:
    			si.nPos += 1;
    			break;
    			
    		case SB_LINEUP:
    			si.nPos -= 1;
    			break;
    		}
    
    		si.fMask = SIF_POS;
    		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		if (nVscrollPos != si.nPos)
    		{
    			ScrollWindow(hwnd, 0, cyChar * (nVscrollPos - si.nPos), NULL, NULL);
    			UpdateWindow(hwnd);
    		}
    		return 0;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    
    		si.cbSize = sizeof(si);
    		si.fMask = SIF_POS;
    		GetScrollInfo(hwnd, SB_VERT, &si);
    
    		nVscrollPos = si.nPos;
    
    		iPaintBeg =max(0,nVscrollPos+ps.rcPaint.top / cyChar);
    		iPaintEnd =min(50,nVscrollPos+ps.rcPaint.bottom / cyChar);
    
    		for (i = iPaintBeg; i <= iPaintEnd; i++)
    		{
    			y = cyChar * (i-nVscrollPos);
    			sprintf(output,"WM_PAINT-%d",i);
    
    		TextOut(hdc, 0, y, output, strlen(output));
    		}
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    
    		return 0;
    
    	default:
    		break;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    As I said before the min and max macros are only used here to keep the scrolling within the limits. In simple programs you probably don't need them because nothing else has any influence on the variable your using to determine what part of the display is going in the client area.

    If you had two variables -

    int x=10,y=20;

    and you did

    max(x,y)

    then it would return 20(and 10 using min). By using them to constrain the scrolling area -

    iPaintBeg = max(0, nVscrollPos + ps.rcPaint.top / cyChar);

    you're making sure that 'nVscrollPos + ps.rcPaint.top / cyChar' is greater than 0 and with

    iPaintEnd =min(50,nVscrollPos+ps.rcPaint.bottom / cyChar);

    your making sure that 'nVscrollPos+ps.rcPaint.bottom / cyChar' is less than 50(lines).
    zen

  8. #8
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584
    Thanks for the reply! I'm going to go see and work this out with the code you supplied. I'll get back to you (via this thread) and if there are any problems, I'll tell you.

    Thanks again!

    --Garfield
    1978 Silver Anniversary Corvette

  9. #9
    the Corvetter
    Join Date
    Sep 2001
    Posts
    1,584
    Thanks! Working great.
    1978 Silver Anniversary Corvette

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How can I make a scroll bar in c++?
    By androide in forum C++ Programming
    Replies: 7
    Last Post: 11-18-2003, 12:26 AM
  2. Scroll Bars and Focus
    By Thantos in forum Windows Programming
    Replies: 1
    Last Post: 08-21-2003, 11:57 AM
  3. Horizontal Scroll Bars with CListBox
    By Malek in forum Windows Programming
    Replies: 1
    Last Post: 04-10-2003, 09:58 PM
  4. Scroll bar question
    By converge in forum Windows Programming
    Replies: 4
    Last Post: 01-22-2003, 09:15 AM
  5. Problem with Scroll Bar
    By Garfield in forum Windows Programming
    Replies: 3
    Last Post: 11-01-2001, 02:23 PM