Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2009.12.20;
Скачать: [xml.tar.bz2];

Вниз

Как узнать размер текста в произвольном контроле   Найти похожие ветки 

 
MsGuns ©   (2008-09-20 19:53) [0]

Это может быть, например, ячейка сетки и шрифт может быть разным в разных ячейках (код в соотв.обработчике).

Узнать надо не внутри OnDrawxxx, а "извне", скажем по кнопке.

Спасибо за ответы


 
Д С   (2008-09-20 20:08) [1]

type
TFontedControl=class(TControl)
public
property Font;
end;

....

TFontedControl(SomeControl).Font


 
Юрий Зотов ©   (2008-09-20 20:55) [2]

> Д С   (20.09.08 20:08) [1]

Достаточно даже просто объявить класс, без публикации свойства. И даже без END:
type
 TFontedControl = class(TControl);


> MsGuns ©   (20.09.08 19:53)

Если юзер рисует текст сам и размер "локального" фонта не связан ни с какими свойствами контрола, то как его узнаешь? Он известен только там, где рисуется текст.

Но можно придумать косвенный способ. Например, сканируем ячейку грида по горизонтальным линиям пикселей. Там, где первый раз встретился пиксель с цветом, отличным от цвета фона - там начало высоты текста. А там, где такой пиксель встретился последний раз - там конец высоты текста. Добавляем стандартные зазоры - получаем высоту фонта.


 
guav ©   (2008-09-20 21:40) [3]

Если можно заставить контрол нарисоваться на своём HDC, нарисовать на метафайле (TMetafileCanvas вроде), из метафайла можно получить всю прорисовку как последовательность GDI комманд.


 
MsGuns ©   (2008-09-20 21:43) [4]

>Юрий Зотов ©   (20.09.08 20:55) [2]

Собственно, вопрос возник из того, что необходимо иметь средство для автоматического изменения размеров клиентской части области "рисования" текста в зависимости от его фактической ширины и высоты в пикселях (в данный момент это ширина колонки грида, но хотелось бы сделать что-то универсальное)


 
Германн ©   (2008-09-21 01:32) [5]


> MsGuns ©   (20.09.08 21:43) [4]
>
> >Юрий Зотов ©   (20.09.08 20:55) [2]
>
> Собственно, вопрос возник из того, что необходимо иметь
> средство для автоматического изменения размеров клиентской
> части области "рисования" текста в зависимости от его фактической
> ширины и высоты в пикселях (в данный момент это ширина колонки
> грида, но хотелось бы сделать что-то универсальное)
>

А если я там в "клиентской части" захочу нарисовать картинку?


 
MsGuns ©   (2008-09-21 02:08) [6]

>Германн ©   (21.09.08 01:32) [5]
>А если я там в "клиентской части" захочу нарисовать картинку?

Дело в том, что пользователь при всем старании не сможет рисовать картинки - он может лишь вводить текст, изменяя его ширину и/или высоту (при многострочности) - вот их-то мне и надо бы как-то определять.
Конечно, при программном изменении физических параметров текстов (фонта, стиля и т.д.) определить что там реально нарисовано можно, вероятно только так, как описал Юра, но можно же наплевать на "хитрости" и предположить, что фонт един для всего контрола (речь идет о сетках к примеру). В этом случае можно, казалось бы, воспользоваться методами TCanvas так, как это, к примеру, описано у Пачеко, но вся сложность в том, как доступиться до канвы этого самого контрола. Я пробовал делать так, как реализовано в методе TextWidth канвы в самом VCL, используя HDC и WinAPI (саму функцию не помню, помню только что последние 2 символа ее названия "32") - но ширина текста определяется одинаково для разных физических ширин (брал просто два эдита, писал туда один и тот же текст, но в одном делал fsBold, а в другом нет, - код давал одиниковое значение, хотя ширины, естественно, отличались)


 
Германн ©   (2008-09-21 02:20) [7]


> MsGuns ©   (21.09.08 02:08) [6]
>
> >Германн ©   (21.09.08 01:32) [5]
> >А если я там в "клиентской части" захочу нарисовать картинку?
>
>
> Дело в том, что пользователь при всем старании не сможет
> рисовать картинки - он может лишь вводить текст, изменяя
> его ширину и/или высоту (при многострочности) - вот их-то
> мне и надо бы как-то определять.
> Конечно, при программном изменении физических параметров
> текстов (фонта, стиля и т.д.) определить что там реально
> нарисовано можно, вероятно только так, как описал Юра

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


 
Дмитрий_С   (2008-09-21 12:24) [8]


