Delphi - How to add tabs in TMEMO? - delphi

As shown like here.
pic: tabs with memo
Currently, my TMEMO displays bunch of different data, like this:
Data #1
Paragraphs
Data #2
Paragraphs
Data #N
Paragraphs
So to avoid scrolling, I want to add tabs to the Nth number.
So what components do I need and how should I intiate the process?

you need to use a combination of a TMemo and TTabControl.

Do not know how you get your paragraphs but you'll have to iterate through them, creating a TabSheet and a Memo for each.
procedure TfrmMemo.CreateTabsWithMemo;
var
pgControl: TPageControl;
TabSheet: TTabSheet;
Memo: TMemo;
begin
pgControl := TPageControl.Create(self);
pgControl.Parent := Self;
pgControl.Align := alClient;
//Do this for each paragraph
TabSheet := TTabSheet.Create(pgControl);
TabSheet.PageControl := pgControl;
TabSheet.Caption := Format('Tab %d', [pgControl.PageCount]);
Memo := TMemo.Create(TabSheet);
Memo.Parent := TabSheet;
Memo.Align := alClient;
Memo.Lines.Text := 'Your Paragraph here'
///
end;

Use TPageControl and TTabSheet. Place a TMemo component on each TTabSheet.
You can drage the TPageControl onto the form to get started.

Related

Firemonkey LiveBinding to TListbox to show images

I'm trying to use LiveBindings with TListbox. This works with TListview but not TListbox. I'm trying to display only images in a TListbox (no text) but added CustomerID just to show the LiveBindings works. Just that the image does not display from the CustomerPhoto blob field.
LinkFillControlToField1.Control := Listbox1;
with LinkFillControlToField1 do
begin
FillExpressions.Clear;
with FillExpressions.AddExpression do
begin
SourceMemberName := 'CustomerPhoto';
ControlMemberName := 'Bitmap'; // no error, means ControlMember exists
end;
with FillExpressions.AddExpression do
begin
SourceMemberName := 'CustomerID'; // just added this for testing
ControlMemberName := 'Text'; // displays correct
end;
end;
The Bitmap in TListview seems to be directly in it, whereas the 'Bitmap' of TListbox is in ItemData of the TListboxitem - could this be the issue?
Is there anything else to do to make the bitmap appear in TListbox, given that it's so automatic in TListview?

Make Timage move down and left 3 pixels when Mouse enters and return to original position when mouse leaves

