妥善处理与编辑子项目的ListView子项编辑(或子项编辑抵消) [英] Properly handle subitem editing ( or canceling of subitem editing ) in listview with editable subitems

查看:334
本文介绍了妥善处理与编辑子项目的ListView子项编辑(或子项编辑抵消)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

引言:

我想实现可编辑的子项ListView控件。对于就地项目/子项目的编辑我用的编辑控件。

我相信,我已成功地妥善$上述项目/子项目的编辑控件的C $下将。

问题:

我不知道哪些事件我应该结束/取消子项目编辑(编辑隐藏控制,设置子项文本等),我应该怎么做。

为了澄清,我说当用户结​​束时刻/取消在就地编辑。

此时编辑控件不再需要,所以我应该隐藏它(我不喜欢每次都重新创建它,我认为,一旦建立,然后显示/需要的时候隐藏它更有效)。

我是针对行为的属性窗口在Visual Studio中(见附件图像清楚地看到我指的是窗口)。

我要实现编辑/取消当用户presses ESC键/点击另一个窗口这个窗口不会以同样的方式/滚动条上点击等。

我的努力解决这个问题:

使用谷歌,我发现了几个例子,但他们都老了,不解决所有的相关案件,所以这就是为什么我问这里寻求帮助。

不过,我能发现,我必须考虑的一个事件是的 EN_KILLFOCUS ,情况下,当用户presses <大骨节病> ESC / <大骨节病> ENTER 键,当用户点击超过编辑控件以外的东西的情况。

编辑:

我成功当用户点击另一个兄弟控制或交换机ALT + TAB窗口处理ESC和ENTER键,以及这种情况。我已经更新SSCCE有关修改

问:

为了实现默认行为一格(如果有一个用于Windows的应用程序),消息/事件必须我处理它?<​​/ P>

你能同时指出我应该在哪里编辑分项目,并隐藏编辑控件,并在那里我应该只是隐藏编辑控件?

编辑:

我唯一的问题仍然是,当用户点击列表视图滚动条,或在主窗口的背景来处理这种情况。我只是不知道如何处理这一点,将AP preciate所有帮助我能。

相关信息:

我使用Visual Studio 2013,在Windows 7的x86;

我开发的C ++使用原始WinAPI的;

SSCCE

下面是解决方案,我至今。我试图彻底发表评论,但如果需要更多的信息发表评论,我会更新我的职务。

 的#include&LT;&WINDOWS.H GT;
#包括LT&;&windowsx.h GT; //各种列表视图宏等
#包括LT&;&CommCtrl.h GT;
#包括LT&;&stdio.h中GT; // swprintf_s()//启用视觉样式
的#pragma评论(连接/ manifestdependency:\\类型='win32的'\\
                         名称= Microsoft.Windows.Common - 控制版本='6.0.0.0'\\
                         的ProcessorArchitecture ='*'公钥='6595b64144ccf1df'\\
                         语言='*'\\)//与普通链接控件库
