Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

TlistView и как его побеждать

513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
TListView
Друзья, хочу поделиться своим опытом с TlistView. Многое из того, что я тут напишу Вы легко найдете и многие знаете, но некоторые вещи я с трудом отрыл и в не самом готовом виде.
Сначала опишу, что мне следовало сделать:
Была задача создать список чудо-строк. Чудо-строки это неокторого рода информация, которая для Вас никакого интереса не представляет, просто будем называть их чудо-строками. Количество чудо-строк заведомо неизвестно, количество полей из которых соcтоит строка тоже неизвестно. Первое поле – ID, остальные поля какие угодно и в любом количестве.
Необходимо, чтобы в зависимости от определенного признака строки выводились с такими цветом:
1. первый столбец темно красный, остальные светло красные
2. первый столбец темно зеленый, остальные светло зеленые
3. + к цвету фона должен в зависимости от некоего другого признака меняться цвет текста (черный/серый) и жирность текста

Цвет выделителя (выделенной строки) должен быть не стандартным. Важно! Первую колонку никогда не выделять, чтобы было ясно какая эта запись-красная или зеленая. Подробнее, как выглядит мой TlistView – на рисунке.
Так же надо, чтобы сохранялись в ИНИ файле ширины колонок, их порядок следования и “отображаемость” (видимость).

1. Почему я предпочел TListView компонентам TStringGrid и TDbGrid

2. Проблемы с которыми я встретился - столбцы:
2.1 Скрытие столбцов
2.2 Порядок следования столбцов (первый двигать нельзя – запрет на передвижение первого столбца)
2.3 Ширина столбцов
2.4 Сортировка при нажатии на столбец. Отображение стрелочек


3. Проблемы с которыми я встретился - кастомизация:
3.1 Изменить цвет ячеек, причем так, чтобы одна ячейка была одного цвета, а другая другого
3.2 Изменить цвет ячеек - пол дела, но вот беда - как изменить цвет выделителя (выделенного элемента) со стандартного на свой. При выделенной строке, надо, чтобы первый столбец не выделялся
3.3 Как писать в TListView своим шрифтом (не стандартным в системе Windows)? Если его устанавливаю, то он не помещается в строку.

4. Глюки TListView
4.1 Глюки TListView при определении колонки, если они перетсавляли местами
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
3.1 Все дело в том, что DBGrid отобрадает то, что тащит из БД, мне же надо было дополнять тот select в некоторых случаях другими данными, отсутствующими в выборке, потому я перешл на TStringGrid. TStringGrid позволял мне делать мне всю эту цветовую гамму, которая есть на рисунке, но очень он глючно работал с выделениями строк, приходилось долавливать за него и до выделять, в итоге комбинации клавиш типа Shift / ctrl вверх вниз стали нетривиальны по своей обработке и перерисовке, и в общем по совету заслуженного mfender я пришел к TListView.
Надо сказать, что чем мне понравился RlistView, тиак это тем, что каждая строка компонента может связаться с неким обхектом. Это очень удобно. У меня каждая строка это class, у которого есть переменные – поля записи (а точнее колонки с их значениями):
 
Код:
type
  TMyStr = class
  public
    Ntfn_id:       string;  // логическое ID
    Selected: boolean; // покахывает выделеоно оно в гриде или нет
    info_fields: TStringDynArray; // массив значения полей  
end;

И есть массив StrList с этими TMyStr (то есть массив всех строк).

При заполнении TlistView я дополнительно связываю каждую строку ListView с эти объектом:
 
Код:
ListView.AddItem(StrList  .Ntfn_id, StrList  );
ListView.Items.SubItems.Add(StrList  .info_fields[0])
ListView.Items.SubItems.Add(StrList  .info_fields[1])
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
К сожалению в режиме vsReport (да и в другом тоже) нет свойств, чтобы прятать колонки, единственный вариант, это установить им ширину=0 (тогда колонки прячутся). Но в таком случае можно ее потом растянуть опять в нормальный размер, что не всегда подходит, потому что она как бы все таки спрятана. Многие в таком случае запрещают изменение размера колонок, как это сделать – поищите в сети, я не делал, так как мне это не подошло. Возможность менять размер колонок мне была нужна. Я вышел таким образом: когда пользователь в специальном окошке, ставит галочку “не показывать такое-то поле”, то я просто перерисовываю TListView без этих колонок.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Я включил событие OnColumnDragged, предварительно установив свойство FullDrag:=True. В этом событии я сначала проверял, что первая колонка не стала не первой. И если это было так, то просто насильственно возвращал ее на свое место.
 
