Delphi TRichEdit page setup - delphi

I have an RTF document with defined page settings:
(...}\paperw16840\paperh11907\margl794\margt709\margr794\margb983\viewkind4\\uc1\trowd\....)
In my app I use a TRichEdit to show the document.
The TRichEdit has a TPanel as its Parent, and is using Align=alClient and AlignWithMargins=True.
I set the Panel's Width to 16840 * PixelsPerInch/1440 (1123 pixels) and I see that is equal to the page's width, as shown in MSWord (scale=100%).
Setting the RichEdit's Margins to 794 * PixelsPerInch/1440 (53 pixels), the Width of the RichEdit is smaller than it must be, or the margins are bigger than they must be (compared with MSWord).
No borders, no other margins, except what I set in code:
function pixelsOf(prop : string) : integer;
var
i,j,l : integer;
begin
result := -1;
l := length(prop);
i := pos(prop,s);
if i > 0 then begin
j := i+l;
while s[j] in ['0'..'9'] do inc(j);
result := round(strToIntDef(copy(s,i+l,j-i-l),-1)*PixelsPerInch/1440);
end;
end;
paperW := pixelsOf('\paperw'); // pixelsOf() calcs twips*pixelsPerInch/1440
PanelPreview.Width := paperW;
Lm := pixelsOf('\margl');
RichEdit1.Margins.Left := Lm;
Rm := pixelsOf('\margr');
RichEdit1.Margins.Right := Rm;
Tm := pixelsOf('\margt');
RichEdit1.Margins.Top := Tm;
The value of paperW gives the correct Panel width (compared with MSWord), but the values of Lm and Rm give bigger margins, so the RichEdit becomes narrower.
How can I calculate the correct margins so the RichEdit has the same layout as MSWord?
This maybe helps. I noticed that :
TRichedit leaves a space about 10 pixels at the left side (the rendering is starting after this space). Is there a parameter that can be fix this other than margins.left ?
TRicheditdoesn't render any table wider than its width (MSword do
this adjusting the margins). So the TRichedit trancates everything
outside its margins.
The result of the above is that the left margin seems wider than must be and truncates the right side of the table if it is winder.
see the image

Related

Filling blank space in delphi stringgrid

I have a grid that can be resized. And i'm now stuggeling with filling the blank space around columns in the grid. I'm trying to achieve this on FormResize.
First i calculate what is the total of columns width and then i'm comparing it to the string grid width. if the stringgrid widths is bigger then i add to each columns width equal portions of the blank space left. This is how it looks in formResize Procedure:
procedure TBDDTool.FormResize(Sender: TObject);
var
totColWidth,i : integer;
begin
totColWidth := 0;
for i := 0 to sgFilePreview.ColCount - 1 do
totColWidth := totColWidth + sgFilePreview.ColWidths[i];
if sgFilePreview.Width > TotColWidth then
begin
for i := 0 to sgFilePreview.ColCount - 1 do
begin
sgFilePreview.ColWidths[i] := round(sgFilePreview.ColWidths[i] +
((sgFilePreview.Width - totColWidth)/(sgFilePreview.colCount)));
end;
end;
end;
This actualy doesn't work cause sgFilePReview.Width is the width of my grid. And i don't know how to get the width of the whole space inside the grid, like every columns + blank space left. How can i get the real width of the grid? Cause sgFilePreview.Width return the width of the grid but as seen from outside the grid.
Thank you!
EDIT
Addine new columns
for val in sLineSplitted do
begin
if Pos('#',val) <> 0 then propVal := copy(val,0,pos('#',val)-1)
else propVal := val;
col := col +1;
if (row = 1) then
begin
if (col >1) then
//Add column
sgFilePreview.ColCount := col;
sgFilePreview.Cols[col-1].Text := propVal;
SetLength(aSourceData[row-1],col);
aSourceData[row-1,col-1] := val;
end
else
begin
sgFilePreview.RowCount := row;
SetLength(aSourceData[row-1],col);
aSourceData[row-1, col-1] := val;
sgFilePreview.Cells[col-1, row-1] := propVal;
pnlFileManager.Visible := true;
end;
end;
Auto size columns to fit word if the world is bigger than the cell's width
procedure TBDDTool.AutoSizeGrid(Grid: TStringGrid);
const
ColWidthMin = 10;
var
C,R,W, ColWidthMax: integer;
begin
for c := 0 to Grid.ColCount - 1 do
begin
ColWidthMax := ColWidthMin;
for R := 0 to Grid.RowCount - 1 do
begin
W := Grid.Canvas.TextWidth(Grid.Cells[C,R]);
if W > ColWidthMax then
ColWidthMax :=W;
end;
Grid.ColWidths[C] := ColWidthMax +5;
end;
end;
The main problem why these empty spaces are occurring to you even when you have too many columns so that all of them can be seen at the same time is the fact that in StringGrid scrolling works a bit different than you are used to in other controls.
When you scroll around in StringGrid the scrolling position is always aligned to the position of TopLeft visible cell. So if the combined width of visible cols isn't the same as ClientWidth this means that you will either have partially visible col at the right side or and empty space when you have scrolled all the way to the right.
Now one possible way to avoid this is to resize the columns so that they always fit into the client width (no partially visible columns). But the problem is that this becomes practically impossible if you have different widths for each column.
In case if you can live with the fact that all columns will have same width you can use the code below which works in most cases. It isn't perfect because you can only set column width to integer values where sometimes you would need larger precision.
procedure TForm1.FormResize(Sender: TObject);
var cwDefaultWidth: Integer;
VisibleCols: Integer;
ColWidth: Integer;
begin
cwDefaultWidth := 64;
VisibleCols := StringGrid1.ClientWidth div cwDefaultWidth;
if VisibleCols >= StringGrid1.ColCount then
begin
ColWidth := Round(StringGrid1.ClientWidth / StringGrid1.ColCount-1);
end
else
begin
ColWidth := Round(StringGrid1.ClientWidth / VisibleCols-1);
end;
StringGrid1.DefaultColWidth := ColWidth;
end;
But if you are using variable column widths then the only thing that you could do is adjust the size of the last column so that it's width fills the empty space that would otherwise appear.
In order to do that you would first have to check to see if you are scrolled fully to the right. Then you would have to sum up the width of currently seen columns. You could do this by using:
for I := StringGrid1.LeftCol to StringGrid1.RowCount-1 do
begin
VisibleColsWidth := VisibleColsWidth + StringGrid1.ColWidths[I];
end;
Then you subtract this width from StringGrid1.ClientWidth and you get the width of empty space. So finally you increase the size of last column for the empty space width.
I really hope that even if my answer doesn't provide you with an actual solution it would at least guide you towards finding the right solution.

How to set the form position and font size in a KOL form

I have two questions about KOL.
I have a main form. As I see this placed on the TForm's position.
I wanna put it to the screen center.
How can I access it's coordinates, or the handle for "SetWindowPos"?
I have 4 labels in the form. For 2 of them I want to use smaller fonts. But I don't found any Font Size property... How to do it then?
1.1. How to center form on screen ?
Use the CenterOnForm method. As description says, if it's applied to a form, centers form on screen:
Form.CenterOnForm(nil);
1.2. How to get form position ?
Just like in Delphi by the Left and Top properties or e.g. by the Position property. The following pseudo-code results to the same:
ShowMessage('Form pos.: [' +
Int2Str(Form.Left) + '; ' +
Int2Str(Form.Top) + ']'
);
ShowMessage('Form pos: [' +
Int2Str(Form.Position.X) + '; ' +
Int2Str(Form.Position.Y) + ']'
);
Note, that unless you change the form position by yourself or until the applet is running, both properties returns 0. The form position, if you didn't set it, is adjusted when the applet starts.
1.3. How to get form handle ?
Use either the Handle property or the GetWindowHandle method. The following pseudo-code results to the same:
ShowMessage(Int2Str(Form.Handle));
ShowMessage(Int2Str(Form.GetWindowHandle));
2. How to change the font size ?
As #David already mentioned in his post, use the Font.FontHeight property. Just one sidenote, the default font when you create e.g. that label is set to System to which is not possible to change the size, so don't be surprised when the size changes won't be applied. Here's a quote from the source:
Value 0 (default) says to use system default value, negative values
are to represent font height in "points", positive - in pixels. In XCL
usually positive values (if not 0) are used to make appearance
independent from different local settings.
And here's a sample usage shown on positioned label creation:
Label1 := NewLabel(Form, 'Label1').SetPosition(8, 8);
Label1.Font.FontName := 'Tahoma';
Label1.Font.FontHeight := -11;
3. Example project
program Project1;
uses
KOL;
type
PForm1 = ^TForm1;
TForm1 = object(TObj)
Form, Label1, Label2, Label3, Label4: PControl;
end;
var
Form1: PForm1;
procedure CreateForm(var Result: PForm1; AParent: PControl);
begin
New(Result, Create);
with Result^ do
begin
Form := NewForm(AParent, 'Caption').SetSize(320, 240);
Form.CenterOnForm(nil);
Label1 := NewLabel(Form, 'Label1').SetPosition(8, 8);
Label1.Font.FontName := 'Tahoma';
Label1.Font.FontHeight := -11;
Label2 := NewLabel(Form, 'Label2').SetPosition(72, 8);
Label2.Font.FontName := 'Tahoma';
Label2.Font.FontHeight := -11;
Label3 := NewLabel(Form, 'Label3').SetPosition(136, 8);
Label3.Font.FontName := 'Tahoma';
Label3.Font.FontHeight := -15;
Label4 := NewLabel(Form, 'Label4').SetPosition(200, 8);
Label4.Font.FontName := 'Tahoma';
Label4.Font.FontHeight := -15;
end;
end;
begin
Applet := NewApplet('Test');
CreateForm(Form1, Applet);
Run(Applet);
end.
Question 1
Call the SetPosition and SetSize methods on the form.
Form.SetPosition(x,y);
Form.SetSize(w,h);
Question 2
Use Font.FontHeight.
MyControl.Font.FontHeight := ...;
If you want to call SetWindowPos, you can retrieve the handle calling the GetWindowHandle method of the returned pointer.
Please, don't ask more than 1 question per question.

How to get text extent of RichEdit in Delphi

