在列表视图中正确处理子项目编辑(或取消子项目编辑)可编辑的子项目 [英] Properly handle subitem editing ( or canceling of subitem editing ) in listview with editable subitems

查看:385
本文介绍了在列表视图中正确处理子项目编辑(或取消子项目编辑)可编辑的子项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介:



我正在尝试使用可编辑子项实现列表视图控件。对于项目/子项目的就地编辑,我使用编辑控件。



我相信我已经设法正确地将编辑控件放置在项目/子项目的上方。 p>

问题:



我不知道我应该结束/取消子项编辑的事件(隐藏编辑控件,设置subitem文本等),我该怎么办



澄清一下,当用户完成/取消在场编辑的时候。


$ b $在这一点上,编辑控件不再需要,所以我应该隐藏它(我不喜欢每次重新创建它;我相信创建它一次,然后在需要时显示/隐藏它更有效率) 。



我的目标是在Visual Studio中具有属性窗口的功能(请参阅附件,以查看我所参考的窗口)。





我想实现编辑/取消与用户按下ESC键/点击另一个窗口/点击滚动条等相同的方式。



我的努力解决这个问题:



使用Google,我发现很少的例子,但是它们都是旧的,并没有解决所有的相关案例,所以这就是为什么我在这里请求帮助。



但是,我能够发现我必须考虑的一个事件是 EN_KILLFOCUS ,用户按 ESC / ENTER 键的情况,用户点击不同于编辑控件的情况。



编辑



我已经设法处理ESC和ENTER键,以及用户点击另一个兄弟续约的情况滚动或使用ALT + TAB切换窗口。我已经更新了SSCCE相关的更改



QUESTION:



为了实现网格的默认行为(if还有一个用于Windows应用程序),哪些消息/事件必须处理?



还可以指出我应该在哪里编辑子项并隐藏编辑控件,我应该在哪里隐藏编辑控件?



编辑:



我唯一的问题是处理用户点击列表浏览滚动条或主窗口背景时的情况。我只是不知道如何处理这个,并希望所有的帮助我可以得到。



相关信息:



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



我正在使用原始WinAPI开发C ++;



< h2> SSCCE:

以下是迄今为止的解决方案。我试图彻底评论它,但如果需要更多的信息留下评论,我会更新我的帖子。

  #include < windows.h> 
#include< windowsx.h> //各种listview宏等
#include&CommCtrl.h>
#include< stdio.h> // swprintf_s()

//启用Visual样式
#pragma注释(链接器,/ manifestdependency:\type ='win32'\
name ='Microsoft .Windows.Common-Controls'version ='6.0.0.0'\
processorArchitecture ='*'publicKeyToken ='6595b64144ccf1df'\
language ='*'\)

//与Common Controls库链接
#pragma comment(lib,comctl32.lib)

//全局变量
HINSTANCE hInst;