> MsGuns ©   (21.09.08 02:08) [6]

ты покажи кодом как ты делал


 
Бурундук   (2008-09-21 12:46) [9]

А если размеры определять именно в OnDrawxxx, где они доступны,
и сохранять их? Для грида идея такая:
В обработчике OnDrawCell заменить DrawText на метод специального
объекта, который, помимо собственно рисования текста, будет
сохранять данные о его размере.

Принцип работы примерно такой:

unit main;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, Grids, StdCtrls;

type
 TForm1 = class(TForm)
   SGrid: TStringGrid;
   Button1: TButton;
   procedure SGridDrawCell(Sender: TObject; ACol, ARow: Integer;
     Rect: TRect; State: TGridDrawState);
   procedure FormCreate(Sender: TObject);
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

 TTextSizeInfo = class(TComponent)
 private
   FCellTextSizes: array of array of TSize;
   function GetSizes(ACol, ARow: Integer): TSize;
   procedure SetSizes(ACol, ARow: Integer; const Value: TSize);
 public
   function Grid: TStringGrid;

   property TextSizes[ACol, ARow: Integer]: TSize read GetSizes write SetSizes;
   function DrawText(ACol, ARow: Integer; AText: PChar; ATextLen: Integer; var ARect: TRect; AFormat: Cardinal): Integer;
 end;

var
 Form1: TForm1;

function TextSizeInfo(Grid: TStringGrid): TTextSizeInfo;

implementation

uses Types;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 SGrid.Cells[0, 0] := "123";
end;

procedure TForm1.SGridDrawCell(Sender: TObject; ACol, ARow: Integer;
 Rect: TRect; State: TGridDrawState);
var Grd: TStringGrid;
   Fmt: Cardinal;
   S: string;
begin
 Fmt := DT_LEFT or DT_BOTTOM;
 Grd := (Sender as TStringGrid);
 with Grd.Canvas do
 begin
   S := Grd.Cells[ACol, ARow];
   FillRect(Rect);
   TextSizeInfo(Sender as TStringGrid).DrawText(ACol, ARow, PChar(S), Length(S), Rect, Fmt);
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var sz: TSize;
begin
 // Получение размеров текста в ячейке
 sz := TextSizeInfo(SGrid).GetSizes(0, 0);
 Caption := IntToStr(sz.cx);
end;

{--------------------------- TTextSizeInfo ------------------------------------}

function TextSizeInfo(Grid: TStringGrid): TTextSizeInfo;
var i: Integer;
begin
 for i := 0 to Grid.ComponentCount-1 do
   if Grid.Components[i] is TTextSizeInfo then
   begin
     Result := Grid.Components[i] as TTextSizeInfo;
     Exit;
   end;
 Result := TTextSizeInfo.Create(Grid);
end;

function TTextSizeInfo.DrawText(ACol, ARow: Integer; AText: PChar;
 ATextLen: Integer; var ARect: TRect; AFormat: Cardinal): Integer;
var sz: TSize;
begin
 Windows.DrawText(Grid.Canvas.Handle, AText, ATextLen, R, AFormat or DT_CALCRECT);

 sz.cx := ARect.Right - ARect.Left;
 sz.cx := ARect.Bottom - ARect.Top;
 TextSizes[ACol, ARow] := sz;

 Windows.DrawText(Grid.Canvas.Handle, AText, ATextLen, ARect, AFormat);
end;

function TTextSizeInfo.GetSizes(ACol, ARow: Integer): TSize;
begin
 Result.cx := 0;
 Result.cy := 0;

 if ( FCellTextSizes = nil                      )or
    ( Length(FCellTextSizes) < Grid.ColCount    )or
    ( Length(FCellTextSizes[0]) < Grid.RowCount )
  then
    SetLength(FCellTextSizes, Grid.ColCount, Grid.RowCount);

 if (ACol >= 0)and(ACol < Grid.ColCount)and
    (ARow >= 0)and(ARow < Grid.RowCount) then
   Result := FCellTextSizes[ACol, ARow];
end;

procedure TTextSizeInfo.SetSizes(ACol, ARow: Integer; const Value: TSize);
begin
 if ( FCellTextSizes = nil                      )or
    ( Length(FCellTextSizes) < Grid.ColCount    )or
    ( Length(FCellTextSizes[0]) < Grid.RowCount )
  then
    SetLength(FCellTextSizes, Grid.ColCount, Grid.RowCount);

 if (ACol >= 0)and(ACol < Grid.ColCount)and
    (ARow >= 0)and(ARow < Grid.RowCount) then
   FCellTextSizes[ACol, ARow] := Value;
