Automatically display vertical scrollbar in multiline text edit control - c++

On a windows mobile device I have a mutliline text edit control that is set to read-only and has some static text displayed during it's display lifetime. I would like to only display a vertical scrollbar when it's actually useful (i.e. the text is larger than the display).
I can't easily figure out if the text is to large to display because of two reasons.
There is no horizontal scroll bar displayed so the text wraps.
Under windows mobile, the win32 routines to calculate the size of what text will display does not work correctly. They return a incorrect rectangle.
The edit control must tell the scroll bar what it's scroll range is at some point. I was wondering if I could get in between then and hide the scroll bar if it's not going to be used.

This is how I solved this problem.
First off:
It only works with read-only mode of a edit control (as you don't want the text to change often).
I think is specific to Windows Mobile MFC, big windows can handle this a lot better.
The solution is very very hacky.
Solution:
I have a standard CEdit bound to the control.
CEdit m_Message;
DDX_Control(pDX, IDC_MESSAGE,
m_Message);
During the InitDialog and OnSize calls, turn on the display of the scroll bar and setup a timer message.
m_Message.ShowScrollBar(SB_VERT,
TRUE);
SetTimer(DO_ADJUST_DISPLAY_STATE, 50,
0);
In the timer handling code, use the scroll information to determine if the scroll bar needs to be displayed.
If not being displayed, turn off the scroll bar and force the window to redisplay.
void CMessageDlg::OnTimer( UINT_PTR nIDEvent )
{
switch(nIDEvent)
{
case DO_ADJUST_DISPLAY_STATE:
KillTimer(nIDEvent);
// deselect all text
m_Message.SetSel(0, 0);
SCROLLINFO info;
m_Message.GetScrollInfo(SB_VERT, &info);
if(info.nPage > (UINT)info.nMax)
{
// need to re-display the non scroll bar version
m_Message.ShowScrollBar(SB_VERT, FALSE);
// I could not find any other way to force a redisplay
// correctly without display problems...
// first move the window to a know invisible area
// then wait (using a timer) for the window to move
// then move the window back to it's original position
RECT rt;
rt.left = 0;
rt.right = 5;
rt.top = 0;
rt.bottom = 5;
m_Message.MoveWindow(&rt);
SetTimer(DO_REDISPLAY_MESSAGE, 50, 0);
}
break;
case DO_REDISPLAY_MESSAGE:
KillTimer(nIDEvent);
// m_MessagePosition holds the original position
// worked out dynamically during the WM_SIZE
// processing
m_Message.MoveWindow(&m_MessagePosition);
break;
}
}
I hope that helps out other people who may have similar requirements.

I was wondering if I could get in between then and hide the scroll bar if it's not going to be used.
I highly doubt it. But I bet you could call SetScrollInfo some time after the text box is created, and send your own scroll parameters.
EDIT: Wrong link, my bad. Here's the one for the Windows CE

Related

C++ how to display text in child window at real time

This application creates a child window (which is the white box) when I right click anywhere, and destroys the child window after another right click. I have implemented the mechanics to expand and shrink the red rectangle through Direct 2D. I would like the child window to display the width and height of the rectangle at real time, as I make changes to it. I do not interact with the child window at all: it doesn't need an "x" button for closing and stuff; it just prints out a couple lines of data.
Here is what I have in my main window procedure:
case WM_RBUTTONUP:
{
DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(hwnd, GWLP_USERDATA)));
pDemoApp->showTextBox = !pDemoApp->showTextBox; //showTextBox is a boolean
if (pDemoApp->showTextBox) {
POINTS cursor = MAKEPOINTS(lParam);
pDemoApp->child_hwnd = CreateWindowEx(
WS_EX_TOPMOST,
"LISTBOX",
"I dont need a title here",
WS_CHILDWINDOW,
cursor.x,
cursor.y,
100,
200,
pDemoApp->main_hwnd,
NULL,
HINST_THISCOMPONENT,
pDemoApp
);
ShowWindow(pDemoApp->child_hwnd, SW_SHOWNORMAL);
UpdateWindow(pDemoApp->child_hwnd);
}
else {
DestroyWindow(pDemoApp->child_hwnd);
}
}
break;
How may I go from here? I would like to know:
Is using Direct Write to draw text in the child window my only option? I see the dashed lines in the white box so I assume there must be a way to display plain text.
I used LISTBOX here, which is a predefined windows class name. How do I set a procedure for it? What else predefined class name can better suit my need? Or do I have to register a custom one;
I would like to drag the child window around, how can I set it up so that the system handles dragging for me.
Would popping a dialog box to display text be better than popping a child window?
Thanks.
Since you are using a Win32 LISTBOX control as the child window, then you have a couple of options for displaying the rectangle's dimensions in it:
give the ListBox the LBS_HASSTRINGS style, and add 1-2 items to it. Then, any time you change the rectangle, send LB_DELETESTRING and LB_ADDSTRING messages to the ListBox's HWND to display the updated dimensions as needed. A ListBox does not have a way to update an existing item, so to change an existing item's text, you have to remove the item and then re-add it with the new text.
give the ListBox an LBS_OWNERDRAW... style, and add 1-2 blank item(s) in it. Then have the ListBox's parent window handle WM_MEASUREITEM and WM_DRAWITEM notifications from the ListBox to display the rectangle's current dimensions in the item(s) as needed. Whenever the rectangle is changed, call InvalidateRect() on the ListBox's HWND to trigger a redraw.

