How to get CEdit to scroll properly? - mfc

I have a CEdit control that's used to display a diagnostics output.
Sometimes the data overflows the screen size, so naturally I set the Vertical Scroll property to true (MFC dialog editor).
But then, when I tried to scroll the text that was in the window before isn't cleared and the new text is written over it.
The result is a big mess of everything I have scrolled past.
I've looked for a draw background property or something similar that will erase everything in the window while scrolling (before redrawing the new data).
Any suggestions?

I think you might want to set Auto VScroll and Multiline to true, and Auto HScroll to false.

We were having a similar problem. We ended up having to invalid the region of the parent window to get it to update when we got WM_VSCROLL. I tried to do as user demorge says here:
SetBkMode(hdc, TRANSPARENT) doesn't work
But our code doesn't use handles, we actually use the class CWnd, so we ended up doing this in the WindowProc instead:
switch(message)
{
...
case WM_VSCROLL:
case WM_HSCROLL:
LRESULT answer;
PAINTSTRUCT ps;
CDC* pdc;
CWnd* MyParentHWnd;
// We want the scroll to work the same way it has always worked for our
// ancestor class. Let them handle the scrolling and save off their
// return.
answer = AncestorClass::WindowProc(message, wParam, lParam);
pdc = BeginPaint(&ps);
// DO NOT change the assignement operator in the conditional below to an
// equality operator. We are actually trying to get the parent window and
// and storing locally, and then verifying that we didn't get back null.
// This is a purposeful design decision.
if (MyParentHWnd = GetParent()){
RECT MyRect;
GetClientRect(&MyRect);
ClientToScreen(&MyRect);
MyParentHWnd->ScreenToClient(&MyRect);
MyParentHWnd->InvalidateRect(&MyRect);
}
EndPaint(&ps);
return answer;
break;
...
}
Of course, I had to genericize it a little bit. I just wanted you to know that yes, there are other people that are seeing your problem, and we found how to fix it.

I tested this with VS2005, which ships with MFC 8.0. I couldn't replicate your problem.
I added one CEdit and one CRichEditCtrl to a dialog based app. Changed properties Multiline, Auto VSCroll and Vertical Scroll to true. Used SetWindowText-method to put loooooong string of text to both of them. I started the app and text scrolled just fine.
What did you do differently?
Just to be sure. You didn't use the SetCaretPos-method, did you? There was some note about that in the MSDN page. Here's the Knowledge Base article.

Related

C++ WinAPI Conflict between SetLayeredWindowAttributes and BitBlt

