Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Продвинутый LabelEdit в ListView 
:(
    Опции темы
0x07L
Дата 10.6.2006, 22:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Для одного проекта надо сделать разновидность ListView с возможностью редактирования текста во всех ячейках, а не только ячейках первого столбца. Я пробовал создавать первоначально скрытый Edit и по двойному клику мышкой на ячейке ListView показывать его, помещать над этой ячейкой и ставить на него фокус. При прокрутке я двигаю Edit, чтобы он всегда находился над нужной ячейкой. Все вроде работает, только вот Edit иногда налезает на стандартный Header ListView и сидит там, пока Header не перерисуется. Пробовал делать Header всегда "topmost" функцией SetWindowPos и много чего еще пробовал с этой функцией, но ничего не получается. Что делать? 
PM MAIL   Вверх
Earnest
Дата 12.6.2006, 11:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



В таких случаях обычно Edit не держат постоянно, а создают только для редактирования (по клику, нажатию клавиш, etc). Это тем более разумно, что часто редактор для каждого столбца нужен свой. Проблема с "налезанием" на заголовок, возможно, связана с тем, что заголовок находится в клиентской области List View. 
А еще, таблицы с редактированием всех ячеек нет смысла делать на основе List View: чаще всего их делают с нуля. Это только кажется, что готовая функциональность List View помогает, на самом деле она больше мешает...
Посмотри примеры на codeproject, там разных списков\таблиц - выше крыши. 


--------------------
...
PM   Вверх
0x07L
Дата 12.6.2006, 11:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Я так понял, что контролы там делаются с помощью MFC. А на чистом WinAPI что-нибудь есть (до MFC не дорос)?

PS Да, Header находится в клиентской области ListView. Но я пробовал делать ролителем моего Edit'а и ListView. Все равно ничего не выходит. 
PM MAIL   Вверх
Earnest
Дата 12.6.2006, 12:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Да в общем, не большая проблема переделать MFC-код на АПИ, по крайней мере то, что касается контролов. Или по крайней мере, посмотреть как сделано, принципы.
Естественно, родителем едита должен быть ListView, кто же еще. Наверно, ты неаккуратно обрабатываешь скролл-сообщения, но в твоем посте недостаточно информации. Возможно,  едит "сползает" со своей ячейки, а возможно, ты ошибочно ставишь едит на ряд, который меньше чем верхний видимый. 
Скажем, ситуация следующая: верхний видимый ряд N 10, в нем текущей является ячейка, скажем, 2я: имеет видимый едит с фокусом ввода и т.д. Пользователь каким-то образом дает команду ScrollDown (например, нажимает соотв. кнопку на скролл-баре). Что произойдет? Теперь верхний видимый ряд 11. А едит по прежнему связан с 10м? Его кто-нибудь переставлял? Я не говорю, что у тебя именно так: просто нужно знать логику, заложенную в управление редактором, вариант-то не один.
 


--------------------
...
PM   Вверх
threef
Дата 14.6.2006, 12:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 375
Регистрация: 27.10.2005
Где: Запорожье

Репутация: нет
Всего: 10




Как вариант - после утери фокуса редактором прятать его(SW_HIDE). Тогда никуда он не залезет. Отловить правильную позицию после скроллинга поможет позиция первой ячейки сразу после первого отображения listview - она хранит "отступы " - ширину рамки слева и высоту header+ рамка вверху.
Конечно,как сказала Earnest , listview использовать трудно но можно - сделай один раз свой собственный класс и используй дальше в проектах. У меня есть такой, правда производный от MFC-шного CListCtrl 
PM MAIL   Вверх
0x07L
Дата 14.6.2006, 22:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



2_Earnest:

Перемещением Edit'а занимаюсь я, обрабатывая уведомление LVN_ENDSCROLL.

Нет, Edit с ячейки не сползал и всегда находился там, где мне надо, за исключением, конечно, случаев, когда он заползал на заголовок.

Да, я ставил Edit на ячейку, которая при прокрутке уезжала вверх под заголовок. В том и было дело, что я хотел, не производя лишних действий с Edit'ом, заставить его аналогично уезжать под заголовок, а не наползать на последний.

Выход из этой ситуации я нашел очень простой (не знаю, чего я упорствовал с z-order'ом). При прокрутке я получаю номер текущей верхней строки. Если он больше, чем номер строки с Edit'ом, то переставляем Edit куда-нибудь в невидимую область (в своей тестовой программе я использовал следующее: SetWindowPos (hEdit, 0, -100, -100, 0, 0, SWP_NOZORDER). SW_HIDE я не применяю, т.к. в этом случае Edit потерял бы фокус, а заканчиваю редактирование ячейки я как раз при получении сообщения EN_KILLFOCUS от Edit'а (самый легкий вариант).

PS А с контролами на Code Project я обязательно разберусь...

2_threef:

SW_HIDE мне не подходит, как сказано выше.

Теперь к тебе вопрос: разве у ListView есть рамка?

// -----------------------------------------------------------------

На всякий случай, выкладываю код своей тестовой программы. Пожалуйста, сообщите, если найдете в нем ошибки.

Код

#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
#include "resource.h"

#pragma comment (lib, "ComCtl32.Lib")

int APIENTRY _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR strCmdLine, int CmdShow);
LRESULT CALLBACK DlgProc (HWND hDlg, UINT MessageCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ListViewSubclassWndProc (HWND hListView, UINT MessageCode, WPARAM wParam, LPARAM lParam);

HWND hEdit;
WNDPROC OriginalListViewWndProc;
POINT LabelEditSlot;

int APIENTRY _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR strCmdLine, int CmdShow)
{
    INITCOMMONCONTROLSEX InitComCtl;
    InitComCtl.dwSize = sizeof InitComCtl;
    InitComCtl.dwICC = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx (&InitComCtl);
    
    return static_cast<int>(DialogBoxW (
        hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0,
        reinterpret_cast<DLGPROC>(DlgProc)));
}

LRESULT CALLBACK DlgProc (HWND hDlg, UINT MessageCode, WPARAM wParam, LPARAM lParam)
{
    const int LinesInListView = 15;
    const int ColumnsInListView = 15;
    const int ColumnWidth = 50;
    
    static HWND hListView;
        
    switch (MessageCode)
    {
    case WM_INITDIALOG:
        {
            
        ::LabelEditSlot.x = -1;
            
        RECT rcClient;
        GetClientRect (hDlg, &rcClient);

        hListView = CreateWindowW (
            WC_LISTVIEW, 0,
            WS_CHILD | WS_VISIBLE | LVS_REPORT,
            0, 0, rcClient.right, rcClient.bottom,
            hDlg, 0, 0, 0);

        ListView_SetExtendedListViewStyle (hListView, LVS_EX_GRIDLINES);

        LVITEM Item;
        Item.iItem = LinesInListView;
        Item.iSubItem = 0;
        Item.mask = 0;
        
        for (int i=0; i<LinesInListView; i++)
        {
            ListView_InsertItem (hListView, &Item);
        }

        LVCOLUMN Column;
        Column.mask = LVCF_WIDTH;
        Column.cx = ColumnWidth;

        for (int i=0; i<ColumnsInListView; i++)
        {
            ListView_InsertColumn (hListView, ColumnsInListView, &Column);
        }

#pragma warning (push)
#pragma warning (disable: 4244)
#pragma warning (disable: 4312)
        ::OriginalListViewWndProc = 
            reinterpret_cast<WNDPROC>(SetWindowLongPtr (
            hListView, GWLP_WNDPROC,
            reinterpret_cast<LONG_PTR>(ListViewSubclassWndProc)));
#pragma warning (pop)

        ::hEdit = CreateWindow (
            _TEXT("EDIT"), 0, WS_CHILD | WS_BORDER,
            CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
            hListView, 0, 0, 0);
        
        HFONT hListViewFont = reinterpret_cast<HFONT>(SendMessage (hListView, WM_GETFONT, 0, 0));
        SendMessage (::hEdit, WM_SETFONT, reinterpret_cast<WPARAM>(hListViewFont), false);

        return true;

        } // case WM_INITDIALOG

    case WM_NOTIFY:
        {

        LPNMHDR pHeader = reinterpret_cast<LPNMHDR>(lParam);

        if (pHeader->hwndFrom == hListView)
        {
            switch (pHeader->code)
            {
            case NM_CLICK:
                {
                
                LPNMITEMACTIVATE pItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(lParam);

                POINT ptMy = pItemActivate->ptAction;

                SCROLLINFO ScrollInfo;
                    ScrollInfo.cbSize = sizeof ScrollInfo;
                    ScrollInfo.fMask = SIF_POS;
                    GetScrollInfo (hListView, SB_HORZ, &ScrollInfo);
                    ptMy.x += ScrollInfo.nPos;
                    GetScrollInfo (hListView, SB_VERT, &ScrollInfo);
                    ptMy.y += ScrollInfo.nPos;

                LVHITTESTINFO HitTestInfo;
                HitTestInfo.pt = ptMy;
                
                ListView_SubItemHitTest (hListView, &HitTestInfo);

                if (HitTestInfo.flags & LVHT_ONITEMLABEL)
                {
                    RECT rcSlot;
                    ListView_GetSubItemRect (
                        hListView,
                        HitTestInfo.iItem,
                        HitTestInfo.iSubItem,
                        LVIR_BOUNDS,
                        &rcSlot);

                    SetWindowPos (
                        ::hEdit, 0,
                        rcSlot.left,
                        rcSlot.top,
                        rcSlot.right-rcSlot.left,
                        rcSlot.bottom-rcSlot.top-1,
                        SWP_NOZORDER | SWP_SHOWWINDOW);

                    SetFocus (::hEdit);

                    ::LabelEditSlot.x = HitTestInfo.iSubItem;
                    ::LabelEditSlot.y = HitTestInfo.iItem;
                }

                return true;

                } // case NM_CLICK

            case LVN_ENDSCROLL:
                // только для ComCtl32.dll v6 и Windows XP

                if (::LabelEditSlot.x != -1)
                {
                    if (::LabelEditSlot.y < ListView_GetTopIndex (hListView))
                    {
                        SetWindowPos (::hEdit, 0, -100, -100, 0, 0, SWP_NOZORDER);
                    }
                    else
                    {
                        RECT rcSlot;

                        ListView_GetSubItemRect (
                            hListView,
                            ::LabelEditSlot.y,
                            ::LabelEditSlot.x,
                            LVIR_BOUNDS,
                            &rcSlot);

                        SetWindowPos (
                            ::hEdit, 0,
                            rcSlot.left,
                            rcSlot.top,
                            rcSlot.right-rcSlot.left,
                            rcSlot.bottom-rcSlot.top-1,
                            SWP_NOZORDER);
                        
                        InvalidateRect (::hEdit, 0, true);
                    }

                    return true;
                }
                return false;

            } // switch (...code)

        } // if (... == hListView)

        return false;

        } // case WM_NOTIFY

    case WM_SIZE:

        MoveWindow (hListView, 0, 0, LOWORD(lParam), HIWORD(lParam), true);
        return true;

    case WM_CLOSE:

        DestroyWindow (hDlg);
        return true;

    case WM_DESTROY:

        EndDialog (hDlg, 0);
        return true;

    } // switch (MessageCode)

    return false;
}

LRESULT CALLBACK ListViewSubclassWndProc (HWND hListView, UINT MessageCode, WPARAM wParam, LPARAM lParam)
{
    switch (MessageCode)
    {
    case WM_COMMAND:

        if (reinterpret_cast<HWND>(lParam) == ::hEdit
            && HIWORD(wParam) == EN_KILLFOCUS
            && ::LabelEditSlot.x != -1
            && GetFocus() == hListView)
        {
            ShowWindow (::hEdit, SW_HIDE);
            ::LabelEditSlot.x = -1;
        }
    }
    return OriginalListViewWndProc (hListView, MessageCode, wParam, lParam);
}
  

Это сообщение отредактировал(а) 0x07L - 14.6.2006, 22:20
PM MAIL   Вверх
Earnest
Дата 15.6.2006, 07:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Цитата(0x07L @  14.6.2006,  23:19 Найти цитируемый пост)
Теперь к тебе вопрос: разве у ListView есть рамка?
 
При желании рамка есть у всех. Видимо, threef имел ввиду бордюр, ширина которого зависит от стиля: WS_BORDER, WS_EX_CLIENTEDGE, etc 

Цитата(0x07L @  14.6.2006,  23:19 Найти цитируемый пост)
не знаю, чего я упорствовал с z-order'ом

Наконец я поняла, чего и как ты хотел сделать. Чтобы эдит "задвигался" по заголовок. Дело в том, что эдит ты создаешь после того как хедер уже создан. И оно, разумеется,сверху. Чтобы задвинуть его вниз попробуй 
SetWindowPos (hWndEdit,HWND_BOTTOM,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE).
Не уверена, что оно "влезет" под заголовок. 

 


--------------------
...
PM   Вверх
0x07L
Дата 15.6.2006, 10:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Да, именно так. О том, что HWND_BOTTOM ничего не дает, я писал в самом первом посте. Также ничего не дает HWND_TOP для Header'а и помещение Header'а перед Edit'ом. В том и дело.

Да, про такую рамку я чего-то не подумал. Наверное, из-за того, что никогда не приходилось видеть ListView с рамкой.
Спасибо threef.

PS Видимо, я не слишком подробно описал проблему в первом посте. Извините. 

Это сообщение отредактировал(а) 0x07L - 15.6.2006, 10:06
PM MAIL   Вверх
Earnest
Дата 15.6.2006, 13:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Цитата(0x07L @  15.6.2006,  11:05 Найти цитируемый пост)
 О том, что HWND_BOTTOM ничего не дает, я писал в самом первом посте.

Не заметила, ну да ладно. Все равно это странно. Спаем не смотрел? Действительно едит наверху остается?
Хотя ... кажется где-то попадалась такая ситуация: вверху эдит или внизу, когда в нем фокус - текст спокойно рисуется поверх всего. Выглядит жутко. Если так, то действительно нужно извращаться.
Тем не менее, лучше бы обработку ввода делать не (только) на KillFocus, а писать явную функцию, вызываемую на ряд событий. А Edit прятать (или вообще уничтожать).  Скажем, пользователь нажимает ENTER: видимо, ожидает, что текущий текст будет запомнен в таблице, а фокус перейдет на ячейку вниз. Нажатие стрелок вних-вверх-вправо-влево - аналогично. Нажатие ESCAPE - замена введенного текста старым значением и отказ от редактирования.
Кроме того, если эдит заново создавать для каждой ячейки, можно менять его тип в зависимости от типа ячейки (где-то это может быть выпадающий список и т.д.).
В общем, сколько я не видела реализаций таблиц - никогда редактор по ячейкам не ездил, а всегда создавался-уничтожался. Наверное, не зря. 


--------------------
...
PM   Вверх
0x07L
Дата 15.6.2006, 16:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Цитата(Earnest @  15.6.2006,  13:42 Найти цитируемый пост)
Не заметила, ну да ладно

Просто это было скрыто за формулировкой
Цитата(0x07L @  10.6.2006,  22:51 Найти цитируемый пост)
 и много чего еще пробовал с этой функцией

Цитата(Earnest @  15.6.2006,  13:42 Найти цитируемый пост)
лучше бы обработку ввода делать не (только) на KillFocus, а писать явную функцию, вызываемую на ряд событий

Это просто тестовая программа, а не реальное приложение. В последнем случае, полностью согласен, обработки EN_KILLFOCUS совершенно недостаточно.

Цитата(Earnest @  15.6.2006,  13:42 Найти цитируемый пост)
Спаем не смотрел? Действительно едит наверху остается?

Честно говоря, я не знаю, как Spy'ем определять z-order. Могу только сказать, что в списке окон Edit стоит выше Header'а.
А если посмотреть свойства окна ListView, то его "First Child Window" - это Edit. 
PM MAIL   Вверх
Earnest
Дата 15.6.2006, 18:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Цитата(0x07L @  15.6.2006,  17:42 Найти цитируемый пост)
Честно говоря, я не знаю, как Spy'ем определять z-order. Могу только сказать, что в списке окон Edit стоит выше Header'а.

Так и смотреть. Я, честно говоря, тоже всегда забываю - первое окно - это первое по z-порядку или последнее. Всегда проверяю: вариантов-то всего 2. насчет "посмотреть Спаем" я имела ввиду - меняется что-нибудь от применения SetWindowPos или нет.
Но раз не помогает, то и ладно. 


--------------------
...
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1080 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.