Windows 10 : Pen (stylus) not working on MFC application

I have an MFC application that works fine with Pen (Stylus) under Windows 7, but unfortunately, it does not work on Windows 10.
Under Windows 7, I am able to scroll vertically with the stylus WITHOUT using (clicking and dragging) the scroll bar, i can scroll vertically by clicking and dragging from anywhere in my dialog (formview)
Under Windows 10, I am not able to scroll vertically with the stylus WITHOUT using (clicking and dragging) the scroll bar. I must click (and drag) with the Stylus on the scroll bar to scroll vertically
My need :
I need to be able to scroll vertically with the stylus WITHOUT using (clicking and dragging) the scroll bar on Windows 10
Detail:
I am on Windows 10 with Visual studio 2010.
My opinion
I think that the version of MFC100.dll (related to my Visual Studio 2010) does probably not support the functionality of the stylus under Windows 10 because the code works correctly under windows 7
Someone has already encountered this problem ?
Thanks.
I post this answer to help people who are in the same situation as me.
1. Introduction
Interaction with the scrollbar can be can be done using (It’s probably not the only methods that exist):
SetScrollPos coupled with ScrollWindow. The first scrolls the bare scroll, the second scrolls the contents of the window
ScrollToPosition which scrolls both the scroll bare and the contents of the window
The first method (SetScrollPos coupled with ScrollWindow) requires management of all scrollbar informations : scrollable size, current position, ... and recalculate it on each OnSize () (which is not too trivial ...)
With the seconde method (ScrollToPosition) mode, all we have to do is to call the ScrollToPosition with the desired scrolling size. We don't have to manage the details of the scroll bare, Windows does it for us!
For some reason that I don't know, the stylus scroll functionality on Windows 10 does not have the same behavior on Windows 7 : The scrollbar scrolls but not the contents of the window !!!
2. Solution
Having spent several hours on this problem, I conclude that the simple solution to work around this problem is to use the ScrollToPosition function as much as possible. And in this case, you just need to calculate scrolled size (delta) which is defined by:
Starts when the mouse button is pressed (Stulys pointed) (use ON_WM_LBUTTONDOWN)
Extended/reduced during drag (use ON_WM_MOUSEMOVE)
Ends when the button is released (use ON_WM_LBUTTONUP)
All you have to do is call the ScrollToPosition function with the calculated delta. This will allow you to move from an extremely complicated management mode to a few things that look like this:
ON_WM_LBUTTONDOWN
void CMyView::OnLButtonDown (UINT nFlags, CPoint point)
{
// Code here ...
POINT p;
GetCursorPos(&p);
ScreenToClient ( &p); // Convert to screen coordinates
m_dragPosition = p; // m_dragPosition : member variable to store click position
m_bMouseDown = true; // Set Flag to true
CWnd::OnLButtonDown (nFlags, point) ;
}
ON_WM_LBUTTONUP
void CMyView::OnLButtonUp (UINT nFlags, CPoint point)
{
// Code here ...
m_bMouseDown = false;
CWnd::OnLButtonUp(nFlags, point);
}
ON_WM_MOUSEMOVE
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
int delta = p.y - m_dragPosition.y;
// Use absolute value to take into account top/bottom scrolling
if ( abs (delta) > SCROLLING_STEP_SIZE)
{
CPoint pp = GetScrollPosition (); // Get current scrollbar position
pp.y += -delta;
pp.x = 0; // I'm intersting only for vertical scrolling
ScrollToPosition (pp); // Scroll to new position
UpdateWindow (); // This redraw new showed area
m_dragPosition = p; // Update drag position
}
}
By adding this, you avoid explicit management which is not at all trivial...
Hope this will help those who are in the same situation as me

Set horizontal scroll of CListBox row back after resetting content

I got a multiple selection CListBox with horizontal scroll bar enabled and showed correctly. Problem is, that when I use function
lst.ResetContent() and fill it back, I can't find way to scroll text in the rows back to the same position. I tried to use
lst.SetScrollPos(SB_HORZ, horizScroll, TRUE); , where horizScroll = lst.GetScrollPos(SB_HORZ); This works correctly on scroll bar itself, but
text in the row stays not scrolled (manual scrolling functions OK).
Structure of my program is:
CListBox lst;
int horizScroll;
/*Periodically doing code bellow*/
//Get current scroll position
horizScroll = lst.GetScrollPos(SB_HORZ);
//Reset current content
lst.ResetContent();
//Add item into CListBox (UNICODE in my application)
lst.AddString(L"Some longer text then width of CListBox");
//Calculate horizontal extent and set it through
lst.SetHorizontalExtent(calculatedWidth);
//Try to scroll text (scrolls only scroll bar, not text itself)
lst.SetScrollPos(SB_HORZ, horizScroll, TRUE);
UpdateData(FALSE);
Thanks in advance!
EDIT:
As "rrirower" answered correctly,
lst.PostMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 250), 0);
message does the job. Scroll position from horizScroll works perfectly. I suggest posting this message twice, because if you do it only once, text is re-scrolled visually from beginning to the wanted position. When you post it twice, text visually stays at the correct position and scroll bar just quickly comes to the right place.
If I understand you correctly, you're trying to scroll the text in the list box horizontally using the program code. If you use Spy++, you'll see that when you manually scroll, using the mouse, a series of WM_HSCROLL messages is posted to the list box control. You can accomplish the same thing by doing this...
lst.PostMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 250), 0);
You need to calculate the position (I used 250 above), but, the above code should move the text and the scroll bar horizontally.
After some reading it seems that Invalidate should do the trick. Since as I understand you have one text line this should be fine, however if the painting itself is complex and requires resources you can use ScrollWindowEx and then InvalidateRect on the rectangle returned by the latter to repaint only the changed area.

How to set a bitmap as the background of a CHILD window (WIN32)

First of all this is not MFC.
Here is a cropped version of the GUI I have been working on:
As you can see there is a bitmap image above the tab control (which uses the default window's handle), I will refer to it as "Blue Bitmap" I have no problems with it, it is functioning perfectly.
The problem I am having is setting another bitmap (from a bitmap file on disk) to the background of the child window (more specifically the tab control child window), replacing the grey colour. Here are some things I did to try and set the bitmap as the background for the child window (the tab).
1) I used the same method as was used to assign the blue bitmap to the window by first importing the bitmap using LoadImage like so:
index->hbmBitmapBanner = (HBITMAP)LoadImage(index->hInstance,L"Images\\horizontal.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
Then in the WM_PAINT message:
case WM_PAINT:
{
PAINTSTRUCT ps1;
if (BeginPaint(WINDOWHANDLE,&ps1))
{
HDC hdcBanner = CreateCompatibleDC(ps1.hdc);
HBITMAP hbmOldHorizontal = (HBITMAP)SelectObject(hdcBanner,index->hbmBitmapBanner); // banner
BitBlt(ps1.hdc,0,0,516,101,hdcBanner,5,0,SRCCOPY);
SelectObject(hdcBanner,hbmOldHorizontal);
DeleteDC(hdcBanner);
EndPaint(WINDOWHANDLE,&ps1);
}
break;
}
That code was successful in setting blue bitmap to the window, but not in setting the other bitmap into the window, (with the blue bitmap's variables substituted into, size and orientation changed as well of course). It simply did not display the bitmap on the screen, and the window remained unchanged, yet no functions failed. ALSO, when I swapped the file locations around, it loaded the bitmap into where the blue bitmap was, so there is definitely no failure in the loading function.
2) Using the above code, I tried changing the first argument of BeginPaint to the window handle of the tab control box, this proved yet again of no use, and the window remained unchanged. I also tried changing the first argument of BeginPaint to a child window which is displayed on the information tab, this caused the child window (which is a groupbox) to disappear, yet still no bitmap appeared on the window.
3) As a last resort I stupidly attempted to use this function on the child window
SetClassLongPtr(tab->hTabIndex[0],GCLP_HBRBACKGROUND,(DWORD)GetStockObject(BLACK_BRUSH));
And of course, it didn't work
If it is of any use, here is how I created the tabbing in my window:
TCITEM tie = {0};
tab->hTab = CreateWindowEx(0,WC_TABCONTROL,L"",WS_CHILD | WS_VISIBLE,0,101,600,400,
WINDOWHANDLE,NULL,(HINSTANCE)GetWindowLong(WINDOWHANDLE,GWLP_HINSTANCE),NULL
);
Is the main tab control, followed by inserting individual the tabs (i.e the information tab)
TCHAR pszTab1 [] = L"Information"; // tab1's text
tie.pszText = pszTab1; // the tab's text/caption
TabCtrl_InsertItem(tab->hTab, 0, &tie); // insert the tab
Additional info: The tab control, and the windows are working/interacting perfectly apart from this. I was testing/attempting this for only one tab, because I knew if it worked on one tab, then it would work on all the tabs, hence saving time.
I have Programming For Windows Fifth Edition, by Charles Petzold beside me as reference, and there is a rather large section on bitmaps, but he doesn't go into talking about loading a bitmap to a child window, the closest he gets is using a bitmap in a menu, which is completely different than a window.
I think that the issue here is that Windows Common Controls do not interact with your application via your main thread's window procedure but via their own (system-defined) window procedures.
So, for example, when your tab window redraws itself, it does not do so in response to a WM_PAINT message arriving at the window procedure defined in your code. You should be able to verify for yourself that this is so by examining the window handles of messages passed to your window procedure, eg WM_PAINT.
If you wish to 'tap into' the default behaviour of a common control you have to 'subclass' it (see here) but in my experience attempting to change the re-draw behaviour is generally problematic.
In the case of a tab control the best thing to do is create an array of child windows sized to the client area of the tab control and arrange that the one displayed at any one time corresponds to the tab that is selected.
If you're using C++ you might consider creating a base class to wrap these windows in which the background (bitmap) drawing is handled. You can then derive a series of wrappers to handle each individual tab. I've used exactly this approach in the past and it works well.
It may be helpful to bear in mind that the tabs of the tab control do not extend over the whole of the control's client area but are just literally the tabs themselves. I don't remember the details but I think they are handled internally by the control and that manipulating them in any way is rather trick, even when the control is subclassed - may be wrong about that.
Hope that helps.
Cheers, Ian.

MFC PreCreateWindow to create a window that fill the screen except for the taskbar

I would like to create a window that fills the entire space of the desktop without including the Windows start menu, and without maximizing the window.
I think the code should be something like this :
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
cs.cx = ::GetSystemMetrics(SM_CXSCREEN);
cs.cy = ::GetSystemMetrics(SM_CYSCREEN); // minus start menu height please
return CMDIFrameWnd::PreCreateWindow(cs);
}
But how do I get the height of the start menu ? Thanks.
To avoid the taskbar and any appbars, you want to fill the work area. Use the GetMonitorInfo function and look at the rcWork member. (Note that each monitor has a different work area, so you need to know which monitor you care about.)