// listview子类过程
LRESULT CALLBACK ListViewSubclassProc(HWND hwnd,UINT消息,
WPARAM wParam,LPARAM lParam,
UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
{
开关(消息)
{
case WM_VSCROLL:
case WM_HSCROLL:
//如果编辑控件有焦点取走,并给予listview
if(GetFocus()== GetDlgItem(GetParent(hwnd),5000))
SetFocus(hwnd); //使用WM_NEXTDLGCTL进行对话框!!!!
break;
case WM_NCDESTROY:
:: RemoveWindowSubclass(hwnd,ListViewSubclassProc,uIdSubclass);
返回DefSubclassProc(hwnd,message,wParam,lParam);
}
return :: DefSubclassProc(hwnd,message,wParam,lParam);
}

//编辑控件的子类过程
LRESULT CALLBACK InPlaceEditControl_SubclassProc(HWND hwnd,UINT消息,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);
返回DefSubclassProc(hwnd,message,wParam,lParam);
case WM_CHAR:
//处理此消息以避免消息嘟嘟声。
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:
{
//获取列表视图句柄
HWND hwndLV = GetDlgItem GetParent(hwnd),2000)
//获取编辑控件的客户端矩形
RECT rc = {0};
GetClientRect(hwnd,& rc);
//因为编辑控件位于项目矩形内
//我们可以测试编辑控件的
//客户端矩形
//中的任何坐标//我选择了(rc.left,rc。顶部)
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);
}
return 0L;
case VK_ESCAPE:
SetFocus(GetDlgItem(GetParent(hwnd),2000));
返回0L;
default:
return :: DefSubclassProc(hwnd,message,wParam,lParam);
}
break;
case WM_NCDESTROY:
:: RemoveWindowSubclass(hwnd,InPlaceEditControl_SubclassProc,uIdSubclass);
返回DefSubclassProc(hwnd,message,wParam,lParam);

}
return :: DefSubclassProc(hwnd,message,wParam,lParam);
}
//主窗口程序
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
开关(msg)
{
case 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);
//编辑控件必须与listview具有相同的字体
HFONT hf =(HFONT)SendMessage(hwndLV,WM_GETFONT,0,0);
if(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;

for(long nIndex = 0; nIndex< 5; nIndex ++)
{
wchar_t txt [50];
swprintf_s(txt,50,LColumn%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 ++)


{
for(long nIndex = 0; nIndex< 5; nIndex ++)
{
wchar_t txt [50];
swprintf_s(txt,50,LItem%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 //子项
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://记住,这是我们的listview的ID
{
LPNMITEMACTIVATE lpnmia =(LPNMITEMACTIVATE)lParam;

// SHIFT / ALT / CTRL /他们的组合,不能被按
if((lpnmia-> uKeyFlags || 0)== 0)
{
//存储项/子项矩形
RECT rc = {0,0,0,0};
//帮助器值,用于处理部分可见项目
int topIndex = ListView_GetTopIndex(lpnmia-> hdr.hwndFrom);
int visibleCount = ListView_GetCountPerPage(lpnmia-> hdr.hwndFrom);
//如果项目是垂直部分可见的,使其完全可见
if((topIndex + visibleCount)== lpnmia-> iItem)
{
//获取矩形的上述项目 - > lpnmia-> iItem - 1
ListView_GetSubItemRect(lpnmia-> hdr.hwndFrom,lpnmia-> iItem - 1,lpnmia-> iSubItem,LVIR_LABEL,& rc);
//确保点击的项目可见
ListView_EnsureVisible(lpnmia-> hdr.hwndFrom,lpnmia-> iItem,FALSE);
}
else // item是完全可见的,只是得到它的ectangle
ListView_GetSubItemRect(lpnmia-> hdr.hwndFrom,lpnmia-> iItem,lpnmia-> iSubItem,LVIR_LABEL,& ; rc);

RECT rcClient = {0}; // listview客户端矩形,如果项目部分可见,需要
GetClientRect(lpnmia-> hdr.hwndFrom,& rcClient);
// item水平部分可见 - >从右边
if(rcClient.right< rc.right)
{
//显示整个项目
ListView_Scroll(lpnmia-> hdr.hwndFrom,rc。 right - rcClient.right,0);
//调整矩形,以便编辑控件正确显示
rc.left - = rc.right - rcClient.right;
rc.right = rcClient.right;
}
// item水平部分可见 - >从左侧
if(rcClient.left> rc.left)
{
//显示整个项目
ListView_Scroll(lpnmia-> hdr.hwndFrom,rc。 left-rcClient.left,0);
//调整矩形,以便编辑控件正确显示
rc.right + = rcClient.left - rc.left;
rc.left = rcClient.left;
}
//是时候定位编辑控件,我们从获取窗口句柄开始
HWND hwndEdit = GetDlgItem(hwnd,5000);
//获取项目文本并将其设置为编辑控件的文本
wchar_t text [51];
ListView_GetItemText(lpnmia-> hdr.hwndFrom,lpnmia-> iItem,lpnmia-> iSubItem,text,50);
Edit_SetText(hwndEdit,text);
//选择整个文本
Edit_SetSel(hwndEdit,0,-1);
// map listview客户矩形到父矩形
//所以编辑控件可以正确放置在项目上方
MapWindowPoints(lpnmia-> 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);
}
}
break;
默认值:
break;
}
}
}
break;
case WM_CLOSE:
:: DestroyWindow(hwnd);
返回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在全局变量中供以后使用
hInst = hInstance;

WNDCLASSEX wc;
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(hInstance,IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = LMain_Window;
wc.hIconSm = LoadIcon(hInstance,IDI_APPLICATION);

如果(!RegisterClassEx(& wc))
{
MessageBox(NULL,LWindow Registration Failed!,LError!,
MB_ICONEXCLAMATION | MB_OK);

return 0;
}

//初始化常用控件
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
InitCommonControlsEx(& iccex);

//创建主窗口
hwnd = CreateWindowEx(0,LMain_Window,LGrid 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;
}


解决方案

更新:

第二个想法是,我之前发布的方法是错误的。我认为在编辑框中使用 SetCapture 是设计错误,它可能会干扰其他一些事情。我要删除我的旧答案,假装没有人看到它!



您自己的方法很好,检查 KILLFOCUS ,您只需要ListView的子类来检查滚动消息来模拟 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;
}
}

返回DefSubclassProc(...);
}

LRESULT CALLBACK ListProc ...
{
if(msg == WM_VSCROLL || msg == 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(...);
}

这篇关于在列表视图中正确处理子项目编辑(或取消子项目编辑)可编辑的子项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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