Self.VertScrollBar.Range := Application.MainForm.Height;
Successfully created a VertScrollBar.
Pressing the arrow on the VertScrollBar does not respond.
Is it possible to forward messages or set object options?
If the scroll bars you show on your screen dump are TScrollBar components you dropped on the form, then assign an OnChange event handler and do whatever you need to do:
procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
Memo1.Lines.Add(ScrollBar1.Position.ToString);
end;
But maybe what you really need is a TScrollBox which is a kind of TPanel with content bigger than visible size and scroll bars permitting to scroll the visible part.
Related
Delphi controls have AutoSize property which is exposed e.g. in TPanel. It adjusts the width/height of the panel depending on the content.
Apparently it does nothing when the panel is invisible, and does no realigning later when it's set to visible. So if I put some controls into it and then make it visible, the size is not adjusted.
I can trigger adjusting size by setting height to any value in FormShow:
procedure TForm1.FormShow(Sender: TObject);
begin
Panel1.Height := Panel1.Height + 1; //triggers auto-resize
end;
But I have to do this manually for every control which has AutoSize on. I'm bound to forget something.
Are there better ways to fix this, preferably once and for all?
I don't think there's much that you can do. A better way to for the re-sizing is to add a call to the Realign method of the panel immediately after you make it visible.
You could hook into the CM_VISIBLECHANGED message and force the matter there, for auto-sized controls. For instance, using an interceptor:
type
TPanel = class(Vcl.ExtCtrls.TPanel)
protected
procedure CMVisibleChanged(var Message: TMessage); message CM_VISIBLECHANGED;
end;
procedure TPanel.CMVisibleChanged(var Message: TMessage);
begin
inherited;
if Visible and AutoSize then
Realign;
end;
Its been a while since i used delphi, but one thing i remember is that the controls did play a bit of mind games on me, most of the time it was because the rendering engine did not refresh the form and the controls.
If you have the control set to auto-resize i would suggest checking if form1.refresh or panel1.refresh since its been a few years since i played with it ( delphi 7 ) i might confuse refresh with repaint. which some controls had, which initiated the size calculation before repainting the control. since delphi controls are open source you can go in to the apropriate pas file to find the control and look at refresh/repaint to see if you can persist the auto-resizing.
hope that helped.
Here's the setting (I'm using Delphi 7, not sure if this will happen in later/earlier versions):
Create a new project with two forms.
Put a TMemo, a TRichEdit and a TButton on the first form.
Set the Lines properties of both the TMemo and the TRichEdit to 123456.
In the TButton's OnClick event handler put Form2.Show;
Run the application, click the button and the empty Form2 will show and get focus.
Now click in the middle of the text "123456" of the TMemo in Form1 - the focus will change back to Form1 and the cursor (caret) will be in the middle of the text "123456" where you clicked as expected.
Click on Form2 again to give it focus again.
Now click in the middle of the text "123456" of the TRichEdit in Form1 - the focus will change back to Form1, but the cursor (caret) won't be in the middle of the text "123456" where you clicked, but on the second empty line of the RichEdit (or wherever it was previously).
If you click a second time in the same place in the middle of the text "123456" of the TRichEdit, the cursor (caret) will now be moved there as expected.
So the TRichEdit control gets focus, but the cursor (caret) isn't moved as expected.
Note: This only happens when changing the focus from one form to another. Changing the focus from one control to a TRichEdit control in the same form doesn't exhibit this problematic behavior.
My question: How to avoid the need for this second click inside a TRichEdit and have the control behave like the TMemo in this regard.
Thanks in advance!
You can derive a new control, or subclass the richedit in any way you like, to intervene with the activation mechanism. Below sample interposer class sets the focus to the control before the mouse down message is posted when it is about to be activated by the left button of the mouse if the control is not already focused:
type
TRichEdit = class(comctrls.TRichEdit)
protected
procedure WMMouseActivate(var Message: TWMMouseActivate);
message WM_MOUSEACTIVATE;
end;
procedure TRichEdit.WMMouseActivate(var Message: TWMMouseActivate);
begin
if (GetFocus <> Handle) and (Message.MouseMsg = WM_LBUTTONDOWN) then
SetFocus;
inherited;
end;
There is the good component to maximize a child window in a client area (the SHIFT key must be held) - NLDExtraMDIProps.
Or this code can be used:
procedure WMSIZE(var Msg: TMessage); message WM_SIZE;
procedure TForm2.WMSIZE(var Msg: TMessage);
begin
inherited;
if Msg.WParam = SIZE_MAXIMIZED then
begin
ShowWindow(Handle, SW_RESTORE);
Left := 0;
Top := 0;
Width := Form1.ClientWidth - 4; // The BORDER
Height := Form1.ClientHeight - 4;
end;
end;
But maximizing isn't real maximizing. The child window is only aligned to the client area. It must automatically resize and fit the client area when the parent window is resized, the maximize/restore system button must change etc.
I try to accomplish effects that are described below.
As you see on the pictures the child windows are maximized, and
they don't take the entire parent window (only the client area).
It's impossible to move them over the caption/title bar because they are maximized.
They have the restore button, not the maximize button any more.
They are aligned to the client area (resizing of the parent window causes resizing of the child one withing the client area).
The code in my question and the component don't do like the child windows on the pictures.
Can we make a window really maximized (not just aligned)?
Not maximized (not good; the component and the code from my question maximize like on these pictures):
Maximized (what I need):
I do not understand your problem. Maximizing an MDI child window is done:
programmatically: by using ShowWindow(ActiveMDIChild.Handle, SW_MAXIMIZE),
manually: by clicking the Maximize border icon, or by double clicking on the form caption.
Both these actions result in:
the disappearance of the child window border (collapses into the MDI form border),
the addition of small border icons (for the child window) to the main menu bar,
a resize effect similar to that of Align=alClient.
To restrict the available space for the child windows within the main form, make sure to align windowed controls to edges of the form.
Setting the Align or Anchors properties for MDI child windows has no effect: they are not part of the default VCL aligning implementation anymore; Windows has taken over that job.
If you want to intervene on the resizing of an MDI child, then handling WM_SIZE is the wrong approach, because that message is send áfter the resize. Instead, handle WM_SYSCOMMAND as I explained here.
As for my component that you refer to:
Manually maximizing by clicking the Maximize border icon does exactly thát: a default maximize operation as outlined above,
Manually maximizing by clicking the Maximize border icon - while holding the Shift key - does resize the child window to the largest spare space within the MDI form. In this case, resizing the MDI main form does nót resize the MDI child forms.
if (Msg.WParam = SIZE_MAXIMIZED) then
begin
Left := 0;
Top := 0;
Width := frmMain.ClientWidth - 4;
Height := frmMain.ClientHeight - 4;
SendMessage(Handle, WM_SIZE, SIZE_RESTORED, 0);
end;
I'm building an editor which uses a TImage to display a picture and has mouse events to be able to draw, move, and resize boxes on the image. All this works perfectly. Now I'm trying to implement the ability to use the arrows on the keyboard to move the selected box, but A) TImage does not get any focus, and B) TImage does not have any key events (because it cannot get focus). I guess I could cheat and switch on the form's KeyPreview property and catch them there, but there's many other controls on this form and I'd need to make sure the user is intending to work with the image. For example, if user has focus in the TEdit control, the arrow keys shall only affect this memo, and not modifying the image.
So is there any way to put or fake some kind of focus in the TImage to recognize key events?
Only controls that inherit from TWinControl can receive keyboard focus.
TImage descents from TGraphicControl and cannot receive keyboard events.
You can put the Image on top of a panel which sits on top of another control e.g. TEdit and give that focus if the Image is selected.
Then just use the OnKeyPress event of the non-visible edit.
Make sure to disallow the tab key if you don't want that to change the focus to another control.
procedure TForm8.Image1Click(Sender: TObject);
begin
Edit1.SetFocus;
end;
procedure TForm8.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #9 then Key = #0; //disable tab key.
case key of
//do stuff here
end; {case}
end;
When Delphi (2006) goes quantum: I've got "something" that appears to be both a TToolBar and a TPanel, depending on how you observe it. I'd like to understand what's going on.
Here is how to create it and what happens:
in the DFM
add a TToolBar named bar;
in that TToolBar, put a TPanel.
in the code and at runtime:
the panel appears in the list of buttons bar.Buttons[], let's say at index i
bar.Buttons[i], from the compiler point of view, is a TToolButton
bar.Buttons[i].ClassName = 'TPanel'
(bar.Buttons[i] is TToolButton) = true, but that's the compiler optimising the call to 'is' out;
indeed IsBarButton(bar.Buttons[i]) is false for function IsBarButton (defined below);
bar.Buttons[i].Name is the name I gave the TPanel in the DFM
inspecting the value bar.Buttons[i] in the debugging:
it has a property 'Caption' the real TToolButton's don't have
strangely, it has all properties TToolButton's have, like TToolButton.Indeterminate (=true).
IsToolButton:
function IsToolButton(X : TObject) : boolean;
begin
Result := X is TToolButton;
end;
So bar.Buttons[i] both is and is not a TToolButton... what's up ?
(Bottom story is I'd like to distinguish my TPanel from the genuine TToolButton's. This I can do in more or less hackish ways. My goal by asking this question here, is to get a fuller understanding of what's really happening here.)
Question: what is happening ?
Sub-question: is it legitimate to add a TPanel to a TToolBar ?
The only thing the OS allows to be added to a tool bar is a tool button. To add anything else, you technically need to create a button and then put your other things on top of it. The button that gets added is literally a placeholder. It's there to take up space so the next thing you add gets positioned properly.
You can see this sometimes if the non-tool-button control you add is transparent. Then you can see the tool bar's separator underneath, so it looks like there's a vertical line running through the middle of your control.
When you add a non-tool-button control to the tool bar, the Buttons property indeed lies about the type of the control. You'll notice throughout ComCtrls.pas that TToolBar itself always casts the buttons to TControl and then checks whether they really descend from TToolButton. It's completely legitimate to add non-buttons to a tool bar; that's why the Form Designer allows it in the first place.
I suggest you use the Form Designer to create your tool bar. That way, the IDE will maintain an identifier for you in your form, so you'll always have a direct reference to your panel. You won't have to go hunting for it in the tool bar. Even if you're creating the tool bar manually, it's a good idea to make an extra field to refer to the panel. Even if you move the panel around within the tool bar, it will still be the same object the whole time, so you needn't worry about dangling references.
When you put a couple of buttons and a panel on a toolbar, and a Memo somewhere, then run this code in the form's onCreate:
procedure TForm1.FormCreate(Sender: TObject);
function _IsToolButton(const aObject: TObject): Boolean;
begin
Result := aObject is TToolButton;
end;
function _IsPanel(const aObject: TObject): Boolean;
begin
Result := aObject is TPanel;
end;
var
i: Integer;
begin
for i := 0 to bar.ButtonCount - 1 do begin
Memo.Lines.Add(Format('bar.Buttons[%d].Name: %s', [i, bar.Buttons[i].Name]));
Memo.Lines.Add(Format('bar.Buttons[%d].ClassName: %s', [i, bar.Buttons[i].ClassName]));
Memo.Lines.Add(Format('bar.Buttons[%d] is TToolButton: %s', [i, BoolToStr(_IsToolButton(bar.Buttons[i]), True)]));
Memo.Lines.Add(Format('bar.Buttons[%d] is TPanel: %s', [i, BoolToStr(_IsPanel(bar.Buttons[i]), True)]));
// Memo.Lines.Add(Format('bar.Buttons[%d] has Caption property: %s', [i, 'dunno yet']));
Memo.Lines.Add('');
end;
end;
you'll see that the panel is not a TooButton and most definitely a TPanel.
The debugger showing properties of a ToolButton for the panel, is simply the debugger casting each and every bar.Buttons[i] to a TToolButton. When you right-click on the "Data" tab of the Debug inspector, you can Type Cast it to a TPanel and you will get the correct information.
'is it legitimate?' - well, you are definitely using the toolbar in a way that the creator of the toolbar did not ment it to be used. Will it blow up in your face? Who knows. I guess you could walk through the sourcecode for the toolbar and check if it is safe or not, but what about possible third party tools or components, expecting to find buttons in a toolbar?
I would see if I could find another way of solving my problem. Clever hacks have a tendency to turn out not so clever after all, and it will surely higten the wtf-rate of your code.
Do you have to use a toolbar? What about a flowpanel with buttons and panels instead? Or a panel with a toolbar and a panel?