使用MoveWindow或SetWindowPos时,编辑控件/组合框的窗口大小未正确调整 [英] Window size of edit control/combobox is not properly adjusted when using MoveWindow or SetWindowPos

查看:1431
本文介绍了使用MoveWindow或SetWindowPos时,编辑控件/组合框的窗口大小未正确调整的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

介绍和相关信息:



我试图用可编辑的项目和子项目实现listview控件。



我使用raw WinAPI

code>和 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