end;

function TTextSizeInfo.Grid: TStringGrid;
begin
 Result := Owner as TStringGrid;
end;

end.


 
MsGuns ©   (2008-09-21 15:09) [10]

>Дмитрий_С   (21.09.08 12:24) [8]
>ты покажи кодом как ты делал

Код на работе. Но там нечего показывать, т.к. я полностью собезъянничал с VCL (TCanvas.TextWidth), а HDC "взял" с помощью ф-ции GetWindowDC(Edit1.Handle). Повторяю, оно все как бы работает, только дает в результате
какую-то фигню.

>Бурундук   (21.09.08 12:46) [9]

Вы, вероятно, не совсем поняли что я хочу.
У меня не возникает никаких проблем в обработчиках перерисовки, но мне надо несколько иное. Поясню на примере
Есть датасет и грид, его отображающий. После открытия датасета я хочу вычислить ширину колонок грида так, чтобы они вмещали полностью тексты всех полей каждой колонки. Т.е. грид еще и не думал рисоваться или отрисовал первые надцать строк, а я уже хочу знать ширины всех его колонок.

Пример с гридом - частный случай, но ИМХО, достаточно избыточно рисует проблему.


 
{RASkov} ©   (2008-09-21 16:48) [11]

> [10] MsGuns ©   (21.09.08 15:09)

Не совсем понятно... Но может какнить так:
// Просто эксперименты :)
procedure TForm1.Button1Click(Sender: TObject);
var C, R, N: Integer; S: String;
begin
 for C:=1 to StringGrid1.ColCount-1 do
  for R:=1 to StringGrid1.RowCount-1 do begin
   S:="Bla-Bla";
   for N:=0 to 30 do if Random(5)=2 then S:=S+#32 else S:=S+CHR(Random(30)+48);
   StringGrid1.Cells[C, R]:=S;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var C, R, W: Integer; Cnv: TControlCanvas; Rc: TRect;
begin
 Cnv:=TControlCanvas.Create;
 Cnv.Control:=StringGrid1;
 Cnv.Font.Assign(StringGrid1.Font);
 try
 for C:=1 to StringGrid1.ColCount-1 do begin
  W:=0;
  for R:=1 to StringGrid1.RowCount-1 do begin
   DrawText(Cnv.Handle, PChar(StringGrid1.Cells[C, R]), -1, Rc, DT_CALCRECT);
   if W<Rc.Right-Rc.Left+4 then W:=Rc.Right-Rc.Left+4;
  end;
  StringGrid1.ColWidths[C]:=W;
 end;
 finally
  Cnv.Free;
 end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var W: Integer; Cnv: TControlCanvas; Rc: TRect;
begin
 Cnv:=TControlCanvas.Create;
 Cnv.Control:=Edit1;
 Cnv.Font.Assign(Edit1.Font);
 DrawText(Cnv.Handle, PChar(Edit1.Text), -1, Rc, DT_CALCRECT);
 ShowMessage(IntToStr(Rc.Right-Rc.Left));
 Cnv.Free;
 Cnv:=TControlCanvas.Create;
 Cnv.Control:=Edit2;
 Cnv.Font.Assign(Edit2.Font);
 DrawText(Cnv.Handle, PChar(Edit2.Text), -1, Rc, DT_CALCRECT);
 ShowMessage(IntToStr(Rc.Right-Rc.Left));
 Cnv.Free;
end;

...попробывать? Конечно, если используется OnDrawCell(или что-то в этом роде), то тут уже нормально никак не получится, наверное только как предложил Юра, но с учетом [7] Германн...

> помню только что последние 2 символа ее названия "32")

GetTextExtentPoint32 ?


 
{RASkov} ©   (2008-09-21 16:51) [12]

> procedure TForm1.Button3Click(Sender: TObject);

Там где эксперимент был с эдитами. В эдитах был одинаковый текст с одинаковым шрифтом, но в одном Style:=[] а в другом Style:=[fsItalic, fsBold];


 
MsGuns ©   (2008-09-21 17:01) [13]

>Cnv:=TControlCanvas.Create;

Обана ! Это интересно - надо попробовать ;)

>GetTextExtentPoint32 ?

Таки да ;)

Спасибо, завтра поэкспериментирую. Способ, конечно, не учитывает "выпендресы" с перерисовкой "ручками", но мне это пока и не надо.

Но вот, блин, интересно, винда же при перерисовке "помнит" все нюансы рисования каждого фрагмента экрана. Как она, собабака, это делает ?
;))


 
MsGuns ©   (2008-09-21 17:05) [14]

Саш, если не затруднит, изобрази код, который подгоняет ширину эдита точно под текст при каждой его правке с клавиатуры, пожалуйста ;)

У меня здесь делфа глючит (есть, но она глючит - комп домашний и давно нуждается в переустановке винды, да все ручки не доходят) - не могу попробовать


 
{RASkov} ©   (2008-09-21 17:34) [15]

> [14] MsGuns ©   (21.09.08 17:05)
> Саш, если не затруднит, изобрази код, который подгоняет
> ширину эдита точно под текст при каждой его правке с клавиатуры

Может как-то так:
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var Rc: TRect;
begin
 with TControlCanvas.Create do try
  Control:=Edit1;
  Font.Assign(Edit1.Font);
  DrawText(Handle, PChar(Edit1.Text+CHR(Key)), -1, Rc, DT_CALCRECT);
  if Rc.Right-Rc.Left<30 then Edit1.Width:=30 else Edit1.Width:=Rc.Right-Rc.Left+4;
 finally
  Free;
 end;
end;


 
guav ©   (2008-09-21 18:56) [16]

> [3] guav ©   (20.09.08 21:40)

function DrawToMf(control: TWinControl): TMetafile;
var canvas: TMetafileCanvas;
begin
 control.DoubleBuffered := False;
 Result := TMetafile.Create();
 try
   canvas := TMetafileCanvas.Create(Result, 0);
   try
     control.PaintTo(canvas, 0, 0);
   finally
     canvas.Free();
   end;
 except
   Result.Free();
   raise;
 end;
end;

function EnhMetaFileProc(
 hDC: HDC;
 lpHTable: PHANDLETABLE;
 lpEMFR: PENHMETARECORD;
 nObj: Integer;
 lpData: TStrings): Integer; stdcall;
var
 c: TCanvas;
 s: WideString;
 p: PEMRText;
 lpETOW: PEMRExtTextOut absolute lpEMFR;
 ex: TSize;
begin
 PlayEnhMetaFileRecord(hDC, lpHTable^, lpEMFR^, nObj);
 if (lpEMFR^.iType = EMR_EXTTEXTOUTW) then
 try
   c := TCanvas.Create();
   c.Handle := hDC;
   if (PEMRExtTextOut(lpEMFR)^.emrtext.nChars > 0) then
   begin
     p := @(PEMRExtTextOut(lpEMFR)^.emrtext);
     SetString(
       s,
       PWideChar(PChar(lpEMFR) + p^.offString),
       p^.nChars);
     ex := c.TextExtent(s);
     lpData.Add(Format("Текст %s выведен шрифтом размера %d x %d",
      [AnsiString(s), ex.cx, ex.cy]));
   end;
 except;
   Result := 0;
   Exit;
 end;
 Result := 1;
end;

procedure EnumControlFonts(control: TWinControl);
var
 mf: TMetafile;
 b: TBitmap;
 r: TRect;
 sl: TStringList;
begin
 mf := DrawToMf(control);
 try
   b := TBitmap.Create();
   try
     b.Width := 1000;
     b.Height := 1000;
     SetRect(r, 0, 0, 1000, 1000);
     sl := TStringList.Create();
     try
       EnumEnhMetaFile(b.Canvas.Handle, mf.Handle, @EnhMetaFileProc, sl, r);
       ShowMessage(sl.Text);
     finally
       sl.Free();
     end;
   finally
     b.Free();
   end;
 finally
   mf.Free();
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 EnumControlFonts(StringGrid1);
end;


 
{RASkov} ©   (2008-09-21 19:43) [17]

> [16] guav ©   (21.09.08 18:56)

Мне кажется, что это немножко усложненный пример получения ширины текста...
Ведь "ядро" данного метода это всего лишь вот эта строка: ex := c.TextExtent(s);
ИМХО конечно...


 
guav ©   (2008-09-21 19:47) [18]

> [17] {RASkov} ©   (21.09.08 19:43)

Это просто пример что делать если каждая ячейка своим шрифтом


 
{RASkov} ©   (2008-09-21 20:09) [19]

Вот еще пример(на основе [15]):
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls, ExtCtrls;

const SET_WIDTH = WM_USER + 401;

type
 TForm1 = class(TForm)
   Panel1: TPanel;
   Edit1: TEdit;
   Button1: TButton;
   procedure Edit1Change(Sender: TObject);
   procedure Button1Click(Sender: TObject);
 private { Private declarations }
   procedure SetWidth(var Mes: TMessage); message SET_WIDTH;
 public  { Public declarations }
 end;

var Form1: TForm1;

implementation
{$R *.dfm}

{ TForm1 }
type TMyCtrl = class(TControl);
procedure TForm1.SetWidth(var Mes: TMessage);
var {Rc: TRect;} WX: Integer;
begin
if Mes.LParam<0 then Mes.LParam:=0;
with TControlCanvas.Create do try
 Control:=TControl(Mes.WParam);
 Font.Assign(TMyCtrl(Control).Font);
 //DrawText(Handle, PChar(Edit1.Text+CHR(Key)), -1, Rc, DT_CALCRECT);
 //if Rc.Right-Rc.Left<Mes.LParam then Edit1.Width:=Mes.LParam
  //else Edit1.Width:=Rc.Right-Rc.Left+GetSystemMetrics(SM_CXEDGE)*2+2;
 WX:=TextExtent(TMyCtrl(Control).Text).cx;
 if WX<Mes.LParam then Control.ClientWidth:=Mes.LParam
  else Control.ClientWidth:=WX+GetSystemMetrics(SM_CXEDGE)*2+2;
finally
 Free;
end;
end;

procedure TForm1.Edit1Change(Sender: TObject);
begin
 PostMessage(Handle, SET_WIDTH, Integer(Sender), -1);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 Panel1.Caption:=Edit1.Text;
 PostMessage(Handle, SET_WIDTH, Integer(Panel1), 50);
 PostMessage(Handle, SET_WIDTH, Integer(Sender), 100);
end;

end.


 
{RASkov} ©   (2008-09-21 20:14) [20]

> [18] guav ©   (21.09.08 19:47)
> если каждая ячейка своим шрифтом

Хм... точно) Я что-то не подумал об этом...
Я пример[16] попробывал с одним шрифтом, поэтому и....
Хех... эти метафайлы, а ведь интересная реализация... т.е. PaintTo в метафайл запишет всю информацию и шрифт включительно... Нужно будет "поюзать" примерчик с метафайлами :)


 
Бурундук   (2008-09-22 13:21) [21]

Можно попробовать соединить мой метод с методом guav"a

procedure TForm1.Button1Click(Sender: TObject);
var sz: TSize;
  mf: TMetafile;
  i, j: Integer;
  oldHeight, testHeight: Integer;
  r: TRect;
begin
 oldHeight := SGrid.Height;

 testHeight := 0;
 for i := 0 to SGrid.RowCount-1 do
   testHeight := testHeight + SGrid.RowHeights[j] + 1;

 // Грид должен нарисоваться в метафайл полностью, без скроллинга.
 // Тогда для всех ячеек будет вызван OnDrawCell
 // а в нём - вычислен размер текста

 SGrid.Height := testHeight;
 try
   mf := DrawToMf(SGrid);
   mf.Free;

   for i := 0 to SGrid.ColCount-1 do
     SGrid.ColWidths[i] := TextSizeInfo(SGrid).GetMaxTextWidth(i) + 2;
 finally
   SGrid.Height := oldHeight;
 end;
end;


 
MsGuns ©   (2008-09-22 15:39) [22]

С простыми контролами все работает превосходно, с сеткой тоже вроде нормально, только вот тормозит :(
Большое спасибо всем !


 
{RASkov} ©   (2008-09-22 15:46) [23]

> [22] MsGuns ©   (22.09.08 15:39)
> с сеткой тоже вроде нормально, только вот тормозит

Неужели ничего нельзя придумать?)


 
MsGuns ©   (2008-09-22 16:06) [24]

Блин, Саша, если б у меня только эта проблема была :(


 
{RASkov} ©   (2008-09-22 16:23) [25]

Ну если что, чем смогу - помогу


 
имя   (2008-12-08 23:15) [26]

Удалено модератором



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2009.12.20;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.005 c
2-1256962231
xucc
2009-10-31 07:10
2009.12.20
Помогите найти компонент NxCustomGridControl.pas


2-1256945135
_
2009-10-31 02:25
2009.12.20
Очистка буфера клавиатуры.


15-1256014283
Дмитрий С
2009-10-20 08:51
2009.12.20
посоветуйте очень простой datetime picker на javascript


2-1257373411
Drowsy
2009-11-05 01:23
2009.12.20
Триггеры в Interbase.


15-1254758929
turbouser
2009-10-05 20:08
2009.12.20
Что-то с аськой опять...





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский