Focus for key events on a TImage? - delphi

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;

Related

delphi How to click the arrow on the VertScrollBar

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.

Mouse-Wheel sending message to the wrong control

I am using Windows XE2, the TVirtualStringTree, and a TComboBox as an in-placed editor.
When I place a TComboBox directly on the form, at run-time I can drop down the list and the mouse wheel scrolls the items in the list up and down (as desired). However, when the TComboBox is created at run-time by TVirtualStringTree as an in-place editor, even though the newly created combo box has focus, the mouse wheel's WM_MOUSEWHEEL message is sent to the tree control and not the combo box.
This is evident because the items in the combo box drop-down list do not scroll. Instead, the tree control behind the combo box scrolls. The fixed portion of the combo box moves with the tree, but the drop-down list becomes disconnected from the fixed portion of the combo box and does not move (as depicted).
In both cases the TComboBox.Style is set to csDropDownList. When the combo box is created as the tree's in-place editor, it is done this way:
FCBox := TComboBox.Create(TreeControl);
FCBox.Visible := False;
FCBox.Parent := TreeControl;
// ... add items to combo box ...
FCBox.Visible := True;
FCBox.SetFocus;
FCBox.DroppedDown := True;
It doesn't matter where the mouse is hovering. It can be directly over the items in the combo box drop-down list and the tree control in the background is still the one that scrolls. The only way to scroll the items in the combo box is to use its scroll bar.
What would cause the parent of the focused control to receive the mouse wheel messages instead of the control itself (in this case, the TComboBox)?
VirtualTrees.pas includes the following declaration in the TBaseVirtualTree class:
private
procedure CMMouseWheel(var Message: TCMMouseWheel); message CM_MOUSEWHEEL;
The component author captured the mouse wheel messages so he could first scroll vertically and then horizontally. The custom code is the reason that the mouse wheel messages are being sent to the TVirtualStringTree instead of the TComboBox. I commented out his code and the TComboBox drop-down list scrolled as expected.
Since I really don't want to remove the TBaseVirtualTree code, I created my own TMyComboBox with the following code to use as the in-place editor. Now scrolling works correctly in both the drop-down list and in the tree control.
interface
type
TMyCombBox = class(TComboBox)
private
procedure CMMouseWheel(var Message: TCMMouseWheel); message CM_MOUSEWHEEL;
end;
implementation
procedure TMyComboBox.CMMouseWheel(var Message: TCMMouseWheel);
begin
if DoMouseWheel([], Message.WheelDelta, SmallPointToPoint(Message.Pos)) then
Message.Result := 1;
end;
This captures the CM_MOUSEWHEEL message before it is passed to the tree control and hands it to the TControl.DoMouseWheel() method to process.

TEdit onclick select all?

How to select all text of a TEdit1 whenever user click on it or click to select some text of it
It can be quite dangerous to do anything beyond the default behaviour of the TEdit control. Your users know how the standard Windows controls behave and any deviation from this is likely to cause confusion.
By default the AutoSelect property is set to True.
Determines whether all the text in the edit control is automatically selected when the control gets focus.
Set AutoSelect to select all the text when the edit control gets focus. AutoSelect only applies to single-line edit controls.
Use AutoSelect when the user is more likely to replace the text in the edit control than to append to it.
When this property is True, the entire contents of the edit control are selected when it gets the focus by means of keyboard action. If the control gets the focus by a mouse click then the contents will not all be selected. In that case you simply press CTRL+A to select all. A double click will select the word underneath the mouse. This is all standard behaviour implemented by the underlying Windows control.
If you change the select in response to the OnClick event, as per the currently selected answer, then you will find that it is impossible to move the caret with a mouse click. This is exceedingly counter-intuitive behaviour.
This is a classic example of why you need to be very careful about changing the behaviour of a control from its default. It's simply very easy not to miss a particular use case when testing but when your users get hold of the program, they are sure to find all such wrinkles.
What you could safely do is to call SelectAll from OnDblClick. This would, I believe have no annoying side-effects.
Another option would be to call SelectAll when the focus switched to the edit control, but not every time you click in the control. This might feel a little odd to the user, but I personally think it would be reasonable to take this course of action. If you want to do this you need to handle the OnEnter event of your edit control:
procedure TForm1.Edit1Enter(Sender: TObject);
begin
PostMessage(Edit1.Handle, EM_SETSEL, 0, -1);
end;
How to select all text of a TEdit1 whenever user click on it
Select Edit1 in the VCL editor and double-click on the OnClick event:
procedure TForm13.Edit1Click(Sender: TObject);
begin
Edit1.SelectAll;
end;
You can also link this event to another control like a button.
Select the button, choose and click on the V arrow to select an event you want to link.
Now both Edit1.OnClick and Button1.OnClick link to the same event.
How to select some text of a TEdit1 whenever user click on it:
procedure TForm1.Edit1Click(Sender: TObject);
begin
Edit1.SelStart:= 1;
Edit1.SelLength:= 2;
end;
You must use OnMouseUp;
procedure cxMRUEdit1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button=mbLeft then cxMRUEdit1.SelectAll;
end;

