I have taken the advice, or at least I think I have, that I got in my previous question.
To summarize, I replaced the THTMLTreeList from TMS with a TTreeList and made two columns in it. I set the first column to 150 pixels and I overrode the AdvancedCustomDrawItem event with this code:
procedure TForm1.trXMLAdvancedCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
var PaintImages, DefaultDraw: Boolean);
var hContext: HDC;
s: PChar;
iLength: Integer;
uRect: TRect;
begin
DefaultDraw := False;
hContext := trXML.Canvas.Handle;
s := PChar(Node.Text);
iLength := Length(Node.Text);
uRect := Node.DisplayRect(True);
DrawText(hContext, s, iLength, uRect, DT_END_ELLIPSIS);
end;
I got the result I expected, that is, when I draw the tree, the text in the first column is clipped. And when I change the size of the first column, the text is appropriately clipped. But when the TreeList is not wide enough and has a scroll bar on the bottom and I scroll to the right, the text now extends into the second column by the same amount as the scroll bar has been moved. It's like the text is drawn relative to the client area of the TreeList rather than the partially hidden first column so it always extends 150 pixels into the TreeList.
It seems to me that I am using the wrong thing for the device context handle or the TRect but I do very little graphic type programming so I don't know what to change. Any help would be appreciated.
I just checked TMS's source code and
TTreeList already uses the DT_END_ELLIPSIS flag
and does per-column clipping of drawn text when the DefaultDraw parameter is set to True, so you don't need to draw the text yourself manually.
Related
How do I ensure a balloon hint that I want to associate with a listview item is properly positioned so that it is next to the item in question, and always shows the full ballon text on screen?
For example, if I enter an invalid character when editing a file name in Windows Explorer, a balloon pops up saying what the invalid characters are. The entire balloon is always on screen even if the list item is near a screen edge or partially off screen. The tail is always positioned at the middle bottom of the list item. The bubble is usually to the bottom right of the tail, but may be above it or to the left if the list item is near the bottom and/or right edges of the screen.
Primarily, I am unable to get the bubble and tail to stay close to the list item.
procedure TForm1.ListEdited(Sender: TObject; Item: TListItem;
var S: string);
var
AHint: string;
R: TRect;
B : TBalloonHint;
begin
if TRegEx.IsMatch(S, '[\\/:*?"<>|]') then
begin
AHint := 'A file name cannot contain any of the following' + sLineBreak +
'characters: \/:*?"<>|';
R := Item.DisplayRect(drBounds);
R.TopLeft := ClientToScreen(R.TopLeft);
R.BottomRight := ClientToScreen(R.BottomRight);
B := TBalloonHint.Create(Self);
B.Description := AHint;
B.HideAfter := 5000;
B.ShowHint(R);
S := TRegEx.Replace(S, '[\\/:*?"<>|]', '');
end;
end;
I've tried the various overloads of ShowHint, as well as the JEDI balloon hint component. I've also adjusted the Top property of the rectangle, which may position the balloon better when the item is in a certain area of the screen, but the ballon is then off position when the item is on some other part of the screen.
Delphi 10.3 Rio, Win 7 x64.
DisplayRect gives client coordinates relative to the listview containing the item, not the form. Hence when converting to screen coordinates, you have to use the listview as the base, not the form:
R := Item.DisplayRect(drBounds);
R.TopLeft := ListView1.ClientToScreen(R.TopLeft); // <--
R.BottomRight := ListView1.ClientToScreen(R.BottomRight); // <--
I am following examples to put a checkbox in a delphi column cell. I can place a checkbox image in a cell. But the underlying boolean value is longer than the image so the checkmark is over the first two letters of "True" or "False". How can I clear the rectangle in the column cell? I tried printing the original "True" or "False" in no color, or white on white, but the text is always Black no matter what choice I make.
Sample of my code:
procedure TFUpdtLists.TreatGridDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State:
TGridDrawState);
begin
with Column do begin
// TreatGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State); ???
if FieldName = 'Repro' then begin
Canvas.Brush.Color:= TreatGrid.Color;
Canvas.FillRect(Rect);//fails to clear cell, clears another area??
if Field.Value = True then
//#7 a checkmark and # 5 a blank image in the image list:
MainView.ImageList1.Draw(TreatGrid.Canvas,Rect.Left,Rect.Top,7)
else
MainView.ImageList1.Draw(TreatGrid.Canvas,Rect.Left,Rect.Top,5);
TreatGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State);//??
end;
end;
end;
Thanks in advance!
Pay attention to the Canvas you refer to in the code when you paint the background. Since there is no argument called Canvas and also Column doesn't have a Canvas it refers to the Canvas of the TFUpdtLists executing the code.
You have already corrected the code for the mageList1.Draw() methods and added the grids reference (TreatGrid). Do the same for the bg painting.
It appears you have tried calling DefaultDrawColumnCell() in different ways, but you should call it only for the columns that you don't draw yourself.
Let me also suggest that you quit using with ... because it prevents code completion from working and it makes debugging harder as well as increases significantly the risc of coding errors.
With the above mentioned changes the OnDrawColumnCell() procedure looks like this;
procedure TForm19.TreatGridDrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if Column.FieldName = 'Repro' then
begin
TreatGrid.Canvas.Brush.Color:= TreatGrid.Color;
TreatGrid.Canvas.FillRect(Rect);
if Column.Field.Value then
//#7 a checkmark and # 5 a blank image in the image list:
Form19.ImageList1.Draw(TreatGrid.Canvas,Rect.Left,Rect.Top,1)
else
Form19.ImageList1.Draw(TreatGrid.Canvas,Rect.Left,Rect.Top,0);
end
else
TreatGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
You will want to correct the image indexes, which I changed to suit my imagelist in my test.
I have a TDBRadioGroup that I've added to my form.
I'd really like to have the caption to the left of it instead of on top (the form's a little busy and tall, and I'm trying to squeeze it in).
I can add my own label to the left of the Radio Group. But the control insists on reserving space of a Caption that does not exists. Is there a way I can turn it off completely?
The best we've come up with so far is sticking it on a TPanel and then hiding the top couple lines off-panel.
A TGroupBox (and it's descendant TDBGroupBox) are basically wrappers around the Windows GroupBox. The control is designed to sport a user-defined label across the upper-left corner, and doesn't have any style setting to remove it.
So, short of creating your own control to host a series of TRadioButton controls yourself and display them, there's no built-in way to disable the space reserved for the caption. You can suppress the text, of course, by setting the Caption := '', but the padding for the text descenders is not removed simply because the caption isn't displayed.
You can override the paint procedure for TRadioGroup so that the frame is drawn closer to the top of your item list. You could create a new component of type TNoCaptionRadioGroup. You might still have to use the panel trick that you have tried, but by lowering the top of the frame you can grab the space consumed by the non-existent caption. Something like this:
tNoCaptionRadioBox = class(TRadioGroup)
protected
procedure paint; override;
end;
procedure tNoCaptionRadioBox.paint;
var
H: Integer;
R: TRect;
begin
with Canvas do
begin
Font := Self.Font;
H := TextHeight('0');
R := Rect(0, H, Width, Height);
if Ctl3D then
begin
Inc(R.Left);
Inc(R.Top);
Brush.Color := clBtnHighlight;
FrameRect(R);
OffsetRect(R, -1, -1);
Brush.Color := clBtnShadow;
end else
Brush.Color := clWindowFrame;
FrameRect(R);
end;
end;
This is taken from the code for painting a TCustomGroupBox. I have removed the code for drawing the caption and have changed the top of the frame to the full height of the Font. Your actual captioned radio buttons will still be drawn where Windows wants them to be and with the default spacing.
Remember to register the new component by running the package installation tool.
procedure Register;
begin
RegisterComponents('myComponents', [tNoCaptionRadioBox]);
end;
I want to extend DbGrid functionality to add colors on odd and even rows. So i wrote this
procedure TGridx.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
row : Integer;
begin
inherited;
row := Self.DataSource.DataSet.RecNo;
if (row mod 2 = 0) then
Self.Canvas.Brush.Color := FColor1 //some color
else
Self.Canvas.Brush.Color := FColor2; //some color
end;
What i am doing wrong ?
The event you want is called DBGridDrawColumnCell, and you need to decide whether to turn the DefaultDrawing property on or off, and the way you handle DBGridDrawColumnCell changes accordingly. For your case, you just set the colors, but leave DefaultDrawing true, and don't do any other canvas.Text or GDI drawing.
A recent question I asked here showed that in later Delphi versions (2010,Xe,Xe2) you ALSO sometimes need to call Canvas.Refresh for both TDBGRID and TListView, when changing canvas properties in ownerdraw events but that doesn't apply to delphi 7.
you should try also 3d party solution which are free, and extends already a lot the DBGrid, like the ones provided by the Jedi project
Opc0de, may be you should override not the "DrawCell" method but "DrawCellBackground"?
Try drawing the cell as well after the brush color is defined:
Self.Canvas.FillRect(ARect);
I draw a list view with OwnerDraw. I need to paint the first column. But I cannot understand how.
I tried:
procedure TFrame6.DownloadListCustomDraw(Sender: TCustomListView;
const ARect: TRect; var DefaultDraw: Boolean);
var
R: TRect;
begin
DefaultDraw := False;
Sender.Canvas.Brush.Color := $F7F7F7;
Sender.Canvas.Brush.Style := bsSolid;
R := ARect;
R.Right := ListView_GetColumnWidth(DownloadList.Handle, DownloadList.Columns[0].Index);
Sender.Canvas.FillRect(R);
DefaultDraw := True;
end;
But I draw over items. How to draw correctly, items and a background?
Thanks!
Summary from comments:
I suggest you to read this delphiDabbler article and hope that it contains enough information to resolve your problem. E.g. Example 1 shows how to change background and Example 4 shows point where item appearance can be changed.
Small tip: don't restore DefaultDraw to True at the end of the handler if you don't want text to be drawn.
I suggest you use VirtualStringTree if you want a lot of customization on the list. Its easy to use and almost anything is possible and most of all freeware. The component can be downloaded at Soft-Gems and few example can be found here