Код:
for i:=0 to ListView.Columns.Count-1 do begin
      if ListView.Columns.Items.Caption=’ID’ then begin //нашли кононку
        // первая колнка всегда первая должна быть!
         ListView.Columns.Items.Index:=0;
        break;
      end;
  end;

В итоге если пользователь перетащил, скажем пятую колонку на первое место, то она становилась не первой, а второй. Далее подобным образом я перебирал все колонки и сохранял их порядок следования в массив. Порядок следования лежит в переменной Index (ListView.Columns.Items.Index);
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Ширину столбцов менять можно (это реализовано в самом компоненте). НО! Как быть если нужно сохранять ширину столбцов? Тоже вроде все просто, перебираем все колонки и сохраняем их ширины:
 
Код:
for i:=0 to ListView.Columns.Count-1 do begin
        // первая колнка всегда первая должна быть!
         My_param_mus:=ListView.Columns.Items.Width;
end;

P.S. На самом деле вместо My_param_mus лучше использовать массив объектов. Где объект имеет свойства ширины колонки, названия колонки, видимости и порядка следования.
Тут тоже был подводный камень, так как у меня был не один TListView, а несколько, мне нужно было сохранять размеры колонок в момент изменения размера колонок, но такого события нет. Помимо всего прочего, забегая вперед, скажу что это событие так же было необходимо и для своей отрисовки, почему именно написано в пунктах 3.1-3.4.
Тут мне помогла статья и компонент: http://www.cracklab.ru/pro/faq.php?pg=2450&ln=50. Тут описано как ввести события для TlistView: OnColumnResize, OnBeginColumnResize, OnEndColumnResize. В итоге на событие OnColumnResize я повесил код, который перебирает все колонки и сохраняет их ширину.
PS на самом деле этот код я использовал не напрямую, а встроил в другой, но об этом ниже.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Необходимо было при нажатии на заголовок проводить сортировку по возхврастанирю/ убыванию и отображать стрелки направления сортировки.
Тут мне помогла статья: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1366 с компонентом TatwListView. Она мне очень помогла, тут все хорошо расписано и с примерами (советую скачать). Создается новый компонент на вкладке Samples. Сортировки у меня были написаны свои, еще когда я работал со StringGridом, поэтому я не стал заморачивать как сортировать методами TListView, а просто ловил стандартное события нажатия на заголовке ListView – OnColumnClick. У меня была переменная, показывающая направление сортировки и я просто делал в этом событии следующее: смотрел на флаг и рисовал соответствующие стрелки силами компонента
Код:

Var
AO: TArrowOptions;
    Begin

TatwListView
  if sort_asc_desc then begin
   sort_asc_desc:=false;
   AO.ArrowType:= atDown;
  end else begin
   AO.ArrowType:= atUp;
   sort_asc_desc:=true;  
  end;

ListView.ArrowOptions:= AO;

Затем просто сортировал своими процедурами в своих массивах, после чего перевыводил содержимое listView в отсортированном виде.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Самое простое как оказалось это использовать события OnCustomDrawItem и OnCustomDrawSubItem. Эти события позволяют установить параметры цвета и стиля текста и ничего не выводить и не рисовать, с установленными тут параметрами цвета виндовс сам все нарисует довольно хорошо:
Код:
procedure TForm1.ListViewCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
  bcolor1:TColor;
 begin
   v_notific:=Item.Data; // найду свой объек-строку с которым связана данная строка TlistView
   if  v_notific.BadGood_flag then begin
      bcolor1 := $006666FF; //темно красный
    end else begin
      bcolor1 := $33CC66;  // темно зеленый
   end;
Sender.canvas.Brush.Color:=bcolor1;
End;

Таким образом в зависимости от признака первая колонка была темно зеленый или темно красной.
Далее в событии OnCustomDrawSubItem я делал тоже самое, но цвет ставил светло красный или светло зеленый. В итоге первая колонка была темно красной или темно зеленой, а все остальные светлыми.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
(выделенного элемента) со стандартного на свой. Увы, такие вещи как
if cdsSelected in State then begin
Sender.canvas.Font.color:=clRed;
end;
Не прокатят, так как виндовс все равно перерисует все таким цветом, каким он хочет. Поэтому вариант один – рисовать самому все в этой ф-ци, устанавливая флаг DefaultDraw:=false. Но и тут у меня возникли проблемы. Нужно чтобы цвет первой колонки отличался от цвета других колонок, первая колонка не выделялась, потому идем дальше.
Ставим свойство OwnerDraw в True и юзаем событие OnDrawItem, которое вызывает после всех виндовс перерисовок и я могу рисовать все что хоче и никто ничего не перерисует, но рисовать на 100% придется самому. Основная проблема, как рисовать SubItems?- ведь их координат нет. Они вычисляются исходя из ширины колонок:
Код:
procedure TNotificationClass.MyListViewDrawItem(Sender: TCustomListView;
  Item: TListItem; Rect: TRect; State: TOwnerDrawState);
