Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
 
Остекление веранды: остекление балконов и лоджий в москве.

Многострочность и StringGrid

Голубь П.М.

Предисловие

  Уж сколько было написано про один из самых удачных компонентов в плане представления табличных данных. По этой стезе прошло немало великих людей. Ну что ж, попробую и я внести свою лепту. Знаком я с этим компонентом давно, и за это время успел узреть как плохие так и хорошие стороны. О хороших обычно никто не говорит, ну и я не буду. Продолжу о плохих и о том как их подправить на пользу всему прогрессивному человечеству.

Многострочность

  Ни раз и не два на всяческих форумах звучит призыв о помощи: "Нужен грид с возможностью объединения ячеек и многострочным выводом в ячейке". По поводу объединения ячеек я отправляю читателя к очень хорошей статье Михаила Зайкина, любителям стильного интерфейса советую статью Елены Филипповой, могу привести и еще несколько, но уверен что первые две и так должны вырезать за рекламу.

  Итак задача: требуется многострочный вывод в ячейку грида. Тут можно пойти двумя путями: первым и вторым, шучу. Можно сделать все капитально, на века, то есть создать свой компонент-наследник стандартного TStringGrid или воспользоваться встроенным обработчиком события TStringGrid.OnDrawCell.

  Начнем, как водится, с простого. Итак, откроем проект и положим на форму компонент TStringGrid (закладка Additional). По умолчанию среда Делфи присвоила нашему компоненту имя StringGrid1, оставим это без изменений.

  Теперь на секунду забудем о компонентах и "погрузимся" в изучение Windows API. Сегодня нам нужна будет только одна функция и имя ей DrawText. Что о ней нам может поведать справка?

function Windows.DrawText(hdc: HDC; lpString: PChar; nCount: integer; var lpRect: TRect; uFromat: Cardinal):integer;

  Функция DrawText выводит форматированный текст в определенном прямоугольнике. Функция форматирует текст, согласно указанному методу (выводит табуляцию, выравнивает символы, разрывает строки и т.д.). Перевод конечно свободный. Мы прочитали об этой функции всего одно предложение, а уже ее любим – ведь она умеет разрывать строки. Читаем дальше про параметры.

hDC
  Указывает на контекст устройства, напомню, что свойство Handle у TCanvas как раз и имеет тип HDC.

lpString
  Строка для вывода. Если параметр nCount равен -1, строка должна быть нуль-терминированная (null-terminated), то есть заканчиваться символом #0.

nCount
  Указывает количество символов в строке. Тут все ясно.

lpRect
  Переменная в которой передается запись типа TRect. Именно в этот прямоугольник будет выводится наш текст.

uFormat
  И наконец переменная для указания методов и типов форматирования. Все их перечислять не буду. Нас интересует константа с именем DT_WORDBREAK, которая собственно и отвечает за многострочный вывод.

  Ну и напоследок, при удачном завершении функция возвращает высоту выведенного текста. Очень удобная штука.

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

Sender: TObject
  Объект, который "просит" себя нарисовать, в нашем случае это будет StringGrid1.

ACol, ARow: Longint
  Номер колонки и строчки. Отсчет начинается с нуля.

Rect: TRect
  Идентифицурует прямоугольную область ячейки с номерами ACol:ARow.

State: TGridDrawState
  Состояние ячейки. Может принимать значения: gdSelected - выделенная ячейка, gdFocused - ячейка имеет фокус ввода, gdFixed - фиксированная ячейка.

  Все готово для первого эксперимента. Напишем обработчик.

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
var s:string;
begin
 s := 'Hello world! Hello world! Hello world! Hello world!';
 DrawText(StringGrid1.Canvas.Handle,PChar(s),length(s),Rect,DT_WORDBREAK);
end;

  Посмотрите на результат.

Первый пример
Как видите мы не использовали параметр Sender, а все потому, что мы точно знаем какой компонент нам нужно рисовать. Далее мы передали в функцию DrawText не просто строку S, а привели эту строку к типу PChar, который используется при вызовах функций API.

  Выравнивание по левому краю это конечно классика, но, лично мне, очень часто хочется задать выравнивание отличное от стандартного. Чем нам может помочь DrawText? Читаем справку и находим константы DT_LEFT, DT_CENTER и DT_RIGHT. Я думаю, объяснять зачем нужны эти константы не нужно. Давайте модифицируем наш код. Имеем:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var s:string;
    Flag: Cardinal;
