使用MoveWindow或SetWindowPos时,编辑控件/组合框的窗口大小未正确调整 [英] Window size of edit control/combobox is not properly adjusted when using MoveWindow or SetWindowPos
问题描述
介绍和相关信息:
我试图用可编辑的项目和子项目实现listview控件。
我使用raw WinAPI
C ++
。我的目标是 Windows XP
。 我的努力来解决问题:
在这里和在互联网上研究后,我只能在 MFC
中找到例子。他们都使用 LVN_BEGINLABELEDIT
技术来实现此行为。
不幸的是,我不完全理解这个概念,所以我决定从头开始(我认为这也是提高编程技能的最佳方法)。
我的意思:
我决定捕捉 NM_DBLCLK
用于listview,并使用 ListView_GetItemRect
或 ListView_GetSubItemRect
宏来获取坐标。
然后,我只需将组合框/复选框/编辑控件移动到相应的项目/子项(组合框/编辑控件/复选框将被创建为单独的,隐藏的窗口)。
用户完成输入后(通过按Enter或更改焦点),我将只隐藏组合框/复选框/编辑控件。
我当前的结果:
目前,我遇到了组合框/编辑控件/
问题:
>我可以改进下面提交的我的代码示例以正确调整组合框/编辑控件/复选框窗口大小到项目/子项的大小?现在,我将只关注这一部分的问题,
这是创建小应用程序说明问题的说明。请注意,我已尽量保持尽可能小的功能:
1。)创建默认 Win32项目
Visual Studio
(我使用 VS 2008
)。
2.)向主窗口的过程添加以下 WM_CREATE
处理程序:
case WM_CREATE:
{
HWND hEdit = CreateWindowEx(0,WC_EDIT,L,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER | ES_AUTOHSCROLL,
250,10,100,20 ,hWnd,(HMENU)1500,hInst,0);
HWND hComboBox = CreateWindowEx(0,WC_COMBOBOX,L,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST,
100,10,100,20,hWnd,(HMENU) 1600,hInst,0);
HWND hwndLV = CreateWindowEx(0,WC_LISTVIEW,
L可编辑子项,
WS_CHILD | WS_VISIBLE | WS_BORDER |
LVS_REPORT | LVS_SINGLESEL,
,100,250,150,hWnd,(HMENU)2000,hInst,0);
//设置扩展listview样式
ListView_SetExtendedListViewStyle(GetDlgItem(hWnd,2000),
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
//添加一些列
LVCOLUMN lvc = {0};
lvc.iSubItem = 0;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for(long nIndex = 0; nIndex< 5; nIndex ++)
{
wchar_t txt [50]
swprintf_s(txt,50,LColumn%d,nIndex + 1);
lvc.iSubItem = nIndex;
lvc.cx = 60;
lvc.pszText = txt;
ListView_InsertColumn(GetDlgItem(hWnd,2000),nIndex,& lvc);
}
//添加一些项目
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = 0; (long nIndex = 0; nIndex <5; nIndex ++)
{
(lvi.iItem = 0; lvi.iItem< 10; lvi.iItem ++)
wchar_t txt [50];
swprintf_s(txt,50,LItem%d%d,lvi.iItem + 1,nIndex + 1);
lvi.iSubItem = nIndex;
lvi.pszText = txt;
if(!nIndex)// item
SendDlgItemMessage(hWnd,2000,
LVM_INSERTITEM,0,
reinterpret_cast< LPARAM>(& lvi)
else // sub-item
SendDlgItemMessage(hWnd,2000,
LVM_SETITEM,0,
reinterpret_cast< LPARAM>(& lvi)
}
}
return 0L;
3)为 WM_NOTIFY
在主窗口的过程中:
case WM_NOTIFY:
{
if(((LPNMHDR)lParam) - > code == NM_DBLCLK)
{
switch(((LPNMHDR)lParam) - > idFrom)
{
case 2000://记住,这是我们的listview ID
{
LPNMITEMACTIVATE lpnmia =(LPNMITEMACTIVATE)lParam;
// SHIFT / ALT / CTRL /它们的组合,不能被按下
if((lpnmia-> uKeyFlags || 0)== 0)
{
//这是我们存储item / subitem rectangle的地方
RECT rc = {0,0,0,0};
if((lpnmia-> iSubItem)< = 0)//这是项目所以我们必须调用ListView_GetItemRect
{
//此矩形保存正确的左坐标
//因为ListView_GetItemRect与LVIR_LABEL标志
//弄乱矩形的左边cordinate
RECT rcHelp = {0,0,0,0};
//这个调用获取整行的长度
//但是保留了左边的正确坐标
ListView_GetItemRect(lpnmia-> hdr.hwndFrom,
lpnmia-> ; iItem,& rcHelp,LVIR_BOUNDS);
//这个调用获得正确的矩形,除了左边
ListView_GetItemRect(lpnmia-> hdr.hwndFrom,
lpnmia-> iItem,& rc,LVIR_LABEL) ;
//现在我们可以更正左边的坐标
rc.left = rcHelp.left;
}
else //它是子项,因此我们必须调用ListView_GetSubItemRect
{
ListView_GetSubItemRect(lpnmia-> hdr.hwndFrom,
lpnmia-> iItem, lpnmia-> iSubItem,
LVIR_BOUNDS,& rc);
}
//将listview客户端坐标转换为父坐标
//所以编辑控制可以正确移动
POINT p;
p.x = rc.left;
p.y = rc.top;
ClientToScreen(lpnmia-> hdr.hwndFrom,& p);
ScreenToClient(hWnd,& p);
MoveWindow(GetDlgItem(hWnd,1500),
px,py,
rc.right - rc.left,
rc.bottom - rc.top,TRUE );
//将焦点设置为我们的编辑控件
HWND previousWnd = SetFocus(GetDlgItem(hWnd,1500));
}
}
break;
默认值:
break;
}
}
}
break;
这是我得到的结果:
您可以清楚地看到编辑控件的顶部和底部边框没有正确绘制。对于combobox,宽度已正确调整,但高度保持不变。
我已尝试替换 MoveWindow
与 SetWindowPos
但结果是一样的。
进一步篡改后,我发现 NMITEMACTIVATE
在返回子项,如果listview未设置LVS_EX_FULLROWSELECT样式。你可以通过简单的注释掉我的 WM_CREATE
处理器中的部分,我设置了这种风格。
在2014年9月17日更新:
/ p>测试 iItem
和 iSubItem
的成员 NMITEMACTIVATE
结构当listview没有 LVS_EX_FULLROWSELECT
我可以验证错误不在我的代码。它总是返回 iItem
为0,无论我点击哪个子项。
如果需要任何进一步的信息,请发表评论,我会尽快采取行动。
您遇到的问题,面对是多面的。
首先,编辑控件的默认字体大于(高于)列表视图的字体。你可以解决这个问题相当简单,首先从列表视图中获取字体,然后将其设置为编辑控件。这样做会使控件的底部边框可见。
下一个问题是编辑控件的插入符号需要在其上方和下方有一个像素,以确保该控制没有受到其边界的干扰。除了这个1像素的'空间',你需要另一个像素的边框。
添加到第二点,由 rc.right - rc.left
和 rc.bottom - rc.top
是1像素太小。想一个rect从1,1开始,并延伸到2,2 - 这是一个4像素的矩形 - 2宽和2高。简单地从底部/右边减去顶部/左边会给你一个宽度/高度每个只有1个像素。要解决这个问题,您需要为每个减法添加1。
最后,由于插入符号正是每个项目的客户区子项目,您需要使编辑控件比该项目/子项目高2像素,并开始 1 高于当前的2像素。
以下是我在进行建议更改时获得的输出:
这是我做的更改/添加。
1。获取/设置字体(在创建列表视图之后插入并设置其扩展样式之前)
lvFont =(HFONT)SendDlgItemMessage(hWnd,2000,WM_GETFONT,0,0);
SendDlgItemMessage(hWnd,1500,WM_SETFONT,(WPARAM)lvFont,TRUE);
2。设置窗口位置/大小
MoveWindow(GetDlgItem(hWnd,1500),
px,py -2,
1+ rc.right - rc.left,
1+ 2 + rc.bottom - rc.top,TRUE);
最后,与您的代码的原始输出形成对比:
UPDATE:
下面是使用内置标签编辑功能时的外观快照(LVS_EDITLABELS样式)
INTRODUCTION AND RELEVANT INFORMATION:
I am trying to implement listview control with editable items and subitems. Instead of regular listview look, items and subitems should have edit control, checkbox or combo box.
I am using raw WinAPI
and C++
. I am targeting Windows XP
onwards.
MY EFFORTS TO SOLVE THE PROBLEM:
After researching here and on the Internet, I was able to only find examples in MFC
. They all use LVN_BEGINLABELEDIT
technique to implement this behavior.
Unfortunately I do not understand entirely this concept so I have decided to start from scratch ( I consider this also to be the best approach for improving ones programming skills ).
MY CONCEPT:
I have decided to catch NM_DBLCLK
for listview and to get coordinates from there using ListView_GetItemRect
or ListView_GetSubItemRect
macro.
Then I would simply move the combobox/checkbox/edit control over corresponding item/subitem ( combobox/edit control/checkbox would be created as separate, hidden windows ).
After user finishes with input ( by pressing enter or changing focus ) I would simply hide the combobox/checkbox/edit control.
MY CURRENT RESULTS:
At the moment, I am stuck with the dimensions of combobox/edit control/checkbox not being the same as item/subitem dimensions, when moved above the item/subitem.
QUESTION:
Can my code example submitted below be improved to properly adjust combobox/edit control/checkbox window size to the size of the item/subitem? For now, I will only focus on this part of the problem, to keep this question as short as possible.
Here is the instruction for creating small application that illustrates the problem. Notice that I have tried to keep things as minimal as I could:
1.) Create default Win32 project
in Visual Studio
( I use VS 2008
).
2.) Add the following WM_CREATE
handler to main window's procedure:
case WM_CREATE:
{
HWND hEdit = CreateWindowEx( 0,WC_EDIT, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER | ES_AUTOHSCROLL,
250, 10, 100, 20, hWnd, (HMENU)1500, hInst, 0 );
HWND hComboBox = CreateWindowEx( 0,WC_COMBOBOX, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST,
100, 10, 100, 20, hWnd, (HMENU)1600, hInst, 0 );
HWND hwndLV = CreateWindowEx( 0, WC_LISTVIEW,
L"Editable Subitems",
WS_CHILD | WS_VISIBLE | WS_BORDER |
LVS_REPORT | LVS_SINGLESEL,
150, 100, 250, 150, hWnd, (HMENU)2000, hInst, 0 );
// set extended listview styles
ListView_SetExtendedListViewStyle( GetDlgItem( hWnd, 2000 ),
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER );
// add some columns
LVCOLUMN lvc = {0};
lvc.iSubItem = 0;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Column %d", nIndex + 1 );
lvc.iSubItem = nIndex;
lvc.cx = 60;
lvc.pszText = txt;
ListView_InsertColumn( GetDlgItem( hWnd,2000 ), nIndex, &lvc );
}
// add some items
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = 0;
for( lvi.iItem = 0; lvi.iItem < 10; lvi.iItem++ )
for (long nIndex = 0; nIndex < 5; nIndex++ )
{
wchar_t txt[50];
swprintf_s( txt, 50, L"Item %d%d", lvi.iItem + 1, nIndex + 1 );
lvi.iSubItem = nIndex;
lvi.pszText = txt;
if( ! nIndex ) // item
SendDlgItemMessage( hWnd, 2000,
LVM_INSERTITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
else // sub-item
SendDlgItemMessage( hWnd, 2000,
LVM_SETITEM, 0,
reinterpret_cast<LPARAM>(&lvi) );
}
}
return 0L;
3.) Add the following handler for WM_NOTIFY
in main window's procedure:
case WM_NOTIFY:
{
if( ((LPNMHDR)lParam)->code == NM_DBLCLK )
{
switch( ((LPNMHDR)lParam)->idFrom )
{
case 2000: // remember, this was our listview's ID
{
LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam;
// SHIFT/ALT/CTRL/their combination, must not be pressed
if( ( lpnmia->uKeyFlags || 0 ) == 0 )
{
// this is where we store item/subitem rectangle
RECT rc = { 0, 0, 0, 0 };
if( (lpnmia->iSubItem) <= 0 ) // this is item so we must call ListView_GetItemRect
{
// this rectangle holds proper left coordinate
// since ListView_GetItemRect with LVIR_LABEL flag
// messes up rectangle's left cordinate
RECT rcHelp = { 0, 0, 0, 0 };
// this call gets the length of entire row
// but holds proper left coordinate
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rcHelp, LVIR_BOUNDS );
// this call gets proper rectangle except for the left side
ListView_GetItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, &rc, LVIR_LABEL );
// now we can correct the left coordinate
rc.left = rcHelp.left;
}
else // it is subitem, so we must call ListView_GetSubItemRect
{
ListView_GetSubItemRect( lpnmia->hdr.hwndFrom,
lpnmia->iItem, lpnmia->iSubItem,
LVIR_BOUNDS, &rc );
}
// convert listview client coordinates to parent coordinates
// so edit control can be properly moved
POINT p;
p.x = rc.left;
p.y = rc.top;
ClientToScreen( lpnmia->hdr.hwndFrom, &p );
ScreenToClient( hWnd, &p );
MoveWindow( GetDlgItem( hWnd, 1500 ),
p.x, p.y,
rc.right - rc.left,
rc.bottom - rc.top, TRUE );
// set focus to our edit control
HWND previousWnd = SetFocus( GetDlgItem( hWnd, 1500 ) );
}
}
break;
default:
break;
}
}
}
break;
And this is the result I get:
You can clearly see that top and bottom border of the edit control are not drawn properly. As for combobox, the width is properly adjusted, but height remains the same.
I have tried substituting MoveWindow
call with SetWindowPos
but the result was the same.
After further tampering, I have found out that NMITEMACTIVATE
bugs when returning the rectangle of a subitem, if listview doesn't have LVS_EX_FULLROWSELECT style set. You can see this by simply commenting out the part in my WM_CREATE
handler where I set this style. Maybe I am doing something wrong and this "bug" may be caused by my code, but I don't see the problem.
EDITED on September, 17th 2014:
After testing the values for iItem
and iSubItem
members of NMITEMACTIVATE
structure when listview doesn't have LVS_EX_FULLROWSELECT
I can verify that the bug is not in my code. It always returns iItem
to be 0, no matter which subitem I click. This explains the faulty behavior I got when removing this style.
If any further info is required please leave a comment and I will act as soon as possible.
Thank you for your time and efforts to help.
The issue you're facing is multi-faceted.
Firstly, the default font of the edit control is larger (higher) than that of the list-view. You can fix this one quite trivially, by first getting the font from the list-view and then setting it to the edit control. Doing this will then make the bottom border of the control visible.
The next issue is that the caret of the edit control needs a pixel above and below it, to ensure that the control doesn't have its borders interfered with. In addition to this 1 pixel of 'space' you then need another pixel for the border.
Added to this second point, the dimensions calculated by rc.right - rc.left
and rc.bottom - rc.top
are 1 pixel too small. Think of a rect that starts at 1,1 and extends to 2,2 - this is a rect of 4 pixels - 2 wide and 2 high. Simply subtracting the top/left from the bottom/right would give you a width/height of only 1 pixel each. To fix this, you need to add 1 to each of these subtractions.
Finally, since the caret is exactly the height of the 'client-area' of each item/sub-item, you need to make the edit control 2 pixels taller than the item/sub-item, and start 1 2 pixels higher than it does currently.
Here's the output I get when making the suggested changes:
And here's the changes/additions I made.
1. Get/Set the font. (inserted after creating the list-view and before setting its extended style)
HFONT lvFont = (HFONT)SendDlgItemMessage(hWnd, 2000, WM_GETFONT, 0, 0);
SendDlgItemMessage(hWnd, 1500, WM_SETFONT, (WPARAM)lvFont, TRUE);
2. Set the window position/size
MoveWindow( GetDlgItem( hWnd, 1500 ),
p.x, p.y-2,
1+ rc.right - rc.left,
1+ 2 + rc.bottom - rc.top, TRUE );
Finally, contrast this against the original output from your code:
UPDATE: Here's a snapshot of the appearance when the built-in label editing functionality is used (LVS_EDITLABELS style)
这篇关于使用MoveWindow或SetWindowPos时,编辑控件/组合框的窗口大小未正确调整的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!