how to make a scrollable window in vc++ win32 - c++

basically I have a scrollbar in a custom window where we could add other controls like buttons, textboxes and so and so forth, now here is the problem I have created the scrollbar alright it scrolls down and up, but the problem is that the maximum scroll should be specified which then blocks it to scroll more than that, but there could be as much control as the user want, which for me it would not be known, I mean like to tell the problem to be more like a textbox's built-in scroll bar when you specify it with WM_VSCROLL OR HSCROLL, and then give the user the ability to scroll as much as you type on the textbox, that's exactly what I want.
here is the code so far I have been able to go through:-
Code for Scrollbar:-
case WM_LBUTTONDOWN:
{
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = 0;
si.nTrackPos = 0;
GetScrollInfo(hwnd, SB_VERT, &si);
break;
}
case WM_VSCROLL:
{
auto action = LOWORD(wParam);
HWND hScroll = (HWND)lParam;
int pos = -1;
if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
pos = HIWORD(wParam);
} else if (action == SB_LINEDOWN) {
pos = g_scrollY + 50;
} else if (action == SB_LINEUP) {
pos = g_scrollY - 50;
} else if (action == SB_PAGEUP) {
GetClientRect(hwnd, &r);
pos = g_scrollY - r.bottom;
} else if (action == SB_PAGEDOWN){
GetClientRect(hwnd, &r);
pos = g_scrollY + r.bottom;
}
if (pos == -1)
break;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = pos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
GetScrollInfo(hwnd, SB_VERT, &si);
pos = si.nPos;
POINT pt;
pt.x = 0;
pt.y = pos - g_scrollY;
auto hdc = GetDC(hwnd);
LPtoDP(hdc, &pt, 1);
ReleaseDC(hwnd, hdc);
ScrollWindow(hwnd, 0, -pt.y, NULL, NULL);
g_scrollY = pos;
return 0;
}
case WM_SIZE:
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = MAX_RANGE;
if(si.nPos > 100)
si.nMax = 5000;
if(SB_THUMBPOSITION == 100){
si.nMax = MAX_RANGE;}
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
break;
}
as you can see si.nMax value is the maximum amount of lines it is going to be able to scroll, but I want it to be as much as there are controls.

You must know how many controls are in your window, so you can set nMax member in the SCROLLINFO structure to whatever value you want and call SetScrollInfo() whenever new controls are added.

Related

I am trying to check if the specified window is visible to the screen using Winapi but not sure how to approach when two windows are not overlapped

What I am trying to do is combining the regions of the top windows and checking if the specified window is present in the region if it is then displaying result. It does not giving results very accurately. And also I have problem when windows are non-overlapped. Does any one have any idea how to overcome this situation.
p is the foreground process
while (p != NULL)
{
if (IsWindowVisible(p)) {
if ((GetWindowLongPtr(p, GWL_STYLE) & WS_ICONIC) || GetWindowLong(p, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) {
}
else
{
RECT a;
if (GetWindowText(p, str, 255)) {
if ((_wcsicmp(str, _T("Program Manager"))))
{
GetWindowThreadProcessId(p, &proc_id);
proc_hnd = OpenProcess(PROCESS_ALL_ACCESS, TRUE, proc_id);
flag = GetProcessImageFileName(proc_hnd, proc_name, 1024);
wstring test(&proc_name[0]); //convert to wstring
string test2(test.begin(), test.end());
LOGGER->info(test2);
GetWindowRect(p, &a);
z = CreateRectRgn(a.left, a.top, a.right, a.bottom);
if (wcsstr(proc_name, L"chrome.exe")) {
RECT op;
GetWindowRect(p, &op);
HRGN j1 = CreateRectRgn(op.left, op.top, op.right, op.bottom);
CombineRgn(j1, j1, y, RGN_DIFF); emphasized text
CombineRgn(y, y, z, RGN_OR); //combining the region
switch (GetRgnBox(j1, &a))
{
case NULLREGION:
LOGGER->info("null region");
break;
case SIMPLEREGION:
LOGGER->info("simple region");
break;
case COMPLEXREGION:
LOGGER->info("complex region");
break;
default:
LOGGER->info("default region");
}
}
else
{
CombineRgn(y, y, z, RGN_OR); //combining the region
}
}
}
}
}
p = GetWindow(p, GW_HWNDNEXT); //getting the next window
}
I use the IsOverlapped sample here, but it is used to detect whether it is overlapped by any window. I modified this sample to check whether the window is visible:
BOOL IsVisible(HWND window)
{
HWND hWnd = window;
RECT thisRect;
if (!IsWindowVisible(window))
return FALSE;
GetWindowRect(window, &thisRect);
HWND explorer = FindWindow(L"Shell_TrayWnd", NULL);
DWORD explorer_pid;
GetWindowThreadProcessId(explorer, &explorer_pid);
HRGN hrgn_above = CreateRectRgn(0, 0, 0, 0);
HRGN hrgn_window = CreateRectRgnIndirect(&thisRect);
while ((hWnd = GetWindow(hWnd, GW_HWNDPREV)) != NULL)
{
DWORD pid;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == explorer_pid || !IsWindowVisible(hWnd))
continue;
RECT rc = {};
GetWindowRect(hWnd, &rc);
HRGN hrgn_hWnd = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
CombineRgn(hrgn_above, hrgn_above, hrgn_hWnd, RGN_OR);
DeleteObject(hrgn_hWnd);
}
int ret = CombineRgn(hrgn_window, hrgn_window, hrgn_above, RGN_DIFF);
DeleteObject(hrgn_above);
DeleteObject(hrgn_window);
if (ret != NULLREGION && ret)
return TRUE;
return FALSE;
}
Use GetWindow + GW_HWNDPREV to get the windows list above the hWnd in Z order.

