Resizing a FireMonkey Combo Box to Fit it’s Contents
MonkeyStyler displays two combo boxes on it’s toolbar, one to list the currently opened files and a second to list the elements in the current file. A number of users mentioned that long items in the combo box would be cropped and sometimes difficult to identify.
Creating a routine to resize a combo box made an interesting little challenge. We need to find the length of the longest item, but this needs to take account of the current font settings, and there’s no convenient ‘TextWidth’ function in FireMonkey. But we cab do it using a TText object.
I started by creating a TText object with AutoSize set to True and WordWrap set to False.
T := TText.Create(nil);
T.AutoSize := True;
T.WordWrap := False;
Next we need to get the font used. In FireMonkey a TComboBox owns a TListBox which is used for the dropdown and the combo box copies the contents of the selected item into itself. So we really need the font of a TListBoxItem. I chose to use the font assigned to an actual list box item of the component:
if Items.Count > 0 then
begin
Item := ListBox.ListItems[0];
Item.ApplyStyleLookup;
O := Item.FindStyleResource('text');
if O is TText then
T.Font.Assign(TText(O).Font);
...
Note the call to ApplyStyleLookup which we need in case the item doesn’t currently have it’s styling applied. This could be because it is newly created or the styling has been removed - in FM2 the stying of non-visible controls is removed to save resources.
Next up is to iterate over the strings in Items and get the width of the widest,
for S in Items do
begin
T.Text := S;
//Bug in XE3: Assigning Text doesn't resize a TText, so hack around it.
T.AutoSize := False;
T.AutoSize := True;
if O <> nil then
NewWidth := T.Width+TControl(O).Padding.Left+TControl(O).Padding.Right;
if NewWidth > MinWidth then
MinWidth := NewWidth;
end;
In the initial release of XE3 there is a bug in TText such that it doesn’t resize when you set the Text property, so we do a little dance with AutoSize to force an update. Also note we’re taking account of the Padding of the ‘text’ resource.
And finally we need to assign the new width, but since there is other stuff in the style besides the text we don’t know exactly what that is (especially if the style later changes). So, we calculate the width of the extra stuff with the current settings (Width-TControl(O).Width) and add out new width for the text,
O := FindStyleResource('content');
if O is TControl then
Width := Width-TControl(O).Width+MinWidth;
Putting it together, here is the complete code:
type TSolentComboBox = class(TComboBox)
public
procedure ResizeToContents(MinWidth: Single);
end;
...
procedure TSolentComboBox.ResizeToContents(MinWidth: Single);
var
T: TText;
NewWidth: Single;
O: TFMXObject;
S: String;
Item: TListBoxItem;
begin
try
T := TText.Create(nil);
T.AutoSize := True;
T.WordWrap := False;
O := nil;
if Items.Count > 0 then
begin
Item := ListBox.ListItems[0];
Item.ApplyStyleLookup;
O := Item.FindStyleResource('text');
if O is TText then
T.Font.Assign(TText(O).Font);
NewWidth := 0;
for S in Items do
begin
T.Text := S;
//Bug in XE3: Assigning Text doesn't resize a TText, so hack around it.
T.AutoSize := False;
T.AutoSize := True;
if O <> nil then
NewWidth := T.Width+TControl(O).Padding.Left+TControl(O).Padding.Right;
if NewWidth > MinWidth then
MinWidth := NewWidth;
end;
end;
O := FindStyleResource('content');
if O is TControl then
Width := Width-TControl(O).Width+MinWidth;
finally
T.Free;
end;
end;
Previous Comments
#2 from .(JavaScript must be enabled to view this email address) on December 08, 2012
One other problem I neglected to mention. TComboBox and TComboEdit to not appear to work at all on the Mac. The drop down list does not and the Comboedit box will not allow you to enter or alter existing text. What obvious thing am I missing here? It works perfectly on windows but not when loaded on a Mac. I created a form with only these two controls on it to verify it was not some interaction with my problem that was at fault. Still did not work. I am using XE2 for these tests and am reluctant to spend money on XE3 based on my experiences with XE2.
#3 from .(JavaScript must be enabled to view this email address) on December 08, 2012
The text of the combobox is that of the TListItem is is displaying, so you’d need to either change the ListBoxStyle style element, or to change an individual item, change the font settings on the ‘text’ component from the style (See FindStyleResource method).
To get an image with scrollbars, place it in a TScrollBox.
For the background you would be best off modifying the style, or using a custom style. The exact method to use will depend on the component you want to modify.
#4 from .(JavaScript must be enabled to view this email address) on December 08, 2012
I’ve never done Mac work, so can’t really comment. You may want to ask on the Embarcadero forums.
#5 from .(JavaScript must be enabled to view this email address) on December 10, 2012
TScrollBox does not work when I tried it but I discovered TImageViewer which does exactly what I want it to. Thanks.
#1 from .(JavaScript must be enabled to view this email address) on December 07, 2012
I have a different problem but similar problem. Rather than resize the control I want to change the font size so the text fixs withing the designed control size.
TComboEdit has a font property but TCombBox does not. How to you set the font size on a TComboBox for all its members? It used to be so simple under VCL but FM is so much more complicate and also so poorly documented.
I would also like to know how you crop an image that is too big for TImage. In the old VCL days you got scroll bars but no more!
I would also like to know how to reliable set the back colour on a FM control. Following works on TPanel but not other controls like TTabItem
procedure FMControlBackColour(const Control: TControl; BackColour: TAlphaColor);
const
XRadius = 0;
YRadius = 0;
Corners = [TCorner.crTopLeft, TCorner.crTopRight, TCorner.crBottomLeft, TCorner.crBottomRight];
Opacity = 1;
CornerType = TCornerType.ctRound;
var
Rect: TRectF;
begin
with Control do begin
Rect.Top := 0;
Rect.Left := 0;
Rect.Width := Width;
Rect.Height := Height;
Canvas.Fill.Color := BackColour;
Canvas.FillRect(Rect, XRadius, YRadius, Corners, Opacity, CornerType);
end;
end;
Any help would be appreciated.