的#pragma评论(LIB,comctl32.lib)//全局变量
HINSTANCE HINST;//列表视图子类过程
LRESULT CALLBACK ListViewSubclassProc(HWND HWND,UINT的消息,
    WPARAM wParam参数,LPARAM lParam的,
    UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
{
    开关(消息)
    {
    案例WM_VSCROLL:
    案例WM_HSCROLL:
        //如果编辑控件具有焦点拿走,并给列表视图
        如果(GetFocus()==函数GetDlgItem(的getParent(HWND),5000))
            SetFocus的(HWND); //使用WM_NEXTDLGCTL为DialogBox的!!!!
        打破;
    案例WM_NCDESTROY:
        :: RemoveWindowSubclass(HWND,ListViewSubclassProc,uIdSubclass);
        返回DefSubclassProc(HWND,消息的wParam,lParam的);
    }
    返回:: DefSubclassProc(HWND,消息的wParam,lParam的);
}//为编辑控件类过程
LRESULT CALLBACK InPlaceEditControl_SubclassProc(HWND HWND,UINT的消息,WPARAM wParam参数,LPARAM lParam的,
    UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
{
    开关(消息)
    {
    案例WM_GETDLG code:
        返回(DLGC_WANTALLKEYS | DefSubclassProc(HWND,消息的wParam,lParam的));
    案例WM_KILLFOCUS:
        的ShowWindow(HWND,SW_HIDE);
        返回DefSubclassProc(HWND,消息的wParam,lParam的);
    案例WM_CHAR:
        //处理该消息以避免消息发出蜂鸣声。
        开关(wParam中)
        {
        案例VK_RETURN:
            返回0L;
        案例VK_ESCAPE:
            返回0L;
        默认:
            返回:: DefSubclassProc(HWND,消息的wParam,lParam的);
        }
        打破;
    案例WM_KEYDOWN:
        开关(wParam中)
        {
        案例VK_RETURN:
        {
            //获取ListView控件的句柄
            HWND hwndLV =函数GetDlgItem(的getParent(HWND),2000年);
            //获取编辑控件的客户端矩形
            RECT RC = {0};
            GetClientRect(HWND,&安培; RC);
            //因为编辑控件谎言项目矩形内
            //我们可以测试任何坐标内编辑控件的
            //客户端矩形
            //我选择(rc.left,rc.top)
            MapWindowPoints(HWND,hwndLV,(LPPOINT)及RC(的sizeof(RECT)/的sizeof(POINT)));
            //获取项目和子项目指标
            LVHITTESTINFO lvhti = {0};
            lvhti.pt.x = rc.left;
            lvhti.pt.y = rc.top;
            ListView_SubItemHitTest(hwndLV,&安培; lvhti);
            //获取编辑控件的文本
            wchar_t的TXT [50] = L,;
            Edit_GetText(HWND,TXT,50);
            //编辑单元格文本
            ListView_SetItemText(hwndLV,lvhti.iItem,lvhti.iSubItem,TXT);
            //恢复焦点的ListView
            //这触发EN_KILLFOCUS
            //这将隐藏的编辑控件
            SetFocus的(hwndLV);
        }
            返回0L;
        案例VK_ESCAPE:
            SetFocus的(函数GetDlgItem(的getParent(HWND),2000));
            返回0L;
        默认:
            返回:: DefSubclassProc(HWND,消息的wParam,lParam的);
        }
        打破;
    案例WM_NCDESTROY:
        :: RemoveWindowSubclass(HWND,InPlaceEditControl_SubclassProc,uIdSubclass);
        返回DefSubclassProc(HWND,消息的wParam,lParam的);    }
    返回:: DefSubclassProc(HWND,消息的wParam,lParam的);
}
//主窗口过程
LRESULT CALLBACK的WndProc(HWND HWND,UINT MSG,WPARAM wParam参数,LPARAM lParam的)
{
    开关(MSG)
    {
    案例WM_CREATE:
    {
        // ================创建控件
        RECT REC = {0};
        GetClientRect(HWND,&安培; REC);        HWND hwndLV =函数CreateWindowEx(0,WC_LISTVIEW,
            L,WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS | LVS_REPORT,
            50,50,250,200,HWND,(HMENU)2000,HINST,0);
        //到位编辑控件
        HWND hwndEdit =函数CreateWindowEx(0,WC_EDIT,L,ES_AUTOHSCROLL | WS_CHILD | WS_BORDER,
            200,265,100,25,HWND,(HMENU)5000,HINST,0);
        //编辑控件必须具有相同的字体列表视图
        HFONT HF =(HFONT)的SendMessage(hwndLV,WM_GETFONT,0,0);
        如果(HF)
            SendMessage函数(hwndEdit,WM_SETFONT,(WPARAM)HF,(LPARAM)TRUE);
        //子类编辑控件,所以我们可以编辑子项与ENTER键,或者
        //取消与ESC编辑
        SetWindowSubclass(hwndEdit,InPlaceEditControl_SubclassProc,0,0);
        //设置的扩展列表视图风格
        ListView_SetExtendedListViewStyle(hwndLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
        //子类的ListView
        SetWindowSubclass(hwndLV,ListViewSubclassProc,0,0);        //添加一些列
        LVCOLUMN LVC = {0};        lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
        lvc.fmt = LVCFMT_LEFT;        为(长参数nIndex = 0;参数nIndex小于5;参数nIndex ++)
        {
            wchar_t的TXT [50];
            swprintf_s(TXT,50,L列%D,参数nIndex);            lvc.iSubItem =参数nIndex;
            lvc.cx = 60;
            lvc.pszText = TXT;            ListView_InsertColumn(hwndLV,参数nIndex,&安培; LVC);
        }        //添加一些项目
        LVITEM LVI;        lvi.mask = LVIF_TEXT;        为(lvi.iItem = 0; lvi.iItem&下; 10000; lvi.iItem ++)
        {
            为(长参数nIndex = 0;参数nIndex小于5;参数nIndex ++)
            {
                wchar_t的TXT [50];
                swprintf_s(TXT,50,L项目%D,lvi.iItem,参数nIndex);                lvi.iSubItem =参数nIndex;
                lvi.pszText = TXT;                如果(!参数nIndex)//项目
                    SendDlgItemMessage(HWND,2000年,LVM_INSERTITEM,0,reinter pret_cast&LT; LPARAM&GT;(安培; LVI));
                //其他分项
                    SendDlgItemMessage(HWND,2000年,LVM_SETITEM,0,reinter pret_cast&LT; LPARAM&GT;(安培; LVI));
            }
        }
    }
        返回0L;
    案例WM_NOTIFY:
    {
        如果(((LPNMHDR)的lParam) - GT; code == NM_DBLCLK)
        {
            开关(((LPNMHDR)的lParam) - GT; idFrom)
            {
            案例2000://记住,这是我们的ListView控件的ID
            {
                LPNMITEMACTIVATE lpnmia =(LPNMITEMACTIVATE)lParam的;                // SHIFT / ALT / CTRL /及其组合,不能是pressed
                如果((lpnmia-&GT; uKeyFlags || 0)== 0)
                {
                    //店项目/子项目矩形
                    RECT RC = {0,0,0,0};
                    //帮手值,需要处理部分可见的物品
                    INT topIndex = ListView_GetTopIndex(lpnmia-&GT; hdr.hwndFrom);
                    INT visibleCount = ListView_GetCountPerPage(lpnmia-&GT; hdr.hwndFrom);
                    //如果产品垂直部分可见,使其完全可见
                    如果((topIndex + visibleCount)== lpnmia-&GT;的iItem)
                    {
                        //获取上述项目的矩形 - &GT; lpnmia-&GT;的iItem - 1
                        ListView_GetSubItemRect(lpnmia-&GT; hdr.hwndFrom,lpnmia-&GT;的iItem - 1,lpnmia-&GT; iSubItem,LVIR_LABEL,和放大器; RC);
                        //保证点击的项目可见
                        ListView_EnsureVisible(lpnmia-&GT; hdr.hwndFrom,lpnmia-&GT;的iItem,FALSE);
                    }
                    否则//产品完全可见,只得到了ectangle
                        ListView_GetSubItemRect(lpnmia-&GT; hdr.hwndFrom,lpnmia-&GT;的iItem,lpnmia-&GT; iSubItem,LVIR_LABEL,和放大器; RC);                    RECT RC客户机= {0}; //列表视图客户端矩形,如果项目部分可见的需要
                    GetClientRect(lpnmia-&GT; hdr.hwndFrom,和放大器; RC客户机);
                    //产品水平部分可见 - &GT;从右侧
                    如果(rcClient.right&LT; rc.right)
                    {
                        //显示整个项目
                        ListView_Scroll(lpnmia-&GT; hdr.hwndFrom,rc.right - rcClient.right,0);
                        //调整矩形,以便正确显示编辑控件
                        rc.left - = rc.right - rcClient.right;
                        rc.right = rcClient.right;
                    }
                    //产品水平部分可见 - &GT;从左侧
                    如果(rcClient.left&GT; rc.left)
                    {
                        //显示整个项目
                        ListView_Scroll(lpnmia-&GT; hdr.hwndFrom,rc.left - rcClient.left,0);
                        //调整矩形,以便正确显示编辑控件
                        rc.right + = rcClient.left - rc.left;
                        rc.left = rcClient.left;
                    }
                    //现在是时候定位编辑控件,我们开始通过获取其窗口句柄
                    HWND hwndEdit =函数GetDlgItem(HWND,5000);
                    //获取项目的文本,并将其设置为编辑控件的文本
                    wchar_t的文[51];
                    ListView_GetItemText(lpnmia-&GT; hdr.hwndFrom,lpnmia-&GT;的iItem,lpnmia-&GT; iSubItem,文本,50);
                    Edit_SetText(hwndEdit,文字);
                    //选择全部文本
                    Edit_SetSel(hwndEdit,0,-1);
                    //地图列表视图客户矩形父矩形
                    //所以编辑控件可以妥善安置项目上方
                    MapWindowPoints(lpnmia-&GT; hdr.hwndFrom,HWND,(LPPOINT)及RC(的sizeof(RECT)/的sizeof(POINT)));
                    //移动编辑控件
                    SetWindowPos(hwndEdit,HWND_TOP,rc.left,rc.top,rc.right - rc.left,
                        rc.bottom - rc.top,SWP_SHOWWINDOW);
                    //将焦点设置到我们的编辑控件
                    HWND previousWnd = SetFocus的(hwndEdit);
                }
            }
                打破;
            默认:
                打破;
            }
        }
    }
        打破;
    情况下WM_CLOSE:
        ::的DestroyWindow(HWND);
        返回0L;
    案例WM_DESTROY:
    {
        :: PostQuitMessage(0);
    }
        返回0L;
    默认:
        返回:: DefWindowProc函数(HWND,味精,的wParam,lParam的);
    }
    返回0;
}// WinMain函数
INT WINAPI的WinMain(HINSTANCE的hInstance,HINSTANCE ^ h prevInstance,LPSTR lpCmdLine,
    INT的nCmdShow)
{
    //店的hInstance为以后使用全局变量
    HINST =实例句柄;    WNDCLASSEX厕所;
    HWND HWND;
    MSG消息;    //注册主窗口类
    wc.cbSize = sizeof的(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc的;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = HINST;
    wc.hIcon = LoadIcon(实例句柄,IDI_APPLICATION);
    wc.hCursor =使用LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = LMain_Window
    wc.hIconSm = LoadIcon(实例句柄,IDI_APPLICATION);    如果(RegisterClassEx(安培;!WC))
    {
        的MessageBox(NULL,L窗口注册失败!L错误!
            MB_ICONEXCLAMATION | MB_OK);        返回0;
    }    //初始化通用控件
    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof的(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
    INITCOMMONCONTROLSEX(安培; iccex);    //创建主窗口
    HWND =函数CreateWindowEx(0,LMain_WindowL网格控制
        WS_OVERLAPPEDWINDOW,50,50,400,400,NULL,NULL,的hInstance,0);    的ShowWindow(HWND,的nCmdShow);
    UpdateWindow(HWND);    而(的GetMessage(安培;消息,NULL,0,0)大于0)
    {
        的TranslateMessage(安培;消息);
        在DispatchMessage(安培;消息);
    }
    返回Msg.wParam;
}


解决方案

更​​新:

在第二个想法,我在前面贴的方法是错误的。我认为这是设计错误,在这样的编辑框使用 SetCapture ,它可以与一堆其他东西干扰。我要删除我的旧的答案,pretend没有人看见了!

您自己的方法是罚款与检查 KILLFOCUS ,你只需要继承的ListView控件来检查滚动消息来模仿 LVN_XXXLABELEDIT

 无效hideEdit(BOOL保存)
{
    //保存或不...
    的ShowWindow(hedit,SW_HIDE);
}LRESULT CALLBACK EditProc ...
{
    如果(MSG == WM_KILLFOCUS)
        hideEdit(1);    如果(MSG == WM_CHAR)
    {
        如果(wParam中== VK_ESCAPE){
            hideEdit(0);
            返回0;
        }
        如果(wParam中== VK_RETURN){
            hideEdit(1);
            返回0;
        }
    }    返回DefSubclassProc(...);
}LRESULT CALLBACK ListProc ...
{
    如果(MSG == WM_VSCROLL ||味精== WM_HSCROLL)hideEdit(1);
    返回DefSubclassProc(...);
}

INTRODUCTION:

I am trying to implement listview control with editable subitems. For in-place editing of items/subitems I use edit control.

I believe that I have managed to properly code placing of the edit control above item/subitem.

PROBLEM:

I do not know on which events I should end/cancel subitem editing ( hide edit control, set subitem text etc ) and how should I do it.

To clarify, I speak of the moment when user finishes/cancels in place editing.

At this point edit control is no longer needed, so I should hide it ( I do not like recreating it every time; I believe that creating it once and then showing/hiding it when needed is more efficient ).

I am targeting the behavior Properties window has in Visual Studio ( see attached image to see exactly the window I refer to ).

I want to achieve editing/canceling the same way this window does when user presses ESC key/clicks on another window/clicks on scrollbar etc.

MY EFFORTS TO SOLVE THIS:

Using Google, I found few examples but they are old and do not address all of the relevant cases, so that is why I ask here for help.

However, I was able to find out that one of the events I must consider are EN_KILLFOCUS, case when user presses ESC/ENTER key and the case when user clicks on something other than edit control.

EDIT:

I have managed to handle ESC and ENTER keys, as well as the case when user clicks on the another sibling control or switches windows with ALT + TAB. I have updated SSCCE with relevant changes

QUESTION:

In order to achieve default behavior for a grid ( if there is one for Windows apps ), which messages/events must I handle?

Can you also point out where should I edit subitem and hide edit control, and where should I just hide edit control?

EDIT:

My only problem remained is to handle the case when user clicks on the listview scrollbars, or on the background of the main window. I just do not know how to handle this and would appreciate all the help I can get.

RELEVANT INFORMATION:

I use Visual Studio 2013, on Windows 7 x86;

I am developing in C++ using raw WinAPI;

SSCCE:

Below is the solution I have so far. I have tried to thoroughly comment it, but if more info is required leave a comment and I will update my post.

#include <windows.h>
#include <windowsx.h>   // various listview macros etc
#include <CommCtrl.h>
#include <stdio.h>      // swprintf_s()

// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")

// link with Common Controls library
#pragma comment( lib, "comctl32.lib") 

//global variables
HINSTANCE hInst;

// listview subclass procedure
LRESULT CALLBACK ListViewSubclassProc(HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam,
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (message)
    {
    case WM_VSCROLL:
    case WM_HSCROLL:
        // if edit control has the focus take it away and give to listview
        if (GetFocus() == GetDlgItem(GetParent(hwnd), 5000))
            SetFocus(hwnd);  // use WM_NEXTDLGCTL for dialogbox !!!!
        break;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass(hwnd, ListViewSubclassProc, uIdSubclass);
        return DefSubclassProc(hwnd, message, wParam, lParam);
    }
    return ::DefSubclassProc(hwnd, message, wParam, lParam);
}

// subclass procedure for edit control
LRESULT CALLBACK InPlaceEditControl_SubclassProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam,
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (message)
    {
    case WM_GETDLGCODE:
        return (DLGC_WANTALLKEYS | DefSubclassProc(hwnd, message, wParam, lParam));
    case WM_KILLFOCUS:
        ShowWindow(hwnd, SW_HIDE);
        return DefSubclassProc(hwnd, message, wParam, lParam);
    case WM_CHAR:
        //Process this message to avoid message beeps.
        switch (wParam)
        {
        case VK_RETURN:
            return 0L;
        case VK_ESCAPE:
            return 0L;
        default:
            return ::DefSubclassProc(hwnd, message, wParam, lParam);
        }
        break;
    case WM_KEYDOWN:
        switch (wParam)
        {
        case VK_RETURN:
        {
            // get listview handle
            HWND hwndLV = GetDlgItem(GetParent(hwnd), 2000);
            // get edit control's client rectangle
            RECT rc = { 0 };
            GetClientRect(hwnd, &rc);
            // since edit control lies inside item rectangle
            // we can test any coordinate inside edit control's
            // client rectangle
            // I chose ( rc.left, rc.top )
            MapWindowPoints(hwnd, hwndLV, (LPPOINT)&rc, (sizeof(RECT) / sizeof(POINT)));
            // get item and subitem indexes
            LVHITTESTINFO lvhti = { 0 };
            lvhti.pt.x = rc.left;
            lvhti.pt.y = rc.top;
            ListView_SubItemHitTest(hwndLV, &lvhti);
            // get edit control's text
            wchar_t txt[50] = L"";
            Edit_GetText(hwnd, txt, 50);
            // edit cell text
            ListView_SetItemText(hwndLV, lvhti.iItem, lvhti.iSubItem, txt);
            // restore focus to listview
            // this triggers EN_KILLFOCUS
            // which will hide edit control
            SetFocus(hwndLV);
        }
            return 0L;
        case VK_ESCAPE:
            SetFocus(GetDlgItem(GetParent(hwnd), 2000));
            return 0L;
        default:
            return ::DefSubclassProc(hwnd, message, wParam, lParam);
        }
        break;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass(hwnd, InPlaceEditControl_SubclassProc, uIdSubclass);
        return DefSubclassProc(hwnd, message, wParam, lParam);

    }
    return ::DefSubclassProc(hwnd, message, wParam, lParam);
}
// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
    {
        //================ create controls
        RECT rec = { 0 };
        GetClientRect(hwnd, &rec);

        HWND hwndLV = CreateWindowEx(0, WC_LISTVIEW,
            L"", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS | LVS_REPORT, 
            50, 50, 250, 200, hwnd, (HMENU)2000, hInst, 0);
        // in place edit control
        HWND hwndEdit = CreateWindowEx(0, WC_EDIT, L"", ES_AUTOHSCROLL | WS_CHILD | WS_BORDER,
            200, 265, 100, 25, hwnd, (HMENU)5000, hInst, 0);
        // edit control must have the same font as listview
        HFONT hf = (HFONT)SendMessage(hwndLV, WM_GETFONT, 0, 0);
        if (hf)
            SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, (LPARAM)TRUE);
        // subclass edit control, so we can edit subitem with ENTER, or
        // cancel editing with ESC
        SetWindowSubclass(hwndEdit, InPlaceEditControl_SubclassProc, 0, 0);
        // set extended listview styles
        ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
        // subclass listview
        SetWindowSubclass(hwndLV, ListViewSubclassProc, 0, 0);

        // add some columns
        LVCOLUMN lvc = { 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);

            lvc.iSubItem = nIndex;
            lvc.cx = 60;
            lvc.pszText = txt;

            ListView_InsertColumn(hwndLV, nIndex, &lvc);
        }

        // add some items
        LVITEM lvi;

        lvi.mask = LVIF_TEXT;

        for (lvi.iItem = 0; lvi.iItem < 10000; lvi.iItem++)
        {
            for (long nIndex = 0; nIndex < 5; nIndex++)
            {
                wchar_t txt[50];
                swprintf_s(txt, 50, L"Item %d%d", lvi.iItem, nIndex);

                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;
    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)
                {
                    // store item/subitem rectangle
                    RECT rc = { 0, 0, 0, 0 };
                    // helper values, needed for handling partially visible items
                    int topIndex = ListView_GetTopIndex(lpnmia->hdr.hwndFrom);
                    int visibleCount = ListView_GetCountPerPage(lpnmia->hdr.hwndFrom);
                    // if item is vertically partially visible, make it fully visible
                    if ((topIndex + visibleCount) == lpnmia->iItem)
                    {
                        // get the rectangle of the above item -> lpnmia->iItem - 1
                        ListView_GetSubItemRect(lpnmia->hdr.hwndFrom, lpnmia->iItem - 1, lpnmia->iSubItem, LVIR_LABEL, &rc);
                        // ensure clicked item is visible
                        ListView_EnsureVisible(lpnmia->hdr.hwndFrom, lpnmia->iItem, FALSE);
                    }
                    else // item is fully visible, just get its ectangle
                        ListView_GetSubItemRect(lpnmia->hdr.hwndFrom, lpnmia->iItem, lpnmia->iSubItem, LVIR_LABEL, &rc);

                    RECT rcClient = { 0 };  // listview client rectangle, needed if item partially visible
                    GetClientRect(lpnmia->hdr.hwndFrom, &rcClient);
                    // item is horizontally partially visible -> from the right side
                    if (rcClient.right < rc.right)  
                    {
                        // show the whole item
                        ListView_Scroll(lpnmia->hdr.hwndFrom, rc.right - rcClient.right, 0);
                        // adjust rectangle so edit control is properly displayed
                        rc.left -= rc.right - rcClient.right;
                        rc.right = rcClient.right;
                    }
                    // item is horizontally partially visible -> from the left side
                    if (rcClient.left > rc.left)  
                    {
                        // show the whole item
                        ListView_Scroll(lpnmia->hdr.hwndFrom, rc.left - rcClient.left, 0);
                        // adjust rectangle so edit control is properly displayed
                        rc.right += rcClient.left - rc.left;
                        rc.left = rcClient.left;
                    }
                    // it is time to position edit control, we start by getting its window handle
                    HWND hwndEdit = GetDlgItem(hwnd, 5000);
                    //  get item text and set it as edit control's text
                    wchar_t text[51];
                    ListView_GetItemText(lpnmia->hdr.hwndFrom, lpnmia->iItem, lpnmia->iSubItem, text, 50);
                    Edit_SetText(hwndEdit, text);
                    // select entire text
                    Edit_SetSel(hwndEdit, 0, -1);
                    // map listview client rectangle to parent rectangle
                    // so edit control can be properly placed above the item
                    MapWindowPoints(lpnmia->hdr.hwndFrom, hwnd, (LPPOINT)&rc, (sizeof(RECT) / sizeof(POINT)));
                    // move the edit control
                    SetWindowPos(hwndEdit, HWND_TOP, rc.left, rc.top, rc.right - rc.left, 
                        rc.bottom - rc.top, SWP_SHOWWINDOW);
                    // set focus to our edit control
                    HWND previousWnd = SetFocus(hwndEdit);
                }
            }
                break;
            default:
                break;
            }
        }
    }
        break;
    case WM_CLOSE:
        ::DestroyWindow(hwnd);
        return 0L;
    case WM_DESTROY:
    {
        ::PostQuitMessage(0);
    }
        return 0L;
    default:
        return ::DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
    int nCmdShow)
{
    // store hInstance in global variable for later use
    hInst = hInstance;

    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    // register main window class
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"Main_Window";
    wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!", 
            MB_ICONEXCLAMATION | MB_OK);

        return 0;
    }

    // initialize common controls
    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
    InitCommonControlsEx(&iccex);

    // create main window
    hwnd = CreateWindowEx(0, L"Main_Window", L"Grid control",
        WS_OVERLAPPEDWINDOW, 50, 50, 400, 400, NULL, NULL, hInstance, 0);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

解决方案

Update:

On second thought, the method I posted earlier was wrong. I think it's design error to use SetCapture in edit-box like that, it can interfere with bunch of other things. I am going to delete my old answer and pretend nobody saw it!

Your own method is fine with checking for KILLFOCUS, you just need subclass for ListView to check scroll messages to mimic LVN_XXXLABELEDIT

void hideEdit(BOOL save)
{
    //save or not...
    ShowWindow(hedit, SW_HIDE);
}

LRESULT CALLBACK EditProc...
{
    if (msg == WM_KILLFOCUS)
        hideEdit(1);

    if (msg == WM_CHAR)
    {
        if (wParam == VK_ESCAPE){
            hideEdit(0);
            return 0;
        }
        if (wParam == VK_RETURN){
            hideEdit(1);
            return 0;
        }
    }

    return DefSubclassProc(...);
}

LRESULT CALLBACK ListProc...
{
    if (msg == WM_VSCROLL || msg == WM_HSCROLL) hideEdit(1);
    return DefSubclassProc(...);
}

这篇关于妥善处理与编辑子项目的ListView子项编辑(或子项编辑抵消)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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