Иконки в заголовках лист-бокса

Кратко о отрисовке иконок в заголовке лист-бокса.
Как известно, в Кларионе не предусмотрен стандартный вывод иконок в заголовки колонок лист-бокса. В этой статье я расскажу вам о том, каким образом
можно обойти это ограничение.

Идея достаточно проста. Если рассмотреть заголовок как таковой, то он похож на обычную кнопку.


На стандартной кнопке мы легко можем показать иконку. Более того мы можем отцентрировать иконку по правому/левому краю или по центру. Таким образом, наложив стандартную кнопку на заголовок колонки мы добьемся желаемого результата. Поэтому все что нужно, это знать координаты заголовка.

Создание кнопки
В Кларионе есть возможность создания контролов «на лету». Для этого используется оператор CREATE. Команда создает контрол. Атрибуты контрола (текст, размеры, иконка и т.д.) задаются через соответствующие свойства. Создание кнопки выглядит примерно так:

ButtonFEQ LONG ! переменная, которая будет использована для идентификации кнопки

 code

 ButtonFEQ=CREATE(0,CREATE:Button)          ! создаем кнопку
 ButtonFEQ{Prop:Icon}=clip(ImageName)       ! присваиваем какую-нибудь картинку
 ButtonFEQ{Prop:Flat}=TRUE                  ! кнопка будет плоской
 ButtonFEQ{Prop:Skip}=TRUE                  ! пропускать кнопку при переходе по TAB
 ButtonFEQ{Prop:Trn}=TRUE                   ! прозрачная кнопка
 ButtonFEQ{Prop:Left}=TRUE                  ! выравнивание влево, на кнопке ничего не будет написано
 ButtonFEQ{Prop:Text}=''                ! строка нужна для правильного выравнивания
 ButtonFEQ{Prop:Xpos}=10                    ! задание размеров кнопки
 ButtonFEQ{Prop:Ypos}=10
 ButtonFEQ{Prop:Width}=20
 ButtonFEQ{Prop:Height}=10
 unhide(ButtonFEQ)                          ! показать вновь созданную кнопку

Определение координат
Определение координат, это чистой воды лирика. Нет ничего сложного, используются известные свойства лист-бокса … и эмпирические наблюдения, на которые было потрачено каких-то полных 3 дня 🙂

Таблица: Свойства лист-бокса Свойство Описание

  • Prop:Xposx координата лист-бокса
  • Prop:Yposy координата лист-бокса
  • Prop:Widthширина лист-бокса
  • Prop:HeaderHeightвысота заголовка
  • Prop:HScrollPosзначение горизонтального скролл бара
  • Proplist:Width, ColumnNumширина колонки ColumnNumProplist:Exists, ColumnNumвозвращает TRUE, если колонка существует, в противном случае FALSE
  • Proplist:HeaderLeft, ColumnNumвозвращает TRUE, если текст колонки выравнивается по левому краю
  • Proplist:HeaderRight, ColumnNumвозвращает TRUE, если текст колонки выравнивается по правому краю
  • Proplist:HeaderCenter, ColumnNumвозвращает TRUE, если текст колонки выравнивается по центру
  • Proplist:HeaderLeftOffset, ColumnNumотступ текста колонки слева
  • Proplist:HeaderRightOffset, ColumnNumотступ текста колонки справа
  • Proplist:HeaderCenterOffset, ColumnNumотступ текста колонки по центру — не работает
  • Proplist:MouseDownRowномер строки, на которой нажали кнопку мыши, 0 если нажали на заголовке колонки
  • Proplist:MouseDownZoneзона, в которой нажали кнопку мыши, LISTZONE:Header если нажали на заголовке колонки
  • Proplist:MouseDownFieldномер колонки, на которой нажали кнопку мыши
  • Proplist:GroupNo, ColumnNumвозвращает номер группы, если колонка входит в группу, если не входит возвращает номер колонки
  • Proplist:GroupNo+Proplist:Group, ColumnNumвозвращает количество колонок в группе, если колонка входит в группу, если не входит возвращает ноль
  • Proplist:Width+Proplist:Group, ColumnNumвозвращает ширину группы, в которую входит указанная колонка

Это далеко не полный перечень свойств лист-бокса.

Известные проблемы
Свойство Proplist:Width,ColumnNum не всегда возвращает корректную ширину колонки. Это происходит в случае если колонка является последней колонкой в лист-боксе или в группе.
Если колонка является последней колонкой в листе: ширина определяется как разница между всей шириной лист-бокса и шириной всех видимых колонок левее искомой. Если колонка является последней колонкой в группе — как разница между шириной группы и шириной всех колонок в группе левее искомой.

