0
votes

Is it possible to change the Multiselect behaviour with a standard TListBox?

I would like items to only be multiselected by holding the Ctrl key, not the Shift key.

I have TActions which update depending on items selected, eg:

TAction(Sender).Enabled := ListBox1.ItemIndex <> -1;

The controls assigned to the action flicker when a listbox item is selected when holding shift to multiselect, this does not happen by holding ctrl key only.

So I would like to use only Ctrl key to multiselect.

Simply put, how can I:

  • Multiselect TListBox
  • Use Ctrl to multiselect
  • Shift has no effect

Thanks :)

1
You should fix the disease. Stop the flickering. Your users will be grateful.David Heffernan
Even if you can figure out how to do this, you shouldn't. Every other application on Windows has the behavior you're trying to "fix"; yours being totally different is just bad UI design. As David said, fix the actual problem instead of trying to hide symptoms. Where are the TAction updates happening?Ken White
What happens if you use the SelCount properties intead of ItemIndex?user160694
I didn't realise this was a problem on my side, I will be sure to look at it further and fix on my side then. @ldsandon I do use SelCount > 1, ItemIndex was just an example.user741875
In your Action OnUpdate handlers be sure to set Handled := True; otherwise the OnUpdate handler will be called continuously. See Ray Konopka's article on Effectively using ActionLists when they were introduced: edn.embarcadero.com/article/27058Marjan Venema

1 Answers

0
votes

Item selection is processed by the default window procedure of the underlying list box api control. Because of this there's no VCL property that would do this. Nevertheless, users might not like this, but you can change the behavior by handling WM_LBUTTONDOWN message that is post the the control which triggers item selection. Subclass the control in any way you like. Or, since WM_LBUTTONDOWN is posted, you can use OnMessage event of an ApplicationEvents component. Or, just as one example below, if the control is immediately parented by the form, you can use the notification sent to the parent:

type
  TForm1 = class(TForm)
    ..
  private
    procedure WMParentNotify(var Msg: TWMParentNotify); message WM_PARENTNOTIFY;
    ..

procedure TForm1.WMParentNotify(var Msg: TWMParentNotify);
var
  Pt: TPoint;
  State: TKeyboardState;
begin
  if (Msg.Event = WM_LBUTTONDOWN) then begin
    Pt := SmallPointToPoint(SmallPoint(Msg.XPos, Msg.YPos));
    MapWindowPoints(Handle, ListBox1.Handle, Pt, 1);
    if PtInRect(ListBox1.ClientRect, Pt) then begin
      GetKeyboardState(State);
      State[VK_SHIFT] := 0;
      SetKeyboardState(State);
    end;
  end;
  inherited;
end;