I have created a custom window using DWM. I painted the caption by using PaintCustomCaption() ,which is an example from MSDN. It worked properly until I added SetLayeredWindowAttributes().
Window before adding
SetLayeredWindowAttributes(hWnd,RGB(0,0,1),0,LWA_COLORKEY);
After adding
I tried changing RGB values but it was still black except RGB(0,0,0).
I wonder if BitBlt() works properly.
Edited:
The reason I added SetLayeredWindowAttributes is to solve this problem
Do you have other ways to paint the caption?
case WM_ACTIVATE: {
DwmExtendFrameIntoClientArea(hWnd,&m); // m={-1,-1,-1,-1};
break;
}
case WM_INITDIALOG: {
SetWindowPos(hWnd,NULL,0,0,500,500,SWP_NOMOVE|SWP_FRAMECHANGED);
SetWindowLongPtr(hWnd,GWL_STYLE,WS_VISIBLE|WS_OVERLAPPEDWINDOW);
SetWindowLongPtr(hWnd,GWL_EXSTYLE,WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd,RGB(0,0,1),0,LWA_COLORKEY);
RedrawWindow(hWnd,NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
return true;
}
case WM_PAINT: {
hdc=BeginPaint(hWnd,&paintstruct);
PaintCustomCaption(hWnd,hdc)
EndPaint(hWnd,&paintstruct);
break;
}
If you keep the window border, you don't need to paint the caption yourself unless you want to add something to your caption.
That is, handle WM_NCCALCSIZE and WM_NCHITTEST normally.
First, use RGB(200,201,202) as the transparency key instead of RGB(0,0,1).
You may try other values but it is the best one so far I have tested.
Then, add this after HBITMAP hbmOld=(HBITMAP)SelectObject(hdcPaint,hbm); in PaintCustomCaption():
FillRect(hdcPaint,&rcClient,CreateSolidBrush(RGB(200,201,202)));

How to Scroll CEdit(MFC) smoothly

I want a multiline CEdit control to scroll itself downside slowly like a rolling subtitle.
So far, I use CEdit::LineScroll() in OnTimer() event. It can do a full line roll with is not as smooth as I expected.
I try to replace LineScroll() by ScrollWindow() so that I can scroll the text by pixels however the ScrollWindow() function don't draw the new text lines which should be scrolled into the control.
Anyone have any idea on how to achieve this?
In case I did not describe the issue clear. I add these gifs:
What LineScroll() do (and I just need it smoother):
And this is what ScrollWindow() do (the following text is cut):
Here is the code of ScrollWindow():
CRect clientRect;
m_editAns.GetClientRect(&clientRect);
m_editAns.ScrollWindow(0, -10, NULL, &clientRect);
m_editAns.UpdateWindow();
ValidateRect(&clientRect);
*PS: The project uses the CEdit control else where so I can't replace it by another control, though inheritance is acceptable.
According to CWnd::ScrollWindow,
To repaint the uncovered area at the same time the scrolling is done,
call the UpdateWindow member function immediately after calling
ScrollWindow.

change background colour in parent window

I'm very new to win32api programming. I have 3 questions.
how to change the background colour in the parent window. i did it as folows but it dont work
wClass.hbrBackground=(HBRUSH)(RGB(255,255,255));
second question is, I use to add text in the window as follows. the text is in bold font and with a background colour. I want the text to be in normal and without background colour.
PAINTSTRUCT ps;
HDC hDC;
char szBuffer[]="Hello, World!";
hDC=BeginPaint(hWnd,&ps);
TextOut(hDC,10,10,szBuffer,strlen(szBuffer));
third question is how to add group boxes in the parent window. i searched it in the internet but this was discribed how to add group boxes on dialog boxes using resources.
pls some one help me with these isue...
> wClass.hbrBackground=(HBRUSH)(RGB(255,255,255));
I suppose the class structure requiers the handle of the brush, not the color value itself (but I'm not sure). Something like this:
wClass.hbrBackground=(HBRUSH)(CreateSolidBrush(RGB(255,255,255)));
To make the text background transparent use special WinAPI function, SetBkMode(TRANSPARENT); (Oof, spend some time to remember it's name:) ).
In most tasks it will be much better to make a dialog resource and use it like an ordinary window (drawing smth in it, putting simple windows in which you draw, etc) than take an ordinary window and try to add dialog controls in it. It became a common practice since WinForms and then WPF - every window in them is a "form" in which you can add controls, draw in it and so on.
I'm trying to remember if Microsoft put in code to clear the client area. I know at the minimum, you can get the client rect and then use that to base a drawRect() command to the whole client area. You also may have to trap the command to erase the background

Listview flickers on Win32 dialog when removing and re-adding all items and all columns

Consider a plain Win32 dialog with listview control (in report mode) written in C++. Upon a certain event all items and all columns are deleted and new columns and items are created. Basically, as content changes, columns are automatically generated based on content.
When old items/columns are removed and new ones added, listview flickers like hell. I have tried WM_SETREDRAW and LockWindowUpdate() with no change to visual experience.
I have even set extended listview style LVS_EX_DOUBLEBUFFER and that didn't help at all.
The parent dialog has WS_CLIPCHILDREN set.
Any suggestions how to make this work with as little flicker as possible? I am thinking of using two listviews, alternating visibility, using the hidden one as a back buffer but this sounds like an overkill. There must be an easy way.
The default list control painting is pretty flawed. But there is a simple trick to implement your own double-buffering technique:
CMyListCtrl::OnPaint()
{
CRect rcClient;
GetClientRect(rcClient);
CPaintDC dc(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bmMem;
bmMem.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
CBitmap* pbmOld = dcMem.SelectObject(&bmMem);
dcMem.FillSolidRect(rcClient, ::GetSysColor(COLOR_WINDOW));
this->DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, (LPARAM)0);
dc.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(pbmOld);
CHeaderCtrl* pCtrl = this->GetHeaderCtrl();
if (::IsWindow(pCtrl->GetSafeHWnd())
{
CRect aHeaderRect;
pCtrl->GetClientRect(&aHeaderRect);
pCtrl->RedrawWindow(&aHeaderRect);
}
}
This will create a bitmap and then call the default window procedure to paint the list control into the bitmap and then blitting the contents of the bitmap into the paint DC.
You should also add a handler for WM_ERASEBKGND:
BOOL CMyListCtrl::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
This will stop the control from always erasing the background before a redraw.
You can optimize the OnPaint further if you add a member variable for the bitmap and only (re)create it when the size of the window changed (because always creating a bitmap may be costly depending on the size of the window).
This should work pretty well.
After trying many things and most of all humbagumba's suggestions I have come to a very simple conclusion. LockWindowUpdate is everybody's friend in this sort of situation. I am not sure how come it failed to work for me the first time but after custom painting failed to deliver in all situations I have tried LockWindowUpdate once again and it worked!
Basically, just wrap all work on listview in a LockWindowUpdate(hWnd) and LockWindowUpdate(NULL) and things work beautifully. There is not even a scrollbar flicker any more.
Just make sure not to nest LockWindowUpdate as only one window can be locked at a time.

How to prevent window resizing temporarily?

I have a window which can be resized, but there are some situations when resizing is not possible because of the application state. Is there a way to prevent resizing the window temporarily?
I want to disable resizing by all means available to the users, which include window menu, dragging edges by mouse, user initiated window tiling performed by OS - and perhaps some other I am not aware of?
To retain the look of the window border and still prevent re-size (and cursor change), catch WM_NCHITTEST, pass it to DefWindowProc, if the returned code is one of the size constants, change the real return to something else, HTCLIENT for example
One way is to use GetWindowLong() with GWL_STYLE flag to get the window style and
reset/remove any styles you need, ie the WS_THICKFRAME style so that the window can't be resized.
You apply the new style with SetWindowLong.
Another possibility is to handle the WM_GETMINMAXINFO message and set the MINMAXINFO struct so that both min and max size of the window is the current size. Then the user can't resize the window either.
Following code in the window procedure seems to handle the case of user dragging the window edge/corner:
case WM_SIZING:
RECT &rc = *(LPRECT) lParam;
RECT windowRect;
GetWindowRect(hwnd, &windowRect);
rc = windowRect;
return 0;
I did not find anything yet to prevent the system from resizing the window when tiling/cascading windows. I hoped following might do the trick, but it seems it does not:
case WM_SIZE:
return TRUE;
I guess I can find similar measure for other cases, but at least I would need to know the exhaustive list of messages which can result in a window changing its size.
Also, while this really prevents the window from resizing, I would rather prevent the user from even initiating the resize, than apparently letting him to resize and then refusing to do so.