Свойство Prop:HScrollPos. Возвращает значение горизонтального скролл-бара. Это значение изменяется от 0 до N, где N = (количества колонок в листе) — 1.
Немного расшифрую: допустим у вас есть 4 (четыре) колонки в листе. Следовательно свойство Prop:HScrollPos может принимать значения 0, 1, 2, 3. Когда горизонт. скролл бар установлен в крайнее левое положение свойство Prop:HScrollPos=0, в крайнее правое Prop:HScrollPos=3. Т.е. это свойство как бы говорит — сколько колонок спрятано, не видно, находится левее первой видимой колонки в листе.
Это свойство работает хорошо … пока не появляются группы колонок. При наличии групп свойство изменяется до M, где M = (количество колонок без групп) + (количество групп) — 1.

Вертикальный скролл-бар. Вертикальный скролл-бар может присутствовать или отсутствовать в лист-боксе. Также ширина скролл-бара зависит от настроек Windows. Я не нашел стандартной возможности Клариона определить эти параметры. Существуют две API функции: GetSystemMetrics(SM_CXVSCROLL) — возвращает ширину скролл-бара, GetScrollInfo — помогает определить видимость скролл-бара.

Точность измерения размеров. Все измерения желательно проводить в пикселях. Для этого для окна необходимо установить свойство MyWindow{Prop:Pixels}=TRUE. Не забудьте восстановить это свойство в первоначальное значение после всех измерений.
Я измеряю x-координату путем суммирования ширины всех колонок левее искомой. При таком способе необходимо помнить о наличии разделительной линии Proplist:RightBorder,ColumnNum шириной в 1 пиксель.

При измерении высоты заголовка колонки и y-координаты необходимо помнить о возможности группирования колонок.

Перерисовка кнопок
После того как вы создали кнопку и наложили ее на заголовок лист-бокса попробуйте изменить размеры колонки. Вы увидите, что кнопка останется на месте, т.е. не переместится и не изменит своих размеров вместе с колонкой. Если вы переместите ползунки вертикального или горизонтального скролл-бара, то кнопка вообще исчезнет. «Исчезновение» произойдет также и при изменении размеров лист-бокса. Раз так, следует учесть этот момент и при возникновении таких событий перерисовывать кнопки (опять узнать размеры заголовков колонки и присвоить их свойствам кнопки).
Для учета изменения размеров колонок, можно использовать событие EVENT:ColumnResize. Например, так:

 REGISTER(EVENT:ColumnResize,address(self.Redraw),address(SELF),,self.ListBoxFEQ)

эта строка говорит о том, что при возникновении события EVENT:ColumnResize в контроле self.ListBoxFEQ (лист-бокс) будет вызван метод self.Redraw (перерисовать кнопки). Возможно вы уже подумали, что для обработки изменения размеров лист-бокса необходимо использовать событие Event:Sized. Неа…нельзя 🙂 Потому что при обработке события Event:Sized Кларионом system is modal, а это означает, что пока идет обработка этого сообщения вы ничего не можете сделать с контролами. Если не можем напрямую обработать событие, то это можно сделать через сабклассинг. Я отлавливаю сообщения WM_HSCROLL/WM_VSCROLL (перемещение ползунка горизонт/вертик. скролл-баров ), WM_SIZE (изменение размеров) и посылаю свое собственное событие EVENT:Redraw лист-боксу.
А для этого события, также выполнен код:

 REGISTER(EVENT:Redraw,address(self.Redraw),address(SELF),,self.ListBoxFEQ)

пока происходит этот несколько «хитрый» обмен сообщениями system перестает быть modal 🙂 и вы сможете изменить размеры ваших кнопок так как вам нужно. Следует помнить, что может возникнуть ситуация, что заголовка колонки, на котором вы хотите отрисовать кнопку, не видно на экране.
В этом случае ранее отрисованную кнопку необходимо скрывать, используя свойство Prop:Hide или совсем уничтожать, используя команду DESTROY.

Эмуляция нажатия кнопки мыши на заголовке
Здесь тоже все просто. Опять используем сабклассинг и отлавливаем события WM_LBUTTONDOWN — нажатие левой кнопки мыши, WM_LBUTTONUP — отпускание левой кнопки мыши.
Используя свойства Proplist:MouseDownField, Proplist:MouseDownRow, Proplist:MouseDownZone убеждаемся, что нажали именно на заголовке, и узнаем номер колонки.
По нажатию кнопки создаем на заголовке контрол Region. Эффект нажатия создается за счет выставления атрибута ?Region{Prop:Bevel,1}=-1. По отпусканию кнопки регион уничтожается.


Код
Эта статья сможет вам помочь понять и разобраться в коде класса, который позволит вам отрисовывать иконки в заголовке лист-бокса и отлавливать момент нажатия кнопок мыши. Переопределив виртуальные методы, вы сможете задать свое поведение при нажатии на заголовок: это может быть сортировка колонок; реверсная сортировка; вы можете обработать нажатие клавиш Ctrl и Shift вместе с кнопкой мыши для создания множественной сортировки и т.д.
Код выполнен на C55H, ABC, на Legacy-шаблонах не проверялся.

xClasses (456)

© Still Zero, 2005-2006. Все права защищены.