var
 vid: string;
 bcolor1,bcolor2,fcolor:TColor;
 v_Rect:TRect;
 v_notific: TNotification;
 canvasq:TCanvas;
  i:integer;
  w:integer;
   R: TRect;
begin

   v_notific:=Item.Data;
   if  v_notific.BadGood_flag then begin
      bcolor2 := $00CCCCFF;  // светло красный
      bcolor1 := $006666FF; //темно красный
    end else begin
      bcolor2 := $00CCFFCC;  // светло зеленый
      bcolor1 := $33CC66;  // темно зеленый
   end;

    if v_notific.UNneeded_flag then begin
        Sender.canvas.Font.color:=clSilver;//clSilver;//clWhite;
    end else begin
        Sender.canvas.Font.color:=clBlack;
    end;

    if (v_notific.Looked_flag=false) and (v_notific.UNneeded_flag=false) then begin
        Sender.canvas.Font.Style:=[fsbold];//clWhite;
    end;

// если выделена срока, то ставлю свой цвет!
    if  odSelected in  State then begin
        bcolor2:=$00FFB34E;
    end;

    v_Rect:=Rect;
    v_Rect.Bottom:=Rect.Bottom+2;
    Sender.canvas.Brush.Color:=bcolor1;
    Sender.canvas.FillRect(Rect);
    v_Rect:=Rect;
    w:=0;
    with (Sender as TCustomListView ) do begin
       canvasq:=Canvas;
       canvasq.TextOut(Rect.Left+5, Rect.Top, item.Caption);
       v_Rect.Left:=v_Rect.Left+w;
       w:=Rect.Left;
       for i:=0 to Item.SubItems.Count-1 do begin
          w:=w+Column.Width;
          v_Rect.Left:=w;
          canvas.Brush.Color:=bcolor2;
          canvas.FillRect(v_Rect);
          canvas.TextRect(v_Rect,v_Rect.Left+5,v_Rect.Top,Item.SubItems);
      end;
   end;

end;

Текст, возможно, не особо ясен, но главное тут показано как перерисовать ListView полностью под свои требования.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Много времени я убил вот на что – если мы перерисовываем все сами так как нам надо (см. 3.2), что возможно только при OwnerDraw := True виндовс не вмешивается в наши отрисовку и если мы рисуем каким-то крупным шрифтом (ListView.Font:=Button2.Font;), то буквы просто не помещаются. Как объяснить виндосу, что не смотря на то, что я рисую сам, надо подгонять высоту ячеек под тот шрифт, котрый утснаовлен? Никто так точно и не сказал КАК! Но  если создать ПУСТОЙ TImageList и установить ListView. smallImages:= MainFrm.ImageList1, то о чудо! Виндовс рисует решетку нужной высоты, а мы вне йрисуем каким хотим цветом.
513
09 сентября 2008 года
Yurec
228 / / 21.09.2005
Вохможно многое написано сумбурно и не ясно. Если кому то действительно не победить LIStView, то пишите мне, возможно, я смогу помочь, как видно из картинки все-таки удалось его кастомизировать под нужные цвета и принципы выделения строк.
14
09 сентября 2008 года
Phodopus
3.3K / / 19.06.2008
Ниасилил :)
Могу лишь сказать что есть такая штука - VirtualTreeView. Офигенная штука, да еще и бесплатная, и профи написанная, да еще и новая версия относительно недавно вышла.
513
10 сентября 2008 года
Yurec
228 / / 21.09.2005
Цитата: Phodopus
Ниасилил :)
Могу лишь сказать что есть такая штука - VirtualTreeView. Офигенная штука, да еще и бесплатная, и профи написанная, да еще и новая версия относительно недавно вышла.



Спасибо,я поищу). Я просто с Дельфи недавно и не особо знаю что куда. Просто тут хотелось поделиться опытом извращенства с ListView, так как в сети нет точных ответов на все эти вопросы. А то что дЕЛЬФИ КОМПОНЕНТЫ стандартные слабоваты это факт. Говорят, что ни в одной компании, которая софт пишет, не юзают стандатртных колмпонентов.

