Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.21.76.0] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Здраствуйте.
Люди, подскажите пожалуйста как скрыть колонку у ListView или SysListView32 так как это сделано в Explorer`е. Стиль vsReport. Допустим у ListView есть три колонки. ListView заполнен 50-ми записями. Как допустим спрятать 2-ю колонку, чтобы были видны только 1 и 3 колонка. Установить длину 2-й колонки =0 не предлагать, т.к это не подходит (пользователь может изменить ее размер). Заранее благодарен. С уважением DaDe. |
Сообщ.
#2
,
|
|
|
ты можешь отслеживать действия пользователя и не давать ему менять ширину колонки, другого метода как раз и не находится, потому как любые удаления и перемещения колонок потребуют перенос большого числа данных.
|
Сообщ.
#3
,
|
|
|
Никак. Только вручную - перестановкой...
|
Сообщ.
#4
,
|
|
|
exodus
Пробовал так сделать, но это не есть хорошо. Допустим, есть три колонки |Column1|Column2|Column3| Если я сделаю размер 2-ой колонки =0 и заблокирую ее перемещение, то получится следующая картина. |Column1|Column3| Но если я слева наведу на разделитель 1-ой и 3-ей колонки, то я смогу изменять размеры 1-ой колонки. Но если я справа наведу на разделитель 1-ой и 3-ей колонки, то я НЕ смогу изменять размеры 1-ой колонки, что не есть хорошо. И кстати, когда у второй колонки ширина =0, то палочка ( | ) соединяющая 1-ю и 3-ю колонку чуть меньше в размере (сверху). А в Explorer`е в такой ситуации ширина второй колонки =0, но палочка соединяющая 1-ю и 3-ю колонку нормального размера и колонки остаются на своих местах. Как тогда они это сделали? n0wheremany Перестановкой сделал, но это все так муторно и неудобно. В Explorer`е сделано как-то по-другому. Интересно как????? С уважением DaDe. |
Сообщ.
#5
,
|
|
|
Цитата DaDe @ В Explorer`е сделано как-то по-другому. Интересно как????? Там данные держатся в кэше, а столбцы просто удаляются. Если столбец надо показать, то вставляется пустой и заполняется данными из кэша. Вобщем, надо все держать в памяти и заполнять ручками. Ну а если данных много, то лучше делать, как exodus сказал. Цитата DaDe @ Но если я справа наведу на разделитель 1-ой и 3-ей колонки, то я НЕ смогу изменять размеры 1-ой колонки, что не есть хорошо. Значит что-то не так делаешь. Как блокируешь ресайзинг столбца? Единственное, от чего при этом не избавишься, так это от Цитата DaDe @ палочка ( | ) соединяющая 1-ю и 3-ю колонку чуть меньше в размере (сверху). По крайней мере, я не знаю, как это исправить. |
Сообщ.
#6
,
|
|
|
Цитата Значит что-то не так делаешь. Как блокируешь ресайзинг столбца? case Msg.NMHdr^.code of HDN_BEGINTRACKA: if Hide then Msg.Result:=-1; Или я что-то не так делаю? Подскажи. Цитата Там данные держатся в кэше, а столбцы просто удаляются. Если столбец надо показать, то вставляется пустой и зополняется данными из кэша. Вобщем, надо все держать в памяти и заполнять ручками. Если у Explorer`а в SysListView32 имеется 6000 строк, то вставка столбца и заполнение данными занимает доли секунды. Почему так быстро? С уважением DaDe. |
Сообщ.
#7
,
|
|
|
Цитата DaDe @ Или я что-то не так делаю? В Win2K и выше нужно ловить HDN_BEGINTRACKW (и, кстати еще и HDN_DIVIDERDBLCLICKW). А как ты WM_NOTIFY для ListView обрабатываешь? Я делаю через subclassing и все нормально работает. Цитата DaDe @ Почему так быстро? Там не создаются новые Item'ы (строки), а просто заполняются subitem'ы через ListView_SetItemText(...). Кроме того, прорисовка происходит уже после заполнения полей данными, одним махом. Да и сам кэш у оболочки оптимизирован хорошо. |
Сообщ.
#8
,
|
|
|
Цитата Krid @ А как ты WM_NOTIFY для ListView обрабатываешь? procedure TForm1.FormCreate(Sender: TObject); begin FListViewOldWndProc := ListView1.WindowProc; Listview1.WindowProc := ListViewNewWndProc; end; procedure TForm1.ListViewNewWndProc(var Msg: TMessage); var hdn: ^THDNotify; begin if Msg.Msg = WM_NOTIFY then begin hdn := Pointer(Msg.lParam); if hdn.hdr.code = HDN_BeginTrackW then begin if hdn.Item=1 then Msg.Result := 1 end else FListViewOldWndProc(Msg); end else FListViewOldWndProc(Msg); end; procedure TForm1.FormDestroy(Sender: TObject); begin ListView1.WindowProc := FlistViewOldWndProc; FListViewOldWndProc := nil; end; Как я понимаю, когда курсор подводишь справа к | (между 1-й и 3-й колонкой) , то это считается правый край 2-й колонки, которая скрыта. А т.к. расширение ее запрещается, то она не передвигается. ??? Как я понимаю, HDN_DIVIDERDBLCLICKW можно ведь не обрабатывать. Если ширина колонки =0, то при двойном нажатии она не изменяет свою ширину. С уважением DaDe. |
Сообщ.
#9
,
|
|
|
Цитата DaDe @ Как я понимаю, когда курсор подводишь справа к | (между 1-й и 3-й колонкой) , то это считается правый край 2-й колонки, которая скрыта. А т.к. расширение ее запрещается, то она не передвигается. ??? Да Ты попадаешь в область разделителя 2-й и 3-й колонок. Т.е. ресайзить колонку можно не только кликнув на самОм разделителе (полоске), но и на некотором расстоянии (если не ошибаюсь, 5*GetSystemMetrics(CX_EDGE)) справа и слева от него. И хоть ты и сделал ширину второй колонки =0, область разделителя справа осталась и реально ты ресайзишь вторую колонку. Чтобы это обойти, обычно запрещают изменение курсора в этой области. Для этого нужно заменить оконную процедуру Header'а у ListView и обработать WM_SETCURSOR. Цитата DaDe @ Как я понимаю, HDN_DIVIDERDBLCLICKW можно ведь не обрабатывать. Если ширина колонки =0, то при двойном нажатии она не изменяет свою ширину. Изменит. HDN_BEGINTRACK и HDN_DIVIDERDBLCLICK - это два разных сообщения. И обрабатывать нужно оба. Вот небольшой примерчик unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) ListView1: TListView; Button1: TButton; Button2: TButton; procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private fCursor:HICON; fWidth:Integer; FListViewOldWndProc: TWndMethod; procedure ListViewNewWndProc(var Msg: TMessage); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} uses commctrl; procedure TForm1.ListViewNewWndProc(var Msg: TMessage); var hdn: ^THDNotify; begin if Msg.Msg = WM_NOTIFY then begin hdn := Pointer(Msg.lParam); case hdn.hdr.code of HDN_BEGINTRACKA,HDN_BEGINTRACKW,HDN_DIVIDERDBLCLICKA,HDN_DIVIDERDBLCLICKW: begin if hdn.Item=1 then Msg.Result := 1 else FListViewOldWndProc(Msg) end; else FListViewOldWndProc(Msg); end end else FListViewOldWndProc(Msg); end; // оконная процедура Header'а function NewHeaderProc(wnd:HWND; uMsg:UINT; wParam:WPARAM; lParam:LPARAM):integer; stdcall; var hti:THDHITTESTINFO; pt:TPoint; begin if uMsg=WM_SETCURSOR then begin GetCursorPos(pt); ScreenToClient(wnd, pt); hti.Point:=pt; SendMessage(wnd, HDM_HITTEST, 0, cardinal(@hti)); if (hti.Item=1) or ((hti.Item=2) and (hti.Flags=HHT_ONHEADER)) then begin SetCursor(Form1.fCursor); // устанавливаем дефолтный курсор Result:=1; end else result:=CallWindowProc(Pointer(GetWindowLong(wnd,GWL_USERDATA)),wnd,uMsg,wParam,lParam) end else result:=CallWindowProc(Pointer(GetWindowLong(wnd,GWL_USERDATA)),wnd,uMsg,wParam,lParam) end; // прячем 2-ю колонку procedure TForm1.Button1Click(Sender: TObject); begin fWidth:=ListView1.Columns[1].Width; ListView1.Columns[1].Width:=0; end; // показываем 2-ю колонку procedure TForm1.Button2Click(Sender: TObject); begin ListView1.Columns[1].Width:=fWidth; end; procedure TForm1.FormCreate(Sender: TObject); var hwndHeader:HWND; i,j:Integer; begin fCursor:=LoadCursor(0,IDC_ARROW); // дефолтный курсор FListViewOldWndProc := ListView1.WindowProc; Listview1.WindowProc := ListViewNewWndProc; // сабклассим Header (адрес старой оконной процедуры будем хранить в GWL_USERDATA) hwndHeader:=ListView_GetHeader(ListView1.Handle); SetWindowLong(hwndHeader,GWL_USERDATA,SetWindowLong(hwndHeader, GWL_WNDPROC, LPARAM(@NewHeaderProc))); end; procedure TForm1.FormDestroy(Sender: TObject); var hwndHeader:HWND; begin ListView1.WindowProc := FlistViewOldWndProc; FListViewOldWndProc := nil; hwndHeader:=ListView_GetHeader(ListView1.Handle); SetWindowLong(hwndHeader,GWL_WNDPROC,GetWindowLong(hwndHeader,GWL_USERDATA)) end; end. |
Сообщ.
#10
,
|
|
|
Цитата Krid @ Чтобы это обойти, обычно запрещают изменение курсора в этой области. Для этого нужно заменить оконную процедуру Header'а у ListView и обработать WM_SETCURSOR. Спасибо. С уважением DaDe. |
Сообщ.
#11
,
|
|
|
Krid
Если ListView (стиль vsReport) имеет 1500 записей (например) и если удалять 2-ю колонку так. ListView1.Items.BeginUpdate; for i:=0 to ListView1.Items.Count-1 do ListView1.Items[i].SubItems.Delete(0); ListView1.Columns.Delete(1); ListView1.Items.EndUpdate; то если находишься в начале списка, то этот процесс занимает 40 ms. а если находишься в конце списка, то этот процесс занимает от 1-ой до 2-х секунд. Вопрос: Как это сделать быстрее. Можно конечно сделать так, но как-то криво получается. x:=ListView_GetTopIndex(ListView1.Items.Handle); ListView1.Scroll(0,ListView1.Items.Item[0].Position.Y-16); // Удаление столбца и SubItema ListView1.Scroll(0,ListView1.Items.Item[x].Position.Y-16); Но если делаешь ListView1.Scroll(0,ListView1.Items.Item[x].Position.Y-16) в блоке BeginUpdate и EndUpdate, то это работает в начале списка, а в конце списка он не возвращает на туже запись!!! И еще один вопрос: Почему в Explorer`е когда удаляется столбец ничего не моргает, а у меня моргает??? С уважением DaDe. |
Сообщ.
#12
,
|
|
|
В своем приложении использовал код из #9
но при закрытии начало вываливаться AV после того как изменил procedure TForm1.FormDestroy(Sender: TObject); var hwndHeader:HWND; begin // освобождение моих данных ListView1.WindowProc := FlistViewOldWndProc; FListViewOldWndProc := nil; hwndHeader:=ListView_GetHeader(ListView1.Handle); SetWindowLong(hwndHeader,GWL_WNDPROC,GetWindowLong(hwndHeader,GWL_USERDATA)) end; на procedure TForm1.FormDestroy(Sender: TObject); var hwndHeader:HWND; begin // освобождение моих данных ListView1.WindowProc := FlistViewOldWndProc; // FListViewOldWndProc := nil; hwndHeader:=ListView_GetHeader(ListView1.Handle); SetWindowLong(hwndHeader,GWL_WNDPROC,GetWindowLong(hwndHeader,GWL_USERDATA)) end; AV пропало При пошаговой отладке определил что после выполнения SetWindowLong(hwndHeader,GWL_WNDPROC,GetWindowLong(hwndHeader,GWL_USERDATA)) управление передается в ListViewNewWndProc Пожалуйста помогите разобраться почему так происходит |
Сообщ.
#13
,
|
|
|
есть такая штука VirtualListView называется, у Delphi в примерах должно быть где-то как это делается, там работа с большим количеством записей, но мне кажется должно и со столбцами работать
|
Сообщ.
#14
,
|
|
|
Step а причем здесь VirtualListView я разве хоть слово о нем написал?
Как мне кажется я задал вполне конкретный вопрос. Почему после назначения ListView1.WindowProc := FlistViewOldWndProc; управление переходит все равно в ListViewNewWndProc? Или это было просто для написать. |
Сообщ.
#15
,
|
|
|
joiner, просто этот компонент куда гибче и мощнее ListView, и манипуляции со столбцами там можно творить без всякого колдунства. Потому как когда на середине проекта ты столкнешься с тем, что, к примеру, LV на 10к записей еле ворочается, переделывать будет куда муторней
|