Does anyone know how to get the width and height of text in a TRichEdit control, in the same way that you would use TextWidth and TextHeight on a TCanvas?
The reason I need to know this doing this is I have a RichEdit on a non-visible form that I copy the contents of to a canvas using Richedit.Perform(EM_FORMATRANGE, ...). The problem is that the EM_FORMATRANGE requires a parameter of type TFormatRange in which the target rect is specified, but I don't know what the rect should be because I don't know in advance the size of the contents in the RichEdit. Hope that makes sense.
Again use EM_FORMATRANGE for measuring, see EM_FORMATRANGE Message on MSDN:
wParam Specifies whether to render the
text. If this parameter is a nonzero
value, the text is rendered.
Otherwise, the text is just measured.
Generally you would already have a destination area, which has a width and height, where you'd do the drawing, like printing on a paper or producing a preview on a predefined surface. A most simple example for a predefined width to get the required height could be;
var
Range: TFormatRange;
Rect: TRect;
LogX, LogY, SaveMapMode: Integer;
begin
Range.hdc := ACanvas.Handle;
Range.hdcTarget := ACanvas.Handle;
LogX := GetDeviceCaps(Range.hdc, LOGPIXELSX);
LogY := GetDeviceCaps(Range.hdc, LOGPIXELSY);
Range.rc.Left := 0;
Range.rc.Right := RichEdit1.ClientWidth * 1440 div LogX; // Any predefined width
Range.rc.Top := 0;
Range.rc.Bottom := Screen.Height * 1440 div LogY; // Some big number
Range.rcPage := Range.rc;
Range.chrg.cpMin := 0;
Range.chrg.cpMax := -1;
RichEdit1.Perform(EM_FORMATRANGE, 0, Longint(#Range));
ShowMessage(IntToStr(Range.rc.Bottom * LogY div 1440)); // Get the height
RichEdit1.Perform(EM_FORMATRANGE, 0, 0); // free cache
For a more complete example see this article, or in general any RichEdit previewing/printing code...

Calculate needed size for a TLabel

Ok, here's the problem. I have a label component in a panel. The label is aligned as alClient and has wordwrap enabled. The text can vary from one line to several lines. I would like to re-size the height of the the panel (and the label) to fit all the text.
How do I get the necessary height of a label when I know the text and the width of the panel?
You can use the TCanvas.TextRect method, along with the tfCalcRect and tfWordBreak flags :
var
lRect : TRect;
lText : string;
begin
lRect.Left := 0;
lRect.Right := myWidth;
lRect.Top := 0;
lRect.Bottom := 0;
lText := myLabel.Caption;
myLabel.Canvas.Font := myLabel.Font;
myLabel.Canvas.TextRect(
{var} lRect, //will be modified to fit the text dimensions
{var} lText, //not modified, unless you use the "tfModifyingString" flag
[tfCalcRect, tfWordBreak] //flags to say "compute text dimensions with line breaks"
);
ASSERT( lRect.Top = 0 ); //this shouldn't have moved
myLabel.Height := lRect.Bottom;
end;
TCanvas.TextRect wraps a call to the DrawTextEx function from the Windows API.
The tfCalcRect and tfWordBreak flags are delphi wrappers for the values DT_CALCRECT and DT_WORDBREAK of the windows API. You can find detailed information about their effects in the DrawTextEx documentation on msdn
Use TextWidth and TextHeight.
See an example here:
http://www.greatis.com/delphicb/tips/lib/fonts-widthheight.html
TextWidth will tell you how wide the text would be, and then you can divide that by the control width to see how many rows you need. The remainder of the division should be an additional row.
You can use one line of code for this:
label.width := label.canvas.textwidth(label.caption);
or you can set the label's autosize property to true in the object inspector.
If you can align it alTop and keep AutoSize on then TLabel will auto adjust the height after settign the caption.
in FMX there is a trick to do that simply :
when creating a Label set Autosize := true and use the OnResize Event to update the size of the parent...
Rectangle1 := TRectangle.create(Form1);
Rectangle1.parent := Form1;
Label1 := TLabel.create(Rectangle1);
Label1.parent := Rectangle1;
Label1.Align := TAlignLayout.Top; // keep the same width and auto size parent height
Label1.OnResize := DoReSize;
Label1.WordWrap := true;
Lable1.Autosize := true;
The parent size will be updated here (assuming that the Sender object is the most bottom control in the parent, if not you need to arrange this function to summarize all the components size and verticaly)
procedure DoParentResize(Sender : TObject);
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Height + 4;
end;
if we use Label1.Align := TALignLayout.None;
then we should add the position inside the parent :
procedure DoParentResize(Sender : TObject);
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Position.Y + TControl(Sender).Height + 4;
end;
Wich result in a single function for (almost) all cases :
procedure TForm1.DoParentResize(Sender : TObject);
begin
if TControl(Sender).Align in [TAlignLayout.None, TAlignLayout.Client, TAlignLayout.Center, TAlignLayout.VertCenter ] then
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Position.Y + TControl(Sender).Height + 4;
end
else
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Height + 4;
end;
end;
You need to reduce the LRect.right by the label left and right margins, and then add the label top and bottom margins to the label height at the end or the text might not fit the label.
procedure TFrm.PatternEditTyping(Sender: TObject);
begin
(Sender as Tedit).Canvas.Font.Size := (Sender as Tedit).Font.Size;
(Sender as Tedit).Width := (Sender as Tedit).Canvas.TextWidth((Sender as Tedit).Text);
end;
This code adjusts Tedit.Width while you type inside it. Just keep the font family in Canvas and in Tedit the same.

Can I make a TMemo size itself to the text it contains?

When you edit a TLabel's caption in the form designer, it resizes the TLabel for you. Is there any way I can get a TMemo to do that, at runtime?
I'd like to be able to take a TMemo, assign something to its .lines.text property, and then tell it to resize itself and not exceed a certain width, though it can get as tall as it wants to. Anyone know how to do that?
This works just fine for me. The constant added (8) might vary on whether you are using a border and/or bevel, experiment with it.
procedure TForm1.Memo1Change(Sender: TObject);
var
LineHeight: Integer;
DC: HDC;
SaveFont : HFont;
Metrics : TTextMetric;
Increase: Integer;
LC: Integer;
begin
DC := GetDC(Memo1.Handle);
SaveFont := SelectObject(DC, Memo1.Font.Handle);
GetTextMetrics(DC, Metrics);
SelectObject(DC, SaveFont);
ReleaseDC(Memo1.Handle, DC);
LineHeight := Metrics.tmHeight;
Increase := Memo1.Height;
LC := Memo1.Lines.Count;
if LC < 1 then
LC := 1;
Memo1.Height := LC * LineHeight + 8;
Increase := Memo1.Height - Increase;
Memo1.Parent.Height := Memo1.Parent.Height + Increase;
end;
Set the WordWrap property of the TMemo to true, dump your text into it, count the lines, and set the height to the product of the line count and the line height, but you need to know the line height.
The TMemo does not expose a line height property, but if you're not changing the font or font size at runtime, you can determine the line height experimentally at design time.
Here's the code I used to set the height of the TMemo that had a line height of 13 pixels. I also found that I needed a small constant to account for the TMemo's top and bottom borders. I limited the height to 30 lines (396 pixels) to keep it on the form.
// Memo.WordWrap = True (at design time)
Memo.Text := <ANY AMOUNT OF TEXT>;
Memo.Height := Min(19 + Memo.Lines.Count * 13, 396);
If you absolutely must extract the line height from the object at runtime, then you might use Someone's answer. Or, you can use TRichEdit, which has the SelAttributes property containing a Height property giving the line height.
-Al.
I've implemented a self-growing TMemo as a nice example of LiveBindings (one of the few useful examples I could come up with for LiveBindings in VCL).
A quote From my Delphi XE2 Development Essentials courseware manual:
"To build this example, place a TMemo component on a VCL form, open the LiveBindings property, and select the “New LiveBinding” option. Pick the TBindExpression choice. Open BindExpressionMemo11 in the Object Inspector and set SourceComponent to Memo1 and SourceExpression to Lines.Count * 22.
To get a better result at runtime, set SourceExpression to the more exact expression
Font.Size - 4 + (Lines.Count + 1) * -1 * (Font.Height - 3)
Finally, in the OnChange event handler of the TMemo, write one line of code:
BindingsList1.Notify(Sender, '');
That’s it. Compile and run to see the growing memo in action.
[screenshot]
Initially, the TMemo control will be two lines high (the line with the contents, and a next line), and whenever we hit enter or word wrapping advances us to the next line, the TMemo control will grow in height (growing down actually, so make sure to leave enough space on the form for the TMemo to expand itself)."
Groetjes, Bob Swart
procedure TTmpMessage.edMsgChange (Sender: TObject);
var
LineHeight : Integer;
DC : HDC;
SaveFont : HFont;
Metrics : TTextMetric;
begin
DC := GetDC ( TRxRichEdit (Sender).Handle );
SaveFont := SelectObject ( DC, TRxRichEdit (Sender).Font.Handle );
GetTextMetrics (DC, Metrics);
SelectObject (DC, SaveFont);
ReleaseDC ( TRxRichEdit (Sender).Handle, DC );
LineHeight := Metrics.tmHeight;
Height := TRxRichEdit (Sender).Lines.Count * LineHeight + 32;
end;
And why not just:
Memo1.Height := Memo1.ContentBounds.Height + 5;

Resources