begin
 s := 'Пожилая женщина ищет мужчину для вскапывания огородов';
 Case AСol mod 3 of
     0: Flag := DT_LEFT;
     1: Flag := DT_CENTER;
 else
     Flag := DT_RIGHT;
 end;
 Flag := Flag or DT_WORDBREAK;
 DrawText(StringGrid1.Canvas.Handle,PChar(s), length(s),Rect,Flag);
end;

Второй пример

  Стоит наверное обратить внимание на включение и выключение опций. В предыдщем примере опции хранились в переменной Flag. Все делается через битовые операции. Не вдаваясь в подробности скажу, что код

Flag := Flag or DT_LEFT

устанавливает опцию связанную с константой, а код
Flag := Flag and not DT_LEFT

эту опцию снимает. Тоже самое относится и к другим константам.

  Теперь обратим внимание на мелочи. Я сознательно оставил одну фиксированную строчку. Внимательно присмотритесь к нему. Наш текст вылез на элементы объемного оформления ячейки, да и в обычных ячейках он смотрится, прямо скажем, не очень. Посему давайте сделаем отступы слева и справа, таким образом:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var s:string;
    Flag: Cardinal;
begin
 s := 'пожилая женщина ищет мужчину для вскапывания огородов';
 Case Acol mod 3 of
   0: Flag := DT_LEFT;
   1: Flag := DT_CENTER;
  else
   Flag := DT_RIGHT;
 end;
 Flag := Flag or DT_WORDBREAK;
 Inc(Rect.Left,3); // отступ слева
 Dec(Rect.Right,3); // отступ справа
 DrawText(StringGrid1.Canvas.Handle,PChar(s),length(s),Rect,Flag);
end;
Третий пример

  Ну что ж, окончательно сформируем рабочий пример. При этом не забываем, что значения ячеек хранятся у нас в массиве Cells. Добавим и еще одну строчку кода, призванную вытереть содержимое ячейки перед непосредственно рисованием. И для того чтобы мы могли вводить текст сами, требуется в StringGrid1.Options включить опцию goEditing.

  Ну и последний штрих. Помните о том, что функция DrawText возвращает высоту выведенного текста? Так давайте исключим ситуацию, когда текст не умещается в ячейке. Мы просто увеличим высоту ячейки до нужного уровня. Имеем код:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var s:string;
 Flag: Cardinal;
 H: integer;
begin
 StringGrid1.Canvas.FillRect(Rect);
 s := StringGrid1.Cells[ACol,ARow];
 Case Acol mod 3 of
   0: Flag := DT_LEFT;
   1: Flag := DT_CENTER;
 else
   Flag := DT_RIGHT;
 end;
 Flag := Flag or DT_WORDBREAK;
 Inc(Rect.Left,3);
 Dec(Rect.Right,3);
 H := DrawText(StringGrid1.Canvas.Handle,PChar(s),length(s),Rect,Flag);
 if H > StringGrid1.RowHeights[ARow] then 
    StringGrid1.RowHeights[ARow] := H;  //увеличиваем
end;
Третий пример

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

Вывод

  Все гениальное просто. Но! К моему большому сожалению, у этого метода есть огромные минусы, которые сможете заметить и вы, если немножко поэксперемен­тируете. А именно. Первое, функция DrawText по непонятным мне причинам не хочет разрывать слитный текст, то есть текст без пробелов. Второе, в процессе редактирования значений ячеек весь текст также выстраивается в одну строчку.

  Но, на то он и простой метод, чтобы работать в ограниченных, тепличных, если хотите, условиях. Если эта статья найдет свое место среди ей подобных, в следующей мы поговорим о создании компонента на основе TStringGrid, обладающего замечательными качествами, в частности закрывающими найденные нами минусы. Ну, а если все будет еще более замечательно, то в третьей статье я хотел бы затронуть тему многострочного DBGrid'а. До следующих встреч.

   Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.
Автор статьи:  Голубь Павел
  

Другие статьи Наверх


      Титульная страница Поиск, карта сайта Написать письмо