![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как узнать размер текста в произвольном контроле [D7]
MsGuns © (2008-09-20 19:53:00)Это может быть, например, ячейка сетки и шрифт может быть разным в разных ячейках (код в соотв.обработчике).
Узнать надо не внутри OnDrawxxx, а "извне", скажем по кнопке.
Спасибо за ответы
(2008-12-08 23:15:00) [1]Удалено модератором
{RASkov} (2008-09-22 16:23:00) [2]Ну если что, чем смогу - помогу
MsGuns (2008-09-22 16:06:00) [3]Блин, Саша, если б у меня только эта проблема была :(
{RASkov} (2008-09-22 15:46:00) [4]> [22] MsGuns © (22.09.08 15:39)
> с сеткой тоже вроде нормально, только вот тормозит
Неужели ничего нельзя придумать?)
MsGuns (2008-09-22 15:39:00) [5]С простыми контролами все работает превосходно, с сеткой тоже вроде нормально, только вот тормозит :(
Большое спасибо всем !
Бурундук (2008-09-22 13:21:00) [6]Можно попробовать соединить мой метод с методом 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;
{RASkov} (2008-09-21 20:14:00) [7]> [18] guav © (21.09.08 19:47)
> если каждая ячейка своим шрифтом
Хм... точно) Я что-то не подумал об этом...
Я пример[16] попробывал с одним шрифтом, поэтому и....
Хех... эти метафайлы, а ведь интересная реализация... т.е. PaintTo в метафайл запишет всю информацию и шрифт включительно... Нужно будет "поюзать" примерчик с метафайлами :)
{RASkov} (2008-09-21 20:09:00) [8]Вот еще пример(на основе [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.
guav (2008-09-21 19:47:00) [9]> [17] {RASkov} © (21.09.08 19:43)
Это просто пример что делать если каждая ячейка своим шрифтом
{RASkov} (2008-09-21 19:43:00) [10]> [16] guav © (21.09.08 18:56)
Мне кажется, что это немножко усложненный пример получения ширины текста...
Ведь "ядро" данного метода это всего лишь вот эта строка: ex := c.TextExtent(s);
ИМХО конечно...
guav (2008-09-21 18:56:00) [11]> [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 17:34:00) [12]> [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;
MsGuns (2008-09-21 17:05:00) [13]Саш, если не затруднит, изобрази код, который подгоняет ширину эдита точно под текст при каждой его правке с клавиатуры, пожалуйста ;)
У меня здесь делфа глючит (есть, но она глючит - комп домашний и давно нуждается в переустановке винды, да все ручки не доходят) - не могу попробовать
MsGuns (2008-09-21 17:01:00) [14]>Cnv:= TControlCanvas.Create;
Обана ! Это интересно - надо попробовать ;)
>GetTextExtentPoint32 ?
Таки да ;)
Спасибо, завтра поэкспериментирую. Способ, конечно, не учитывает "выпендресы" с перерисовкой "ручками", но мне это пока и не надо.
Но вот, блин, интересно, винда же при перерисовке "помнит" все нюансы рисования каждого фрагмента экрана. Как она, собабака, это делает ?
;))
{RASkov} (2008-09-21 16:51:00) [15]> procedure TForm1.Button3Click(Sender: TObject);
Там где эксперимент был с эдитами. В эдитах был одинаковый текст с одинаковым шрифтом, но в одном Style:=[] а в другом Style:=[fsItalic, fsBold];
{RASkov} (2008-09-21 16:48:00) [16]> [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 ?
MsGuns (2008-09-21 15:09:00) [17]>Дмитрий_С (21.09.08 12:24) [8]
>ты покажи кодом как ты делал
Код на работе. Но там нечего показывать, т.к. я полностью собезъянничал с VCL (TCanvas.TextWidth), а HDC "взял" с помощью ф-ции GetWindowDC(Edit1.Handle). Повторяю, оно все как бы работает, только дает в результате
какую-то фигню.
>Бурундук (21.09.08 12:46) [9]
Вы, вероятно, не совсем поняли что я хочу.
У меня не возникает никаких проблем в обработчиках перерисовки, но мне надо несколько иное. Поясню на примере
Есть датасет и грид, его отображающий. После открытия датасета я хочу вычислить ширину колонок грида так, чтобы они вмещали полностью тексты всех полей каждой колонки. Т.е. грид еще и не думал рисоваться или отрисовал первые надцать строк, а я уже хочу знать ширины всех его колонок.
Пример с гридом - частный случай, но ИМХО, достаточно избыточно рисует проблему.
Бурундук (2008-09-21 12:46:00) [18]А если размеры определять именно в 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.
Дмитрий_С (2008-09-21 12:24:00) [19]
> MsGuns © (21.09.08 02:08) [6]
ты покажи кодом как ты делал
Германн (2008-09-21 02:20:00) [20]
> MsGuns © (21.09.08 02:08) [6]
>
> >Германн © (21.09.08 01:32) [5]
> >А если я там в "клиентской части" захочу нарисовать картинку?
>
>
> Дело в том, что пользователь при всем старании не сможет
> рисовать картинки - он может лишь вводить текст, изменяя
> его ширину и/или высоту (при многострочности) - вот их-то
> мне и надо бы как-то определять.
> Конечно, при программном изменении физических параметров
> текстов (фонта, стиля и т.д.) определить что там реально
> нарисовано можно, вероятно только так, как описал Юра
Имхо. После того как пользователь ввёл текст в произвольное место контролла, у тебя для пост-анализа есть только картинка.