I am trying to make a simple spin control and edit box in WTL &/ winapi. and this does not work properly, because I only see 0 as initial value and the arrows don t work, code here:
HWND spin = GetDlgItem(IDC_SPIN1);
HWND edit = GetDlgItem(IDC_RANDOM_EDIT);
::SendMessage(spin, UDM_SETBUDDY, (WPARAM)edit, 0); //set buddy
::SendMessage(spin, UDM_SETRANGE, MAKELPARAM(0,100), 0); //interval
::SendMessage(spin, UDM_SETBASE, 10, 0); //initial position
You have your wparam and lparam reversed. You also have the low and high words reversed.
::SendMessage(spin, UDM_SETRANGE, 0, MAKELPARAM(100,0)); //interval
See the definitions of UDM_SETRANGE and MAKELPARAM.
In WTL you have wrapper class CUpDownCtrl for up-down control. So it goes as simple as this:
CUpDownCtrl Control = ... // e.g. GetDlgItem(IDC_MYCONTROL);
INT nMinValue = 0, nMaxValue = 100;
Control.SetRange(nMinValue, nMaxValue);
Related
i'm trying draw a rectangle roundend around of control, i've done something like this:
LRESULT CALLBACK WindPorc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
case WM_CTLCOLOREDIT:
{
// device handle
HDC Chdc = (HDC)wparam;
// handle to control
HWND CHand = (HWND)lparam;
// new object pen...
HPEN penx = CreatePen(PS_SOLID, 5, RGB(0, 255, 255));
// apply pen to device handle and back up the original handle
HGDIOBJ objb = SelectObject(Chdc, penx);
// auto...
RECT rectx;
GetClientRect(CHand, &rectx);
// Now draw the rect with round borders...
RoundRect(Chdc, (rectx.left -2), (rectx.top - 2), (rectx.right + 2), (rectx.bottom + 2), 5, 5);
// reset device hand...
SelectObject(Chdc, objb);
// Set text color...
SetTextColor(Chdc, RGB(0, 255, 255));
// clean up...
DeleteObject(penx);
DeleteObject(objb);
// I should return a brush for the bk, but this don't care now...
//return;
}
}
void InitUI()
{
// just the edit control, isn't need the RegisterClass etc...
HWND Edit1 = CreateWindowEx
(
NULL,
L"EDIT",
NULL,
WS_CHILD | WS_VISIBLE |
ES_LEFT | ES_MULTILINE, // Just a demonstration, it should be ES_PASSWORD
10, 120, 200, 22,
winx, // parent window HWND...
(HMENU)TEXTBOX_1, // id
(HINSTANCE)GetWindowLong(winx, GWL_HINSTANCE),
NULL
);
}
ok, this would look like this:
but if i change the style on CreateWindowEx(); to ES_PASSWORD the edges disappear, leaving something like this:
Why do they disappear?
I would say the implementation is wrong in multiple ways:
Chdc is a DC that can draw on the client area of the edit control only, while you want to draw on the parent window's (dialog's) client area.
GetClientRect() always returns (0,0) for the top-left corner, as the rectangle is relative to... the edit control itself.
WM_CTLCOLOREDIT may not be the best place to draw the rectangle, you need to make some tests to check when this message is actually called.
If you wish to keep the implementation this way, you should:
Draw using the parent window's DC, not the edit control's one.
Instead of GetClientRect() call GetWindowRect() and then ScreenToClient() to make coordinates relative to the parent window's client area.
Use the WM_PAINT message (and the BeginPaint()/EndPaint() functions) to get a DC for the parent window.
I would consider a different implementation though, create an Owner-Drawn Static Control, some few pixels larger than the edit control, placed somehow "around" the edit control (set the WS_CLIPSIBLINGS style to make sure that the edit control stays on top of the static one). Drawing should be carried-out processing the WM_DRAWITEM message. I think this implementation is more robust.
I am trying to make a search edit control in MFC that has an icon displayed in the control window all the time (regardless the state and text of the control). I have written something like this many years ago and worked very well, but the code no longer works on Windows 7 and newer (maybe even Vista, but did not try that). What happens is that the image shown in the control is overlapped with the input area (see the picture below).
The idea behind the code:
have a class derived from CEdit (that handles painting in OnPaint)
the icon is displayed on the right and the edit area is shrunk based on the size of the icon
resizing is done differently for single-line and multiline edits. For single line I call SetMargins and for multiline edits I call SetRect.
this edit resizing is applied in PreSubclassWindow(), OnSize() and OnSetFont()
This is how the edit input size is applied:
void CSymbolEdit::RecalcLayout()
{
int width = GetSystemMetrics( SM_CXSMICON );
if(m_hSymbolIcon)
{
if (GetStyle() & ES_MULTILINE)
{
CRect editRect;
GetRect(&editRect);
editRect.right -= (width + 6);
SetRect(&editRect);
}
else
{
DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);
}
}
}
The following image shows the problem with the single line edits (the images have been zoomed in for a better view). The yellow background is for highlighting purposes only, in real code I am using the COLOR_WINDOW system color. You can see that when the single line edit has text and has the input the left side image is painted over. This does not happen with the multiline edit where SetRect correctly sets the formatting rectangle.
I have tried using ExcludeClipRect to remove the area of the edit where the image is being displayed.
CRect rc;
GetClientRect(rc);
CPaintDC dc(this);
ExcludeClipRect(dc.m_hDC, rc.right - width - 6, rc.top, rc.right, rc.bottom);
DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);
This does not seem to have any effect on the result.
For reference, this is the painting method, written years ago and used to work well on Windows XP, but not correct any more.
void CSymbolEdit::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect( &rect );
// Clearing the background
dc.FillSolidRect( rect, GetSysColor(COLOR_WINDOW) );
DWORD dwMargins = GetMargins();
if( m_hSymbolIcon )
{
// Drawing the icon
int width = GetSystemMetrics( SM_CXSMICON );
int height = GetSystemMetrics( SM_CYSMICON );
::DrawIconEx(
dc.m_hDC,
rect.right - width - 1,
1,
m_hSymbolIcon,
width,
height,
0,
NULL,
DI_NORMAL);
rect.left += LOWORD(dwMargins) + 1;
rect.right -= (width + 7);
}
else
{
rect.left += (LOWORD(dwMargins) + 1);
rect.right -= (HIWORD(dwMargins) + 1);
}
CString text;
GetWindowText(text);
CFont* oldFont = NULL;
rect.top += 1;
if(text.GetLength() == 0)
{
if(this != GetFocus() && m_strPromptText.GetLength() > 0)
{
oldFont = dc.SelectObject(&m_fontPrompt);
COLORREF color = dc.GetTextColor();
dc.SetTextColor(m_colorPromptText);
dc.DrawText(m_strPromptText, rect, DT_LEFT|DT_SINGLELINE|DT_EDITCONTROL);
dc.SetTextColor(color);
dc.SelectObject(oldFont);
}
}
else
{
if(GetStyle() & ES_MULTILINE)
CEdit::OnPaint();
else
{
oldFont = dc.SelectObject(GetFont());
dc.DrawText(text, rect, DT_SINGLELINE | DT_INTERNAL | DT_EDITCONTROL);
dc.SelectObject(oldFont);
}
}
}
I have looked at other implementations of similar edit controls and they all have the same fault now.
Obviously, the question is how do I exclude the image area from the input area of the control?
I think what's going on is that CPaintDC calls BeginPaint(), which sends a WM_ERASEBKGND to the edit box. I wasn't able to ignore it, so I guess it's being sent to maybe an internal STATIC window? Not sure.
Calling ExcludeClipRect() in your OnPaint() handler won't do anything because EDIT will reset the clipping region to the whole client area in either BeginPaint() or its own WM_PAINT handler.
However, EDIT sends a WM_CTRCOLOREDIT to its parent just before painting itself, but seemingly after setting the clipping region. So you can call ExcludeClipRect() there. Sounds like an implementation detail which may change with future versions of the common controls. Indeed, it seems to have done so already.
I did a quick test without MFC on Windows 7, here's my window procedure:
LRESULT CALLBACK wnd_proc(HWND h, UINT m, WPARAM wp, LPARAM lp)
{
switch (m)
{
case WM_CTLCOLOREDIT:
{
const auto dc = (HDC)wp;
const auto hwnd = (HWND)lp;
RECT r;
GetClientRect(hwnd, &r);
// excluding the margin, but not the border; this assumes
// a one pixel wide border
r.left = r.right - some_margin;
--r.right;
++r.top;
--r.bottom;
ExcludeClipRect(dc, r.left, r.top, r.right, r.bottom);
return (LRESULT)GetStockObject(DC_BRUSH);
}
}
return ::DefWindowProc(h, m, wp, lp);
}
I then subclassed the EDIT window to draw my own icon in WM_PAINT, and then forwarded the message so I didn't have to draw everything else myself.
LRESULT CALLBACK edit_wnd_proc(
HWND h, UINT m, WPARAM wp, LPARAM lp,
UINT_PTR id, DWORD_PTR data)
{
switch (m)
{
case WM_PAINT:
{
const auto dc = GetDC(h);
// draw an icon
ReleaseDC(h, dc);
break;
}
}
return DefSubclassProc(h, m, wp, lp);
}
Note that I couldn't call BeginPaint() and EndPaint() (the equivalent of constructing a CPaintDC) in WM_PAINT because the border wouldn't get drawn. I'm guessing it has something to do with calling BeginPaint() twice (once manually, once by EDIT) and the handling of WM_ERASEBKGND. YMMV, especially with MFC.
Finally, I set the margins right after creating the EDIT:
SendMessage(
e, EM_SETMARGINS,
EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, margin));
You might also have to update the margins again if the system font changes.
Have a look at this tutorial... from www.catch22.net. It gives a clear picture of how to insert a button into edit control. Though it is an Win32 example, this can be improvised to MFC as the basic structure of MFC is using win32 apis.
http://www.catch22.net/tuts/win32/2001-05-20-insert-buttons-into-an-edit-control/#
It uses WM_NCCALCSIZE to restrict the text control.
Could you give the simplest way to achieve double buffering for this example code (to prevent flickering):
HWND hwnd = FindWindow(0, "Untitled - Notepad");
HDC hDC_Desktop = GetDC(hwnd);
...
while( )
{
RECT rect = { 10, 10, 10 + 50, 10 + 50 };
FillRect(hDC_Desktop, &rect, ColorBrush);
InvalidateRect (hwnd, NULL, TRUE);
}
The reason it's "flickering" is because the target window is getting invalidated and it is being redrawn. Since it's not your window - you don't necessarily have control over that.
If this was your own window there is a simple strategy to speed up your drawing speed and reduce flicker: Use a Memory DC to draw on and capture WM_ERASEBKGND to suppress background redraws.
In depth explanation and strategy for fixing it (in your application's window): http://www.catch22.net/tuts/win32/flicker-free-drawing
If your intent is to draw on another application, might I suggest creating a window on top of that application and draw on that.
Does anybody know how to make a 'always-on-bottom'-windows, or a window pinned to the desktop? It should receive focus and mouseclicks, but should stay at the bottom of the Z-order. It would also be great if it could stay on the desktop even when user do a minimize all or show desktop operation.
Both delphi and c# solutions (or partial solutions/hints) would be great.
Warning It was suggested that you can accomplish this by calling SetParent and setting the window to be a child of the Desktop. If you do this, you cause the Win32 Window Manager to combine the input queue of the Desktop to your child window, this is a bad thing - Raymond Chen explains why.
Also, keep in mind that calling SetWindowPos with HWND_BOTTOM is incomplete. You need to do this whenever your window is changing zorder. Handle the WM_WINDOWPOSCHANGING event, look at SWP_NOZORDER for more info.
SetWindowPos can make windows AlwaysOnTop. Most likely it can give the opposite result. Try something along these lines:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
public const uint SWP_NOSIZE = 0x0001;
public const uint SWP_NOMOVE = 0x0002;
public const uint SWP_NOACTIVATE = 0x0010;
public const int HWND_BOTTOM = 1;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
Note:
Haven't tested this approach (for making windows always on bottom)
If it happens to work, then most likely the show desktop operation will hide the window. So maybe you should go even deeper into this 'nice' API.
EDIT: Done some searching along these lines to confirm whether it will do the trick and found something interesting - a duplicate.
Here is solution for ATL window.
If you can apply to c#, it will help you.
BEGIN_MSG_MAP(...)
...
MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
...
END_MSG_MAP()
LRESULT OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (_bStayOnBottom)
{
auto pwpos = (WINDOWPOS*)lParam;
pwpos->hwndInsertAfter = HWND_BOTTOM;
pwpos->flags &= (~SWP_NOZORDER);
}
return 0;
}
I am creating a modeless property sheet using the following settings:
PROPSHEETHEADER pshdr = { 0 };
pshdr.dwSize = sizeof(PROPSHEETHEADER);
pshdr.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE |
PSH_MODELESS | PSH_USECALLBACK;
pshdr.pfnCallback = PropSheetProc;
pshdr.hwndParent = mGlobalState->trayWin;
pshdr.pszCaption = L"My Settings";
pshdr.nPages = mPages.size();
pshdr.ppsp = mWinPages;
In PropSheetProc, I catch the PSCB_PRECREATE message and modify the dialog template so that it gets the DS_CENTER style:
static int CALLBACK
PropSheetProc(HWND hwndDlg, // IN
UINT uMsg, // IN
LPARAM lParam) // IN
{
// Before the dialog is created, bless it with the DS_CENTER style.
if (uMsg == PSCB_PRECREATE) {
DLGTEMPLATE *dlgTemplate = (DLGTEMPLATE *)lParam;
_ASSERT(dlgTemplate);
dlgTemplate->style |= DS_CENTER;
}
return 0;
}
However this doesn't succeed in centering the dialog. I tried to catch PSCB_INITIALIZED instead and call a CenterWindow method on the hwnd passed to the PropSheetProc:
void
CenterWindow(HWND hwndWindow) // IN
{
int nX, nY, nScreenWidth, nScreenHeight;
RECT rectWindow;
nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
GetWindowRect(hwndWindow, &rectWindow);
nX = (nScreenWidth - (rectWindow.right - rectWindow.left)) / 2;
nY = (nScreenHeight - (rectWindow.bottom - rectWindow.top)) / 2;
SetWindowPos(hwndWindow, 0, nX, nY, 0, 0,
SWP_NOZORDER | SWP_NOSIZE);
}
But that doesn't work either!
Finally, I moved the CenterWindow call to directly after the PropSheet call:
mHwnd = (HWND)PropertySheet(&pshdr);
CenterWindow(mHwnd);
return mHwnd != NULL;
And this DOES work, though on a heavily loaded system, the dialog flashes from its initial position over to its final position, which is suboptimal.
Using the PropSheetProc to modify the DLGTEMPLATE structure seems intuitive. Actually, I can apply other window styles. But DS_CENTER seems to have no effect. So what am I doing wrong? There's many ways I can work around this brokennness but why is it broken in the first place?
Overload the InitialUpdate() of the CPropertySheet, and place the CenterWindow() call there. This happens before the window is drawn on the screen, but after it is created, so it's hwnd will be valid. There is nothing broken. The dialog has to be Created to have a valid HWND. Alternatively, if your working with the resource editor you can set it's property to centered, and it will achieve the same result. Why are you overloading the WinProc for the propertysheet? The whole reason MFC uses message maps was to eliminate the need to even touch WinProc's.
If your using raw win api in a SDK style application ::
Handle WM_CREATE in the WinProc of the property sheet. The LPCREATE struct in the LPARAM will contain a valid HWND from the create call. Just make sure you pass the proper parameters back to WndProcDefault() otherwise window creation will fail.