DrawText Refresh Problem - Text Disappears

I'm using Objective Grid and wanted to have a grid cell control that can show an icon and text. So I took RogueWave's example (https://rwkbp.makekb.com/entry/466/) and modified it as below.
Initially the text renders correctly, but if I do anything in the grid that refreshes the relevant cell, the text disappears. If I update the whole grid, or even refresh the cell by dragging it off screen and back again, the text reappears - until it disappears again. Seems like a paint or InvalidateRect problem - but I can't figure out how to fix it. Any ideas?
Thanks!
//------------------------------------------------------------------------------
void CGXIconControl::Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle)
//------------------------------------------------------------------------------
{
BOOL b;
ASSERT(pDC != NULL && pDC->IsKindOf(RUNTIME_CLASS(CDC)));
// ASSERTION-> Invalid Device Context ->END
ASSERT(nRow <= Grid()->GetRowCount() && nCol <= Grid()->GetColCount());
// ASSERTION-> Cell coordinates out of range ->END
ASSERT_VALID(pDC);
DrawBackground(pDC, rect, style);
pDC->SetBkMode(TRANSPARENT);
if (rect.right <= rect.left || rect.Width() <= 1 || rect.Height() <= 1)
{
return;
}
CString str = style.GetIncludeValue() ? style.GetValue() : _T("");
CString sTxtBefore = _T("");
CString sTxtAfter = _T("");
int nHAlign = style.GetHorizontalAlignment();
int nVAlign = style.GetVerticalAlignment();
// Save these value to restore them when done drawing
COLORREF crOldTextColor = pDC->GetTextColor();
COLORREF crOldBkColor = pDC->GetBkColor();
if (!style.GetEnabled())
{
pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
}
CBrush Brush;
Brush.CreateSolidBrush(style.GetEnabled() ? ::GetSysColor(COLOR_WINDOWTEXT) : ::GetSysColor(COLOR_GRAYTEXT));
LOGBRUSH lBrush = { 0 };
Brush.GetLogBrush(&lBrush);
CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
CPen linePen;
linePen.CreatePen(PS_SOLID, 1, &lBrush);
CPen* pOldPen = pDC->SelectObject(&linePen);
// Set font bold if necessary
CFont* pCurfont = pDC->GetCurrentFont();
LOGFONT lf;
CFont font;
if (pCurfont)
{
pCurfont->GetLogFont(&lf);
}
if (style.GetFont().GetBold())
{
lf.lfWeight = FW_BOLD;
}
font.CreateFontIndirect(&lf);
CFont* pOldFont = pDC->SelectObject(&font);
int nIcoStart = str.Find(_T("#ICO"));
if (nIcoStart == -1)
{
// We didn't find an icon indicator, so just draw the text
pDC->DrawText(str, rect, DT_SINGLELINE|nHAlign|nVAlign);
}
else
{
sTxtBefore = str.Left(nIcoStart);
CSize szBefore = pDC->GetTextExtent(sTxtBefore);
int nIconEnd = str.Find(_T(")"), nIcoStart);
CString strIDResource = str.Mid(nIcoStart + 5, nIconEnd - (nIcoStart + 5));
UINT nIDResource = _ttoi(strIDResource);
// Load the highest bit-depth available
HICON hIcon = NULL;
hIcon = LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 32);
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 24) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 16) : NULL;
hIcon == NULL ? LoadResourceIcon(nIDResource, m_nIconSize, m_nIconSize, 8) : NULL;
sTxtAfter = str.Right(str.GetLength() - nIconEnd - 1);
CSize szAfter = pDC->GetTextExtent(sTxtAfter);
CRect rectCell = CGXControl::GetCellRect(nRow, nCol, rect, &style);
CRect rectBefore = rectCell;
CRect rectAfter = rectCell;
int nTotalWidth = szBefore.cx + m_nIconSize + szAfter.cx;
// Calculate positions
int nTop, nLeft;
switch (nHAlign)
{
case DT_LEFT:
{
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_CENTER:
{
rectBefore.left = (rectCell.right - rectCell.Width() / 2) - nTotalWidth / 2;
rectBefore.right = rectBefore.left + szBefore.cx;
nLeft = rectBefore.right;
rectAfter.left = nLeft + m_nIconSize;
rectAfter.right = rectAfter.left + szAfter.cx;
} break;
case DT_RIGHT:
{
// Work from the right
rectAfter.right = rectCell.right;
rectAfter.left = rectAfter.right - szAfter.cx;
nLeft = rectAfter.left - m_nIconSize;
rectBefore.right = nLeft;
rectBefore.left = rectBefore.right - szBefore.cx;
} break;
}
switch (nVAlign)
{
case DT_TOP:
nTop = rectCell.top;
break;
case DT_VCENTER:
nTop = rectCell.top + (rectCell.Height() / 2 - m_nIconSize / 2);
break;
case DT_BOTTOM:
nTop = rectCell.bottom - m_nIconSize;
break;
}
if (!sTxtBefore.IsEmpty())
{
pDC->DrawText(sTxtBefore, rectBefore, DT_SINGLELINE|nHAlign|nVAlign);
}
b = ::DrawIconEx(pDC->m_hDC, nLeft, nTop, hIcon, m_nIconSize, m_nIconSize, 0, NULL, DI_NORMAL);
b = ::DestroyIcon(hIcon);
if (!sTxtAfter.IsEmpty())
{
pDC->DrawText(sTxtAfter, rectAfter, DT_SINGLELINE|nHAlign|nVAlign);
}
}
// Reset original values
pDC->SetTextColor(crOldTextColor);
pDC->SetBkColor(crOldBkColor);
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldFont);
// Child Controls: spin-buttons, hotspot, combobox btn, ...
CGXControl::Draw(pDC, rect, nRow, nCol, style, pStandardStyle);
}

Thumtrack Length Issue when resizing window with Image on Horizontal fit

For Example: if my image width and height is 1600x2000 and client window width and height is 900x600.. now I want to fit the image horizontally. For that I will bring down the image width(1600) to window width(900) and also to maintain the original aspect ratio of the image i will also bring down the height of the image(2000) to 1125. for that I have used the below logic in FITHORIZONTAL Then I will use below wm_size code to set the scroll bar position and thumb length. Unfortunately the full image is not shown even though the thumb tracker has reached the end of the scroll bar.
switch(FitType)
{
case FITHORIZONTAL:
//find out whether resize percentage is decrease or increase
if(ActualImageWidth > WindowWidth) //resize decreasing
{
//find out the pecentage of decreased value
resizedPercent = WindowWidth/ActualImageWidth;
} else //resize increasing
{
//find out the pecentage of increased value
resizedPercent = ActualImageWidth/WindowWidth;
}
ResizedWidth = iWndwidth;
ResizedHeight = ActualImageHeight * resizedPercent;
break;
}
case WM_SIZE :
{
GetWindowRect(hWnd, &rect);
WndWidth = rect.right - rect.left;
WndHeight = rect.bottom - rect.top;
GetScrollInfo(hWnd, SB_VERT, &si);
int yMaxScroll = max((int)ResizedHeight - WndHeight, 0);
int yCurrentScroll = min(si.nPos, yMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = ResizedHeight;
si.nPage = WndHeight;
si.nPos = 0;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
InvalidateRect(hWnd, &rect, true);
return 0;
}

MFC Controls are getting disappeared after scrolling

I am working on dialog based MFC application in WinCE.
I created few controls in a dialog and scrolled down.
When i scroll up again, the controls in the first screen got disappeared.
Controls getting created in OnInitDialog() like below at coordinates (50,10)
test->Create(_T("Title"), WS_CHILD|WS_VISIBLE, CRect(50,10,200,40), this, ID_TITLE);
Scroll handling i am doing in OnVScroll() like below
switch(nSBCode)
{
case SB_LINEDOWN:
{
if(nPos < max)
{
ScrollWindowEx(0, SCROLLDOWN_LINE_STEPSIZE, CRect(0,0, rect.right - 25, rect.bottom), NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
pScrollBar->SetScrollPos(nPos - SCROLLDOWN_LINE_STEPSIZE); //nPos+10
}
break;
}
case SB_LINEUP:
{
if(nPos > min)
{
ScrollWindowEx(0, SCROLLUP_LINE_STEPSIZE, CRect(0,0, rect.right - 25, rect.bottom), NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE);
pScrollBar->SetScrollPos(nPos - SCROLLUP_LINE_STEPSIZE); //Ex: nPos-10
}
break;
}
default:
printf("Notimplemented");
break;
}
I am handling Scroll down and scroll up.
While scrolling down, all the controls in Dialog are shown.
But while scrolling up, the controls at the top got disappeared.
What's going on ?
Should i implement OnPaint() method for drawing the controls each time i scroll ?
If so, how ?
I have the following code that works fine. I hope it will help you.
LRESULT CMyWindow::OnVScroll( UINT code, UINT position )
{
SCROLLINFO info = { sizeof( SCROLLINFO ), SIF_ALL };
GetScrollInfo( m_wnd, SB_VERT, &info );
int previous_pos = info.nPos;
switch( code )
{
case SB_TOP:
info.nPos = 0;
break;
case SB_BOTTOM:
info.nPos = info.nMax - info.nPage;
break;
case SB_LINEDOWN:
info.nPos = min( info.nPos + 1, info.nMax - (int)info.nPage );
break;
case SB_LINEUP:
info.nPos = max( info.nPos - 1, info.nMin );
break;
case SB_PAGEDOWN:
info.nPos = min( info.nPos + (int)info.nPage, info.nMax - (int)info.nPage );
break;
case SB_PAGEUP:
info.nPos = max( info.nPos - (int)info.nPage, info.nMin );
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
info.nPos = position;
break;
}
int offset = previous_pos - info.nPos;
if( offset != 0 )
{
ScrollWindowEx( m_wnd, 0, offset, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE );
SetScrollPos( m_wnd, SB_VERT, info.nPos, FALSE );
}
return 0L;
}
From the ScrollWindowEx docs:
dy Specifies the amount, in
device units, of vertical scrolling.
This parameter must be a negative
value to scroll up.
I bolded the relevant phrase.

Help with WinAPI scroll bars

Right now I have a window with horizontal ad vertical scrollbars. I use these parameters to initialize it.
//Set OGL Frame scroll bar
SCROLLINFO inf;
inf.cbSize = sizeof(SCROLLINFO);
inf.fMask = SIF_PAGE | SIF_POS;
inf.nPage = 20;
inf.nPos = 30;
It creates them in the center and I like their size, but when I scroll I multiply by 50 which creates chopiness. How could I add more resolution to the bars and still keep the same thumb size. Is there a way I can calculate the size and position of the bar based on the above parameters?
Thanks
Right, here's my solution even though one is already accepted.
Everytime I have issues with the windows controls I use Controlspy to experiment with them. Controlspy also lists all the different messages that can be sent to the different controls. Find one that is similar to what you are trying to do and check that specific message on MSDN.
Here is my old chunk of code that handles scrolling events. You could use the same "customized" approach.
Note that it's (probably) not the best way of solving this problem, but still a working one.
case WM_VSCROLL:
{
TEXTHANDLER * handler = ((TEXTHANDLER *)GetProp(hWnd, "TEXTHANDLER"));
BOOL needInvalidation = TRUE;
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &si);
switch (LOWORD(wParam))
{
case SB_LINEUP:
si.nPos -= 1;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_LINEDOWN:
si.nPos += 1;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_PAGEUP:
si.nPos -= handler->renderer->cyCount;
if (si.nPos < 0)
{
si.nPos = 0;
needInvalidation = FALSE;
}
break;
case SB_PAGEDOWN:
si.nPos += handler->renderer->cyCount;
if (si.nPos > si.nMax)
{
si.nPos = si.nMax;
needInvalidation = FALSE;
}
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
// Set new text renderer parameters
handler->renderer->yPos = si.nPos;
if (needInvalidation) InvalidateRect(hWnd, NULL, FALSE);
return 0;
}