7

I want to shorten a filename to fit in a TEdit, something like

Edit1.Text := MinimizeName(FileName, Edit1.Canvas, Edit1.Width);

Unfortunately this doesn't compile because a TEdit does have a Canvas property directly. The canvas is needed for its font metrics. How can I access a TEdit's canvas?

(MinimizeName is declared in Vcl.FileCtrl.)

Joris Groosman
  • 771
  • 8
  • 23

3 Answers3

7

You could use TControlCanvas. You should also take the control's Font into account.

e.g.:

var
  Canvas: TControlCanvas;

Canvas := TControlCanvas.Create;
try
  Canvas.Control := Edit1;
  Canvas.Font.Assign(Edit1.Font); 

  // Do something with Canvas... 
finally
  Canvas.Free;
end;
kobik
  • 21,001
  • 4
  • 61
  • 121
  • Nice idea, BUT cannot be wrapped in a function that receives a TWinControl as parameter because the Font property is not exposed in TWinControl. So, we just trade a problem (canvas not accessible) for another problem (font not accessible). – Gabriel Jun 22 '21 at 11:56
6

OK, I found it. For those who are interested:

procedure TForm1.Button1Click(Sender: TObject);  
var  
  aCanvas: TCanvas;  
begin  
  if FileOpenDialog1.Execute then begin  
    aCanvas := TCanvas.Create;  
    try  
      aCanvas.Handle := GetDC(Edit1.Handle);  
      Edit1.Text := MinimizeName(FileOpenDialog1.FileName, aCanvas, Edit1.Width - 8);  
    finally  
      ReleaseDC(Edit1.Handle, aCanvas.Handle);
      aCanvas.Free;  
    end;  
  end;  
end;


Joris Groosman
  • 771
  • 8
  • 23
  • You also need to call `ReleaseDC` after you're finished using it. You should also consider creating this upon startup, and releasing it on shutdown. And more importantly, you should only paint when Windows tells you to, via the `WM_PAINT` message, or else Windows will just paint right back over what you painted. – Jerry Dodge Dec 06 '15 at 17:23
  • Refer to the docs for `WM_PAINT`: https://msdn.microsoft.com/en-us/library/windows/desktop/dd145213(v=vs.85).aspx – Jerry Dodge Dec 06 '15 at 17:36
  • 1
    @Jerry Good point about the ReleaseDC, though I don't agree about getting the Handle at startup: you shouldn't assign memory for longer than required. Also, I don't need the WM_PAINT; I only need the canvas for the font metrics, like I said in my OP. I'm assigning the filename to the text property of the TEdit, and then the VCL takes care of the painting. – Joris Groosman Dec 06 '15 at 18:43
  • "don't agree" It's up to you when it comes to performance. I'm not quite sure what you mean about not assigning memory longer than required. That may have been true 20 years ago, but that's just a tiny little speck of memory in 2015. – Jerry Dodge Dec 06 '15 at 20:50
  • 4
    This is not accurate, you need to select edit's font to the device context if you want it to be accurate. – Sertac Akyuz Dec 06 '15 at 22:00
  • Easier solution is to simply do aCanvas.Font.Assign(Edit1.Font) instead of GetDC/ReleaseDC. – djsoft Dec 06 '15 at 22:57
  • @Sertac Feel free to post an answer about this. – Joris Groosman Dec 07 '15 at 07:42
  • 1
    @djsoft Looks more simple indeed, but I get a runtime error "Canvas does not allow drawing". – Joris Groosman Dec 07 '15 at 07:47
  • Sorry for the incorrect info. It's even simpler, actually :) Just use TForm's Canvas, you'll only need two lines of code to get the job done: Canvas.Font.Assign(Edit1.Font); Edit1.Text := MinimizeName(FileOpenDialog1.FileName, Canvas, Edit1.Width - 8); – djsoft Dec 07 '15 at 08:30
1

Since the canvas is only used to get the metric, if you assume that the TEdit metric is the same as the form metric, it is sufficient to use the form canvas in the MinimizeName call. This is simpler, and valid unless there is a reason why the metric would differ.

Garth Thornton
  • 94
  • 1
  • 1
  • 7