I have a form with about 168 Timage objects containing Icons that are user selectable.
I wish to make each Icon move Down and Right by 3 pixels when the mouse is over the Timage object. I want it to return to its original position when the mouse leaves the Timage. This will add a pleasing effect to the user interface.
I know I can do this in the OnMouseEnter and OnMouseLeave events and it works well - however I cannot help but think that there must be a more elegant / efficient method to produce this effect for all 168 Timage objects, rather than creating 168 OnMouseEnter procedures and 168 OnMouseLeave procedures.
Any help much appreciated ...
It is enough to create a single OnMouseEnter event handler procedure and assign it to every component (similar for OnMouseLeave).
If these components were created in design-time (hard to imagine), then you can select all 168 images in the Form Designer, and then go to the Object Inspector and assign the events in a single go, as Remy Lebeau wrote in comments. Alternative way - use existing list of components (assuming that owner is form and there is no other TImages on the form):
for i := 0 to Components.Count - 1 do
if Components[i] is TImage then //perhaps more conditions to separate needed images
TImage(Components[i]).OnMouseEnter := EnterHandler;
If components were created in run-time and they are stored in array or list, handler assigning is simpler:
for i := 0 to Images.Length - 1 do
Images[i].OnMouseEnter := EnterHandler;
Then you can work with each component using the event's Sender argument:
procedure TMyForm.EnterHandler(Sender: TObject);
begin
TImage(Sender).Left := TImage(Sender).Left + 3;
TImage(Sender).Top := TImage(Sender).Top + 3;
end;
procedure TMyForm.LeaveHandler(Sender: TObject);
begin
TImage(Sender).Left := TImage(Sender).Left - 3;
TImage(Sender).Top := TImage(Sender).Top - 3;
end;
The cleanest solution here would be to create a custom component and to sanitize your design away from such a heavy and flat design-time layout. These naturally become difficult to maintain and to modify.
That said, if you want a quick hack to save yourself a lot of typing and clicking, you can use an interposer class to inject this mouse behaviour.
In the interface section of your Form's unit, add the following class above the Form's class declaration:
type
TImage = class(Vcl.ExtCtrls.TImage)
private
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
end;
TForm1 = class(TForm)
{ ... rest of your form as normal }
end;
And then, in the implementation section, add this:
procedure TImage.CMMouseEnter(var Message: TMessage);
begin
inherited;
Top := Top + 3;
Left := Left + 3;
end;
procedure TImage.CMMouseLeave(var Message: TMessage);
begin
inherited;
Top := Top - 3;
Left := Left - 3;
end;
Defining an interposer like this effectively causes your modified TImage class to replace all of the existing TImage components that are placed on the Form at design-time.
Note that this example is only for VCL on Windows. For a cross-platform solution using FMX, all UI controls have virtual DoMouseEnter() and DoMouseLeave() methods that you can override instead, eg:
type
TImage = class(FMX.Objects.TImage)
protected
procedure DoMouseEnter; override;
procedure DoMouseLeave; override;
end;
...
procedure TImage.DoMouseEnter;
begin
inherited;
Top := Top + 3;
Left := Left + 3;
end;
procedure TImage.DoMouseLeave;
begin
inherited;
Top := Top - 3;
Left := Left - 3;
end;

TDBGrid right align columns and set caret pos when editing value

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

Delphi/Firemonkey make Listboxses sync scroll better