Delphi: TabStop problems of TRadioButton

When TRadioButton has TabStop=True, it's acting very strange.
If you will try to switch focus between many radio buttons on a form using Tab key, you would do it only 1 time per app session. The tabulation is one-way, never returning back to the first radio button. Also when the focus is moving across radio buttons, they becoming "checked" automatically.
Can this behavior be fixed without creating my own component?
I want standard radio buttons to
switch focus cyclically
prevent radio button from checking when the focus comes into it (I want my users to check them using Space key)
I understand that you're working with existing code, which is a real world constraint that's too often dismissed in these forums.
Sounds to me like checkboxes would suit you better. You can enforce the exclusivity normally expected of RadioButtons in the OnChecked event. That should solve your tabbing/focus and selection/deselection issues.
Checkboxes won't be checked automatically upon receiving focus, and your users can check/uncheck them with the space key.
You can put code in the OnEnter event to prevent the checkbox from selecting.
You'll need to store the previously selected RadioButton somehow though.
var
SelectedRadioButton: TRadioButton;
//event shared by all radiobuttons
procedure TForm1.RadioButton1Enter(Sender: TObject);
begin
if Sender <> SelectedRadioButton then begin
SelectedRadioButton.Checked:= true;
end;
end;
procedure TFrameOrder.RadioButton1Click(Sender: TObject);
begin
SelectedRadioButton:= (Sender as TRadioButton);
end;
procedure TFrameOrder.RadioButton1KeyPress(Sender: TObject; var Key: Char);
var
MyRadioButton: TRadioButton;
begin
MyRadioButton:= (Sender as TRadioButton);
if Key in [#32,#13] then begin
MyRadioButton.Checked:= true;
RadioButton1Click(MyRadioButton);
end; {if}
end;
It probably clearer to create a new TMyRadioButton component though because this will clutter up your regular code.
I have found an interesting article of Craig Stuntz about this problem. As I can see, I'll need to create my own control to solve it.
By default only one RadioButon has property TabStop = True;
All Radiobuttons are treated as one controll.
When radiobutton has focus you can switch beetween radiobutons using arrow up and down.
Now when user choose one option they can press tab to switch to another controll (without changing radio options).

How do I make the TAB key close a TComboBox without losing the current position?

I have a TComboBox on a form. Its Style property is set to csDropDownList. If I open the dropdown and select an option with my mouse or keyboard and hit ENTER, the dropdown box closes and the ItemIndex property is changed before the KeyPress event handler is fired. If I hit TAB, the dropdown doesn't disappear until after the KeyPress event handler has fired and focus has moved off the control, and the ItemIndex doesn't get updated; it reverts back to whatever had been selected before I opened the list.
If I want TAB to update ItemIndex to whatever's currently selected in the dropdown list, how would I implement it?
Set the Form's KeyPreview property to True.
In the ComboBox OnKeyDown event:
procedure TForm1.ComboBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_TAB) then
begin
Key := VK_RETURN;
Perform(WM_NEXTDLGCTL,0,0);
end;
end;
This emulates the return key and then moves focus to the next control.
I believe this is the default behavior, and to change it you might need to subclass the control (or even a class helper), intercept the windows message for the keystroke, then if its a tab send a return to the control and handle the tab yourself.
You should try to trap TAB earlier in the KeyUp event or maybe even earlier in the KeyDown.
When you retrieve your index use this instead of the classical ComboBox->ItemIndex
ComboBox->Items->IndexOf(ComboBox->Text)

Resources