6
10 сентября 2008 года
George
4.1K / / 05.01.2007
имхо нормальные компоненты. иногда хочется навороченности, тогда просто определяешь новый класс, где добавляешь всю навороченность. а стандартные компоненты вполне подходят для стандартных задач.
1
10 сентября 2008 года
kot_
7.3K / / 20.01.2000
Цитата: Yurec
Спасибо,я поищу). Я просто с Дельфи недавно и не особо знаю что куда. Просто тут хотелось поделиться опытом извращенства с ListView, так как в сети нет точных ответов на все эти вопросы. А то что дЕЛЬФИ КОМПОНЕНТЫ стандартные слабоваты это факт. Говорят, что ни в одной компании, которая софт пишет, не юзают стандатртных колмпонентов.


Скорее с точностью до наоборот. Использование "навороченных" компонентов без особой необходимости - уже не есть хорошо. Да и компаний "которая софт пишет" и при том использует делфи (или билдер - один хрен) можно пересчитать по пальцам одной руки - в основном это удел самопальных проектов мелких отделов разработки с непомерно большими амбициями.

303
12 сентября 2008 года
makbeth
1.0K / / 25.11.2004
Цитата: kot_
Да и компаний "которая софт пишет" и при том использует делфи (или билдер - один хрен) можно пересчитать по пальцам одной руки - в основном это удел самопальных проектов мелких отделов разработки с непомерно большими амбициями.


Это ты зря. Довольно много крупных проектов на Delphi/Builder, которые завоевали рынок и до сих пор развиваются (к примеру, ORTEMS). Другое дело, что новых проектов не появляется - предпочитают другие средства разработки.
Что касается использования сторонних компонентов, то тут все верно - не любит народ к внутренним глюкам своего проекта добавлять еще и глюки сторонних компонентов, да и пользователей учить потом... Используются в основном по необходимости подправленные стандартные компоненты. Ну или в крайнем случае создаются свои).

6
12 сентября 2008 года
George
4.1K / / 05.01.2007
[offtop]
makbeth, здорово. давно тебя не видно. ))
[/offtop]
1
12 сентября 2008 года
kot_
7.3K / / 20.01.2000
Цитата: makbeth
Это ты зря. Довольно много крупных проектов на Delphi/Builder, которые завоевали рынок и до сих пор развиваются (к примеру, ORTEMS). Другое дело, что новых проектов не появляется - предпочитают другие средства разработки.<...>


может быть - мне лично не приходилось сталкиваться с требованием к разработчику - знание Делфи/Билдер - хотя есть исключение - мелкие банки и собственные отделы разработок компаний. Так что это ИМХО и не холивара ради. :)

303
12 сентября 2008 года
makbeth
1.0K / / 25.11.2004
Washington, есть такое... :(

Цитата: kot_
может быть - мне лично не приходилось сталкиваться с требованием к разработчику - знание Делфи/Билдер - хотя есть исключение - мелкие банки и собственные отделы разработок компаний. Так что это ИМХО и не холивара ради. :)


Да не, я и сам не в пику ответил ;) Все это в общем то объяснимо. В России крупных девелоперских контор то и нет. Все как раз и ограничивается собственными "шарашками" (кстати, для мелкого софта Delphi самое оно - дешево). А "в европах" борланд, к сожалению, не смог конкурировать с Microsoft и Sun. Ну эта тема тоже пережевана тыщу раз :)

Ну и чтобы совсем не скатиться в оффтоп...
Yurec, вот оффсайт Virtual Trieview: http://www.soft-gems.net/
Там, кстати, можно найти и VT, заточенный под базы данных.
Стандартный TListView же никак не подходит под отображение записей БД, поскольку получается такое нехилое дублирование данных (по крайней мере, в твоем варианте).

513
15 сентября 2008 года
Yurec
228 / / 21.09.2005
Цитата: makbeth
Washington, есть такое... :(


Да не, я и сам не в пику ответил ;) Все это в общем то объяснимо. В России крупных девелоперских контор то и нет. Все как раз и ограничивается собственными "шарашками" (кстати, для мелкого софта Delphi самое оно - дешево). А "в европах" борланд, к сожалению, не смог конкурировать с Microsoft и Sun. Ну эта тема тоже пережевана тыщу раз :)

Ну и чтобы совсем не скатиться в оффтоп...
Yurec, вот оффсайт Virtual Trieview: http://www.soft-gems.net/
Там, кстати, можно найти и VT, заточенный под базы данных.
Стандартный TListView же никак не подходит под отображение записей БД, поскольку получается такое нехилое дублирование данных (по крайней мере, в твоем варианте).