I have a very dirty way of having 7 listboxses scroll in sync, they all have the exact same height so when I move the position of any one of them the other 6 should also move to that position
What I do is Assume Listbox1 is scrolled and ViewportPositionChange() is fired I say
Listbox2.ViewportPosition := NewViewportPosition;
Listbox3.ViewportPosition := NewViewportPosition;
And this all works and I just add this code to each Listboxses ViewportPositionChange() event, but its terribly jerky and too slow on Andriod, I still need to be able to scroll from any one listbox but is there not a way to improve the performace of the scroll?
Ok to be honest it isn't terribly slow but you can feel its not exactly snappy, and also each listbox can have lots of listboxses and each listbox can also have a few controls and more controls in those controls so alot of repainting is done I think and this is what is making it less snappy.
Sample code
Firstly place a Gridpanel layout on your form, Add 6 columns And add a listbox to each, each Listbox represents a day, where Listbox 1 is Monday, Also Align these to client.
Global variables
private Glistbox : array[0..6] of TListBox;
On form Create
Glistbox[0] := ListBox1;
Glistbox[1] := ListBox2;
Glistbox[2] := ListBox3;
Glistbox[3] := ListBox4;
Glistbox[4] := ListBox5;
Glistbox[5] := ListBox6;
Glistbox[6] := ListBox7;
And now on each listboxses OnViewportPositionChange
//Note that this would be for Listbox1 so for listbox 2 it needs to be changed acordingly
ListBox2.ViewportPosition := NewViewportPosition;
ListBox3.ViewportPosition := NewViewportPosition;
ListBox4.ViewportPosition := NewViewportPosition;
ListBox5.ViewportPosition := NewViewportPosition;
ListBox6.ViewportPosition := NewViewportPosition;
ListBox7.ViewportPosition := NewViewportPosition;
Then add a button with the following code :
var
mainlayout,item1,item2 : Tlayout;
listboxitem : TListBoxItem;
myrec,myrec2 : TRectangle;
lbl1,lbl2 : TLabel;
I: Integer;
begin
for I := 0 to 6 do
begin
listboxitem := TListBoxItem.Create(nil);
mainlayout := TLayout.Create(nil);
mainlayout.Align := TAlignLayout.Client;
mainlayout.Parent:= listboxitem;
item1 := TLayout.Create(nil);
item1.Align:= TAlignLayout.Left;
item1.Parent := mainlayout;
item2 := TLayout.Create(nil);
item2.Align:= TAlignLayout.Right;
item2.Parent := mainlayout;
myrec := TRectangle.Create(nil);
myrec.Align := TAlignLayout.Client;
myrec.Parent := item1;
myrec2 := TRectangle.Create(nil);
myrec2.Align := TAlignLayout.Client;
myrec2.Parent := item2;
lbl1 := TLabel.Create(nil);
lbl1.Align := TAlignLayout.Client;
lbl1.TextAlign := TTextAlign.Center;
lbl1.Text := '1';
myrec.AddObject(lbl1);
lbl2 := TLabel.Create(nil);
lbl2.Align := TAlignLayout.Client;
lbl2.TextAlign := TTextAlign.Center;
lbl2.Text := '2';
myrec2.AddObject(lbl2);
Glistbox[I].AddObject(listboxitem);
item2.Width:= mainlayout.Width/2;
end;
There could sometimes be more controls in a listboxitem but generally I think this would be the norm, Now add a few items and try the scroll
The FMX TListBox has a lot of overhead because every listbox item basically is a styled component that is inserted into a scrollbox. This is good for flexibility, but bad for speed.
In Windows and OSX there is functions like ScrollWindowEx or NSView.scrollRect + NSView.setNeedsDisplayInRect that can help improve performance by scrolling a part of the window without issuing paint messages. So you'd only have to invalidate a part of the listbox. I could imagine that Android has something similar. But you'll likely have to patch FMX in order to use that. This can improve performance a bit, but is still not a magic switch for great performance. A similar approach would be caching the content of the scrollbox in a bitmap and then copy parts of the bitmap to the screen while scrolling.
If you need really good listbox performance then you'll need to develop your own listbox that is optimized for speed. This is what I had to do for the OSX version of my application in order to get the performance I needed.
There's also TListView for Firemonkey which is better choice than TListBox when all items share the same visual structure. There's a tutorial by Ray Konopka (from 2014) explaining what each one is suited for:
https://www.youtube.com/watch?time_continue=934&v=XHYtwAu5fl4
For adjusting scrolling behaviour (in case it helps) see:
https://docwiki.embarcadero.com/RADStudio/Alexandria/en/FireMonkey_Layouts_Strategies#Smooth_Inertial_Scrolling

Problem with adding graphics to TLabel

I'm trying to create with Delphi a component inherited from TLabel, with some custom graphics added to it on TLabel.Paint. I want the graphics to be on left side of text, so I overrode GetClientRect:
function TMyComponent.GetClientRect: TRect;
begin
result := inherited GetClientRect;
result.Left := 20;
end;
This solution has major problem I'd like to solve: It's not possible to click on the "graphics area" of the control, only label area. If the caption is empty string, it's not possible to select the component in designer by clicking it at all. Any ideas?
First excuse-me for my bad English.
I think it is not a good idea change the ClientRect of the component. This property is used for many internal methods and procedures so you can accidentally change the functionality/operation of that component.
I think that you can change the point to write the text (20 pixels in the DoDrawText procedure -for example-) and the component can respond on events in the graphic area.
procedure TGrlabel.DoDrawText(var Rect: TRect; Flags: Integer);
begin
Rect.Left := 20;
inherited;
end;
procedure TGrlabel.Paint;
begin
inherited;
Canvas.Brush.Color := clRed;
Canvas.Pen.Color := clRed;
Canvas.pen.Width := 3;
Canvas.MoveTo(5,5);
Canvas.LineTo(15,8);
end;
What methods/functionality are you getting from TLabel that you need this component to do?
Would you perhaps be better making a descendent of (say, TImage) and draw your text as part of it's paint method?
If it's really got to be a TLabel descendant (with all that this entails) then I think you'll be stuck with this design-time issue, as doesn't TLabel have this problem anyway when the caption is empty?
I'll be interested in the other answers you get! :-)

Resources