I'm experiencing some problems programmatically selecting all the text inside a TMemo component
I have an OnClick event handler that looks something like this
procedure TForm8.Memo1Click(Sender: TObject);
begin
Memo1.SelectAll;
end;
If I click inside the memo where the text ends, everything is selected.
But If I click somewhere at a position before where the text ends, the selection goes from 0 to the clicked position instead of selecting everything.
I have tried messing around with the memo's SelStart, SetLength and CaretPosition properties before calling SelectAll, but I haven't got this to work.
Interestingly enough, when I put a breakpoint inside the TCustomMemo.SelectAll procedure and press ctrl+a then SelectAll is also called inside TStyledMemo.KeyDown, but then everything is selected. It seems clicking inside the memo does something that makes SelectAll behave differently somehow.
I'm on Delphi 11.0
Does anybody know if it's possible what I'm trying to do?
Related
I have a problem where:
I have a form with just a combobox.
The combobox has focus and mouse is not hovering over the form when item 3 happens.
I trigger the combobox's drop-down list to show on a key-press event.
When the drop-down list is visible and then I move my mouse pointer over the form, the pointer is either invisible, shows that it is busy, or shows the resizing icon but does not turn back to a normal pointer when over the form.
Is there something that can be done to assure that, when the drop-down of the combobox shows, that the mouse pointer is visible when I move the pointer over the form?
I have tried:
Applicaiton.ProcessMessages after showing the drop-down.
Changing focus to the form the combobox is on after showing the drop-down.
Adding Key := #0; after calling the drop-down to show.
procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, Integer(True), 0);
Key := #0;
end;
Tried using a timer to trigger the drop-down within the key-press event.
Tried using "SetCursor" after commanding the drop-down to appear.
Tried using ".DroppedDown", but did not see any difference in result from that of "SendMessage".
I would hope to be able to show the mouse pointer after the drop-down is displayed, but it is hidden instead. Thanks for any suggestions.
(NOTE: This problem I have run into is not exlusive to Delphi. I was able to duplicate the issue using Visual C# 2017. Either way, if there is a way to correct this, it would be good to know).
As already commented to the question, the issue is not Delphi related. You can observe the same behavior in dialog boxes which contains a similar combo that the OS presents. One example is the one on the "run" dialog.
Involving a single environment, re-setting the cursor in an OnDropDown event handler fixes the problem.
procedure TForm1.ComboBox1DropDown(Sender: TObject);
begin
winapi.windows.SetCursor(Screen.Cursors[Cursor]);
end;
Originally I tested the above because no one calls SetCursor after the drop down. Though it seems that no one calls it before either. So I have no idea about the cause or why the above fix works.
I'm currently trying to create a combobox that has a "caption". By caption I mean the text that you see when you haven't clicked on it yet or if you are choosing an option.
Before I added the csExDropDownList it worked fine, but I wanted it to be ReadOnly. Now when I edit the Text property, it instantly gets deleted. I thought about using a TLabel in front of the combobox and making it dissapear the moment I chose a dropdown, but the TLabel is always in the background. I also tried with the TStaticText component, but that leaves a different colored background in front of the combobox which looks bad.
If I was unable to explain with words what I'm trying to edit/wanted to add a text to, this is what I mean:
I've found a workaround to my problem. I added a third dropdown with the index of 0. Now in properties I put the ItemIndex to 0 which means that it will be displayed similar to the Text property. When I interact with either QuickSort or InsertSort I delete Index 0.
My code looks like this:
procedure TSorterForm.AlgorithmCbxChange(Sender: TObject);
begin
if (AlgorithmCbx.Text <> 'Choose Algorithm...') and not IsAlgorithmSelected then begin
AlgorithmCbx.Items.Delete(0);
IsAlgorithmSelected:= true;
end;
end;
Obviously not perfect so it'd be great if you could tell me how to improve this.
I'm having an issue with the combo box. I have an event handler for OnClick which refreshes data based on what item was selected. The problem is when this scenario occurs:
Drop-down the combo box to list the various options
Type on the keyboard to find a matching item
Combo box changes this selection and calls the OnClick event
My screen refreshes due to this selection / event
Click somewhere outside of the combo box to take the focus away from it
Combo box goes back to the previous selection, even though OnClick was already called
Even though Combo box changed back to prior selection, OnClick isn't called again
After this, Combo Box shows different value than what my data actually represents
So when you open a combo box, and type a few letters on the keyboard to find the item in the drop-down list, OnClick is fired which refreshes my screen. But when you click somewhere outside the combo box (thus taking the focus away from it), the combo box changes back to whatever value was previously selected, instead of what I had typed. And at the same time, the OnClick event isn't fired, so the combo box shows the incorrect value compared to what I had loaded on the screen.
How do I make the combo box stay on the selected item in this scenario of typing the item on the keyboard?
In my code, I deal with this using the OnCloseUp event. Well, in fact I'm using a sub-classed combo for my drop-down lists and they override both the Change and CloseUp methods:
procedure TMyDropDownList.Change;
begin
RespondToChange;
inherited;
end;
procedure TMyDropDownList.CloseUp;
begin
RespondToChange;
inherited;
end;
The RespondToChange method reacts to the new ItemIndex value. If it is expensive to react to every single change whilst the combo is dropped down, then you might consider omitting the call to RespondToChange from the Change method.
You could use OnExit to make the entry with the keyboard jive with the Index on the ComboBox; where VarS is assigned OnChange and is the answer you would like to keep:
procedure TForm1.ComboBox1Exit(Sender: TObject);
begin
{ Windows keyboard select bug, force use of selected }
ComboBox1.ItemIndex := ComboBox1.Items.IndexOf(VarS);
end;
I would call this a bug in the ComboBox design.
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;
I have a simple test app, with one empty form, and a second containing a TButton. The button script is like this:-
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show();
end;
Form2 is auto-created at startup. There's no other code at all.
When I run the app, I can press the button and Form2 appears. I can reposition/resize Form2 on the primary monitor, and then close it. If I press the Form1 button again, Form2 correctly reappears in the position it was last at. All fine so far...
However, if I position Form2 onto a secondary monitor, close it, and then press the button, then Form2 appears back on the primary monitor!
I want the form to reappear on the monitor it was last on - how can I get this behaviour??
This is because the default value for the form's DefaultMonitor property is dmActiveForm.
Set Form2's DefaultMonitor to dmDesktop, and the problem is resolved.