Ок, спасибо, я учту это!

513
29 сентября 2008 года
Yurec
228 / / 21.09.2005
В продолжение темы :)
Есть такой глюк в TListView:
1. Создаем vsreport TlistView
2. Пишем такоф обработчик при нажатии на колонку OnColumnClick
 
Код:
{Событие, выполняющееся при нажатии на заголовок колонки}
procedure TNotificationClass.MyListViewColumnClick(Sender: TObject;
  Column: TListColumn);
ShowMEssage(Column.Caption);
end;

3. Запускаем прогу, жмем на колонку, получаем имя (точнее Caption) колонки
4. Перетаскиваем колонку (надо чтобы было включено заранее свойство FullDrag)
5. Жмем на колонку и получаем заголовок совсем другой колонки.
Модеоируется ошибка "на УРА". То есть до полного Delphi не переставлляет колонки у себя в голове, м в качестве Column: TListColumn передается старая колонка, которая ранбше стояла на этом месте.

Как победить:
Просто вычилим сами по какой колонке жмакнули:
Код:
{Событие, выполняющееся при нажатии на заголовок колонки}
procedure TNotificationClass.MyListViewColumnClick(Sender: TObject;
  Column: TListColumn);
var
 x,i,vorder: Longint;
vstr:string;
begin
  x:=c_ListView.ScreenToClient(mouse.CursorPos).x;

  for i:=0 to c_ListView.Columns.Count-1 do begin
     vleft:=vleft+c_ListView.Columns.Width;
     if vleft > x then begin
        vorder:=i;
        vstr:=c_ListView.Columns.Caption;
        break;
     end;
  end;
ShowMessage('Нажали на колонку №'+inttostr(vorder)+chr(10)+'Заголовок ее '+vstr );
end;
6
29 сентября 2008 года
George
4.1K / / 05.01.2007
Yurec, пожалуйста оформи код. Читать неприятно, да и правила всетаки для всех существуют.
513
02 марта 2009 года
Yurec
228 / / 21.09.2005
Друзья, очень много глюков найдено в TListView за время его недолгой работы. Всем совет, который уже прозвучал в этой теме:

качайте TVirtualTreeView с http://www.soft-gems.net/

хороший материал о том, как его использовать тут:
http://forum.vingrad.ru/act-Print/client/html/f-84/t-97620.html
535
16 февраля 2011 года
Нездешний
537 / / 17.01.2008
Выделение цветом различных знаков в ячейках. В примере - русские буквы одним цветом, английские - другим, остальные знаки - третьим.
Добавлю здесь, чтоб было все в одном месте, хоть и на плюсах

Код:
ListView->OwnerDraw = true;

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

inline bool __fastcall IsEnu(char Symbol)
{
    return ((64 < Symbol  &&  Symbol < 91)  ||  (96 < Symbol  &&  Symbol < 122));
}
//---------------------------------------------------------------------------

inline bool __fastcall IsRus(char Symbol)
{
    return ((-65 < Symbol  &&  Symbol < 0)  ||  (Symbol == -88));    //char(-88) = 'ё'
}
//---------------------------------------------------------------------------

void __fastcall DrawTextSpec(TCanvas *pCanvas, TRect &Rect, AnsiString Text)
{
    int offset = 3;
    for (int i = 1; i <= Text.Length(); ++i)
    {
        if (IsEnu(Text))    pCanvas->Font->Color = RGB(127, 51, 0);
        else if (IsRus(Text))   pCanvas->Font->Color = RGB(13, 107, 0);
        else    pCanvas->Font->Color = clWindowText;

        if (i != 1) offset += pCanvas->TextWidth(Text[i-1]);

        pCanvas->TextOut(Rect.Left + offset, Rect.Top, Text);
    }
}
//---------------------------------------------------------------------------

void __fastcall TFormMain::ListViewDrawItem(TCustomListView *Sender,
      TListItem *Item, TRect &Rect, TOwnerDrawState State)
{
    Sender->Canvas->Font->Color = clWindowText;
    DrawTextSpec(Sender->Canvas, Rect, Item->Caption);

    TRect recSubItem;
    for (int i = 0; i < Item->SubItems->Count; ++i)
    {
        ListView_GetSubItemRect(ListView->Handle, Item->Index, i+1, LVIR_BOUNDS, &recSubItem);
        DrawTextSpec(Sender->Canvas, recSubItem, Item->SubItems->Strings);
    }
}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог