Is there any way to make TMemo display text longer than 1024 into 1 line?
Take a look this simple code:
procedure TForm1.Button2Click(Sender: TObject);
var
s: string;
i: integer;
begin
s := '';
for i := 0 to 10000 do
s := s + 'a';
Memo1.Clear;
Memo1.Lines.Add(s);
end;
The long text "s" will be displayed in multiple lines. The Memo1 will automatically wrap the text after 1024 characters.
A TMemo is a wrapper for a native multi line edit control and is subject to the limitations it has. From INFO: Size Limits for a Multiline Edit Control:
A multiline edit control is also subject to the following limitations:
The maximum number of characters in a single line is 1024.
The maximum width of a line is 30,000 pixels.
The maximum number of lines is approximately 16,350.
Related
I'm printing using TPrinter.Canvas and am trying to align the text to the right due to money displaying numbers.
The text is printed with this code:
Printer.Canvas.Textout(800, 100, 250.00); //charge1
Printer.Canvas.Textout(800, 200, 10.00); //charge2
Printer.Canvas.Textout(800, 300, 260.00); //Total Amount
How can I align the lines to display correctly?
Instead of using VCL's TextOut, you should use the Winapi DrawText and choose to align to the right using that within a given rectangle, specifically using DT_RIGHT. For example...
DrawText(Canvas.Handle, PChar(T), Length(T), R, DT_SINGLELINE or DT_RIGHT);
You could use the lower level device context Api's to more accurately control the text output, such as DrawText() as suggested by Jerry. However, doing this will require that you pre-calculate/set the bounding rectangle for your text output so that Windows knows the area which any alignment is relative to.
There are two potentially simpler alternatives:
Option 1 - For Fixed Width Fonts ONLY
This may be appropriate for you since you appear to be using a fixed-width font.
All we do here is introduce padding to your strings prior to output. This may be simpler than the calculations required for the output rectangles etc.
To do this, decide on the longest string you will accept for output and then add whatever spaces you need to the left of each string to ensure they are all the same length. With a fixed width font, a space takes up the same horizontal space as any other character so by making all strings the same length you will achieve "right alignment":
procedure PrintRightJustified(const aX, aY, aMaxLen: Integer; aValue: String);
var
s: String;
begin
s := aValue;
// Pad the string with spaces to the maximum length
if Length(s) < aMaxLen then
s := StringOfChar(' ', aMaxLen - Length(s)) + s;
Printer.Canvas.Textout(aX, aY, s);
end;
Then call this method to output your strings right-justified to a consistent maximum length, e.g. if 6 is your maximum length (999.99):
PrintRightJustified(800, 100, 6, '250.00'); //charge1
PrintRightJustified(800, 200, 6, '10.00'); //charge2
PrintRightJustified(800, 300, 6, '260.00'); //Total Amount
How to handle scenarios where the specified string is longer than the maximum width is left as an exercise.
Option 2 - For Any Font
Another way, which would work for fixed width or variable width fonts (e.g. Arial) would be to choose the position of the rightmost edge of the strings and calculate the X position relative to this based on the device width calculated for each string you are outputting:
procedure PrintRightJustified(const aRightX, aY; aValue: String);
var
leftX: Integer;
begin
leftX := aRightX - Printer.Canvas.TextWidth(aValue);
Printer.Canvas.Textout(leftX, aY, aValue);
end;
You don't need to specify a maximum width for this version, it works regardless of font being used:
PrintRightJustified(900, 100, '250.00'); //charge1
PrintRightJustified(900, 200, '10.00'); //charge2
PrintRightJustified(900, 300, '260.00'); //Total Amount
This does mean that text may potentially "bleed" too far to the left without some mechanism to detect and handle this. Again, If that is a concern then either move the right-edge further across or you will have to come up with a way of handling it (left as an exercise for you since we don't know what your precise requirements are).
To the precedent answer!
With the above given code (Answer 1) I couldn'get my number aligned with one space.
After some thinking, I check on Word with my font, Times New Roman, how many space I need to get the number aligned, (the decimal dot have to be vertically aligned).
I noticed that two spaces were necessary not only one.
I juste wrote the command two times:
s:= StringOfChar(' ', aMaxLen - Length(s)) + s;
s:= StringOfChar(' ', aMaxLen - Length(s)) + s;
Man could do
s:= StringOfChar(' ', aMaxLen - Length(s))+ StringOfChar(' ', aMaxLen -
Length(s)) + s;
Excuse my english but it is not my mother language.
I hope it is a bit more understandable.
To Option 1 - For Fixed Width Fonts ONLY
First on word you check how many space you need to align your number. For Times I did need 2 spaces so you just double the procedure line:
s := StringOfChar(' ', aMaxLen - Length(s)) + s;
s := StringOfChar(' ', aMaxLen - Length(s)) + s;
I want to set the caret position in my derived TInplaceEdit in a derived DBGrid, similiar to a TDBGrid for right aligned columns.
procedure TMyGridEditor.CreateParams(var Params: TCreateParams);
const
Alignments : array[TAlignment] of LongWord= (ES_Left, ES_Right, ES_Center);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or Alignments[FAlignment];
end;
procedure TMyGridEditor.UpdateContents;
begin
inherited;
// if Assigned(Grid) and Assigned(Grid.SelectedColumn) then begin
// Alignment := Grid.SelectedColumn.Alignment;
if (TDBGrid(Grid).SelectedIndex <> -1) then
Alignment := TDBGrid(Grid).Columns[TDBGrid(Grid).SelectedIndex].Alignment;
end;
Additional description:
Grid.SelectedColumn = Currently selected column of the grid
Sample picure: Column too small for value
Problem1: The caret position is always on the right and positioned after the last character, this looks strange for ie. string and memo contents. How can I set the caret to the left, just after the inplaceeditor will be displayed.
Problem2: The contents for number values are displayed on multiple lines, when there is not sufficient space avaiable (column is too small). This looks very strange.
How can I accomplish these tasks? Any help appreciated... Thanks
I want to load some text in a TStringGrid. The strings are short except for a column where the string is over 100K. It seems that TStringGrid cannot handle this. The text does not appear in the cell until I double click the cell to edit it. But even then the behavior is erratic.
To reproduce: put a grid on the form, set goEdit= true. Run the application and double click a cell. Paste some text (should not contain enters) and press Enter to end the editing. The text disappear.
In the text I made, the limit is about 6208 ASCII chars.
Any quick fix/workaround for this?
The text is painted with ExtTextOut. That is known to fail for very long strings. For instance: ExtTextOut fails with very long strings unless lower font quality specified. From what I can tell, it is tricky to work out exactly what length of string causes failure.
I suggest that if you need to support such long strings then you draw them yourself by implementing an OnDrawCell event handler. Don't draw the entire string, because after all the user won't be able to see anything outside the cell's rectangle. That way you will be able to avoid the problem of sending ExtTextOut a string that is too long for it to handle.
you need to use Word break. of course without Word break nothing will be displayed. and of couse your text must contain spaces.
const
N = 16000;
var
R: TRect;
s: String;
i: Integer;
begin
R := ClientRect;
SetLength(s, N);
for i := 1 to N do
if Random(10) = 0 then
s[i] := ' '
else
s[i] := Char(65 + Random(26));
Canvas.Font.Color := clBlack;
Canvas.TextRect(R, s, [tfCenter, tfVerticalCenter, tfWordBreak]);
end;
I have an input edit field where the user can enter data. I want the box width to be at least 191px (min) and maximum 450px (max).
procedure THauptform.edtEingabeChange(Sender: TObject);
begin
// Scale
if Length(edtEingabe.Text) > 8 then
begin
if Hauptform.Width <= 450 then
begin
verschiebung := verschiebung + 9;
// The initial values like 'oldedtEingabeWidth' are global vars.
edtEingabe.Width := oldedtEingabeWidth + verschiebung;
buDo.Left := oldbuDoLeft + verschiebung;
Hauptform.Width := oldHauptformWidth + verschiebung;
end;
end;
end;
This works for ENTERING text. But when I delete one char, it does not scale back accordingly.
In your code, nothing will happen when your text is less than 8 characters long.
Also, I don't see any condition under which your width becomes smaller. It only becomes larger (by 9) with each iteration.
By the way, you appear to be multiplying by 9 as an average character width. You can use Canvas.TextWidth to determine the actual width required by the text without estimating.
If you want to use "9" anyway, you should name it as a constant to make clear what it is.
Quick and dirty using TextWidth:
const
MAX_EINGABE_WIDTH = 450;
MIN_EINGABE_WIDTH = 191;
procedure THauptform.edtEingabeChange(Sender: TObject);
var Width: Integer;
begin
// Scale
Width := edtEingabe.Canvas.TextWidth(edtEingabe.Text);
if Width > MAX_EINGABE_WIDTH then
Width := MAX_EINGABE_WIDTH
else if Width < MIN_EINGABE_WIDTH then
Width := MIN_EINGABE_WIDTH
edtEingabe.Width := Width;
end;
You're just adding 9 everytime the text changes and the length is grater than 8 - regardless of the change. You need to make it a function based on the length instead.
Something like this would do the trick:
procedure THauptform.edtEingabeChange(Sender: TObject);
var
len: integer;
additionalWidth: integer;
begin
len := Length(edtEingabe.Text);
if len <=8 then
additionalWidth:=0
else
additionalWidth:=(len-8)*9; //Assuming we need an extra 9 pixels per character after the 8th one
if additionalWidth > 259 then additionalWidth := 259; // maximum - minimum
edtEingabe.Width := 191 + additionalWidth;
Width := OriginalFormWidth + additionalWidth; // You'll need to know what the minimum width of your form is
end;
This isn't really a very pretty solution, though - changing all of those properties in the same way is ugly. Instead, since it appears you're also resizing the form, you can change the Anchors property of your edit box to make it maintain its margin to the right side as well, and only resize your form.
However, you probably want to consider if this is really a good idea. Why not let the input field just have a single size? In general, it looks better if windows don't resize on their own.
Do something like this:
procedure THauptform.edtEingabeChange(Sender: TObject);
var
Edit:TEdit;
begin
Edit := TEdit(Sender);
Edit.Width := Canvas.TextWidth(Edit.Text+' |')+
Edit.Padding.Left+
Edit.Padding.Right;
end;
Note 1: Don't manually try to limit the size. Instead, set Constraints.MinWidth and Constraints.MaxWidth via the property editor. That leaves your code clean and useless GUI stuff like this in the .dfm.
Note 2: TEdit doesn't have any public canvas property that you can use to get the text width.
Note 3: I don't like this kind of interface with growing and shrinking inputs, but it's probably just a matter of personal taste.
Here is my problem, I want to know actual length of the text in pixels (note that various letters have different length in some fonts). I am going to use this for better column width adjustment in DBGrid.
You can use the Canvas.TextWidth and Canvas.TextHeight functions.
Option 1, using the canvas of the control
WidthInPixels := Label1.Canvas.TextWidth('My Text');
Option 2, creating a temporary canvas (using a Tbitmap)
Function GetWidthText(const Text:String; Font:TFont) : Integer;
var
LBmp: TBitmap;
begin
LBmp := TBitmap.Create;
try
LBmp.Canvas.Font := Font;
Result := LBmp.Canvas.TextWidth(Text);
finally
LBmp.Free;
end;
end;
if you have a Delphi component has a "Canvas" property, then you can use Component.Canvas.TextWidth. For example: to get the width of the text of DBGrid you can use:
DBGrid1.Canvas.TextWidth('Stack');
Here you can find complete reference about this issue:
Length of Delphi string in pixels