TStringGrid cannot display very long (6K) strings - delphi

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;

Related

Print fields alignment misaligned on report

I have the following PrintValue code that prints a line to the report (tbasedxreportlink). It prints two fields on one line in the header, the caption and m. The problem is that m is never aligned straight for multiple lines. It always prints all over the place.
How do I get it to align to the right or even print decimal aligned.
Printed Data
Caption One 4,685.33
Caption 2 4.99
Caption three 74,586.88
Caption 4 58.66
Code
procedure PrintValue(Caption, Value: string);
var
m: string;
s: string;
begin
m := FormatFloat(',0.00 ;(,0.00);0.00 ', StrToFloat(Value));
s := Format('%-24s %15s', [Caption, m]);
AReportLink.PrinterPage.PageHeader.LeftTitle.Add(s);
end;
The font used on the report is Segoe UI if it matters.
Thanks
The simplest way is using monospace (fixed-width) font, for example, Courier New or Lucida Console
I found no easy way to format the strings to get the desired effect. The main reason for that is the simplicity of using the LeftTitle, CenterTitle or RightTitle 'boxes' - they only allow simple string text to be inserted. Nothing fancy allowed not to mention the True Type Font issue.
In order to solve the problem I added a tPanel to the screen and dropped the all screen fields I needed to show up on the grid print to it. I added a tdxCustomContainerReportLink to link to that panel. I then used a tdxCompositionReportLink to print both the grid and the tdxCustomContainerReportLink (panel) as individual items when the print button was pressed by overwriting the current link code:
procedure TFrmViewAcct.dxBarBtnPrintClick(Sender: TObject);
begin
dxCmpntPrtrDetail.CurrentLink := dxCmpntPrtrDetailLink2;
inherited;
end;
Thus it prints the grid info then prints what ever is on the panel. Problem solved and you can see how this solution can be flexible.
Yes I could have easily changed to a True Type font but that is an ugly workaround as far as I am concerned especially where standardized fonts need to be observed.

Is it possible to make a TListView search by the caption of a different column in Delphi?

When you set the Caption of a TListItem it seems to always set the Text for the first column in the row. When you start typing in the ListView it will search & select the closest match based on the caption of the first column.
I have a situation where I need the caption of the first row to be empty, but still need the search functionality to work as normal (in this case the data I would be searching for may be in the 2nd/3rd column).
Is this possible without using any 3rd party controls?
Depending on why you want the caption/ first column to be blank, you could move the text you want to search for into the caption, and then have a blank sub-item. Then swap the column order in code like so
//Move the 1st sub-item left one column
ListView1.Columns[1].Index := 0;
This would look almost the same, with the exception that if you don't have RowSelect set to true the highlighted caption will be in the wrong column. This would allow you to search as required and use the FindCaption method in code.
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
li : TListItem;
begin
//Add data to the list view for demo
for I := 0 to 10 do
begin
li := ListView1.Items.Add;
li.Caption := intToStr(Random(10000));
li.SubItems.Add('');
li.SubItems.Add('Col2');
//addimages so you can see which column is which
li.SubItemImages[0] := 0;
li.ImageIndex := -1;
end;
//move column 2 left one, this is the important bit
ListView1.Columns[1].Index := 0;
end;
alt text http://img265.imageshack.us/img265/3489/captureqg.jpg
If it's bound to a dataset, then you can do your own search and then move the dataset cursor to the row that you want. Just off the top of my head, because I just did one of those.
Update: Use the OnCompare handler, and do your own comparision on whatever criteria you want. i.e. you get to decide whether item1 < item2 or not.
Here's a nice writeup:
http://www.latiumsoftware.com/en/delphi/00011.php

Delphi: Autoscale TEdit based on text length does not work when removing chars

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.

Width in pixels of a text/caption in Delphi 7

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

How do I make a memo column on a DX grid show partial words?

I've got a TdxDBGrid that's displaying some information retrieved from a database query. One of the columns is a Memo column, (TdxDbGridMemoColumn,) which is necessary because the data in the field it's bound to comes out of the database as type TEXT, not CHAR or VARCHAR.
Problem is, the memo column likes to display whole words, and if it can't display a whole word, it doesn't display any part of it. The normal grid columns show everything they can up to the right border and cut off the display there, but the memo column doesn't, and that's bound to confuse end-users. Is there any way I can get the memo column to display partial words?
You could owner-draw the column. Then you can make the text look however you want. Call DrawText and use the dt_End_Ellipsis flag to draw an ellipsis on the end of long text, or else just let the long text be clipped to the drawing area.
in the onGetText event of the column, you can modify the displayed text to accommodate the available size:
// the TTextFormats flags are defined in Graphics, add it to your uses clause
procedure TMyForm.gridMyColGetText(Sender: TObject; ANode: TdxTreeListNode;
var AText: string);
var
R: TRect;
begin
// Calculate actual displayable text (with ellipsis) depending on cell size
R := (Sender as TdxDBGridColumn).TreeList.CellRect(ANode, (Sender as TdxDBGridColumn).ColIndex); // get the cell rectangle
Windows.InflateRect(R, -2, 0); // shrink a bit for grid lines
grid.Canvas.TextRect(R, AText, [tfModifyString, tfEndEllipsis]); // shorten the text ...
end;

Resources