TListView SelCount reports an incorrect number of items in a virtual list

I need to enable or disable the button depending on whether at least a row in the list is selected or not.

Below is the code to reproduce this problem. The list is populated using the OnData event and allows you to select multiple rows.

I thought I could use OnSelectItem to detect when the user changes the selection and then use the TListView SelCount function to determine the number of rows selected.

The problem is that SelCount returns 0 when the user selects multiple rows. This works fine if the list is populated manually (i.e. Not through the OnData event).

Any ideas?

thanks

Update: using the OnChange event instead seems to do the trick. However, it would be interesting to understand why SelCount returns 0 when multiple rows are selected (from inside the SelectItem event).

Another update: I sent a test project: https://dl.dropboxusercontent.com/u/35370420/TestListView2.zip , as well as a screenshot:

enter image description here

To reproduce this problem, run the application, select Item1, then SHIFT + Click on Item2. The button is disabled. My intention was to enable the button dynamically if there is at least one element in the list. If the selected item is missing, the button will be disabled.

PAS file:

unit MainUnit; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls; type TForm3 = class(TForm) ListView1: TListView; Button1: TButton; procedure FormCreate(Sender: TObject); procedure ListView1Data(Sender: TObject; Item: TListItem); procedure ListView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); private { Private declarations } public { Public declarations } end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.FormCreate(Sender: TObject); begin ListView1.Items.Count := 5; end; procedure TForm3.ListView1Data(Sender: TObject; Item: TListItem); begin Item.Caption := String.Format('Item%d', [Item.Index]); end; procedure TForm3.ListView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); begin Button1.Enabled := ListView1.SelCount > 0; OutputDebugString(pchar(String.Format('SelCount = %d', [ListView1.SelCount]))); end; end. 

the form:

 object Form3: TForm3 Left = 0 Top = 0 Caption = 'Form3' ClientHeight = 600 ClientWidth = 952 Color = clBtnFace DoubleBuffered = True Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object ListView1: TListView Left = 168 Top = 160 Width = 250 Height = 150 Columns = < item AutoSize = True Caption = 'Test' end> HideSelection = False MultiSelect = True OwnerData = True TabOrder = 0 ViewStyle = vsReport OnData = ListView1Data OnSelectItem = ListView1SelectItem end object Button1: TButton Left = 168 Top = 120 Width = 75 Height = 25 Caption = 'Some Action' Enabled = False TabOrder = 1 end end 
+6
source share
1 answer

The root problem is that when you SHIFT + Click multiple items, you will not get any OnSelectItem events for the items that were selected. SHIFT + Click first disables all items in the list view, raising one OnSelectItem event with Item=nil and Selected=False before new items are selected. During this event, TListView.SelCount indeed 0, so you turn off your button, but then there are no OnSelectItem events to tell you that new items have been selected, so you do not check SelCount again on turn on the button.

The OnSelectItem event is OnSelectItem in response to the LVN_ITEMCHANGED notification when one item changes state between the selected and unselected, or when ALL items in the entire ListView change to the same selected / unselected state. However, in virtual mode, when several consecutive elements simultaneously change to the same state, Windows can instead send one LVN_ODSTATECHANGED notification for this range of items. TListLiew does not call OnSelectItem when it receives LVN_ODSTATECHANGED , LVN_ODSTATECHANGED is started OnDataStateChange , for example:

 procedure TForm3.ListView1DataStateChange(Sender: TObject; StartIndex, EndIndex: Integer; OldState, NewState: TItemStates); begin if (NewState * [isSelected]) <> (OldState * [isSelected]) then Button1.Enabled := ListView1.SelCount > 0; end; 

Therefore, you need to use both OnSelectItem and OnDataStateChange to handle all possible changes to the selection / OnDataStateChange state.

The best solution is not to enable / disable TButton manually when changing individual elements. Drop a TActionManager on the form, create a new TAction and assign it to the TButton.Action property, and then use the TAction.OnUpdate event to enable / disable TAction based on the current TListView.SelCount , for example

 procedure TForm3.MyActionUpdate(Sender: TObject); begin MyAction.Enabled := ListView1.SelCount > 0; end; 

This will automatically enable / disable the associated TButton every time the main message queue is idle, including after processing ListView notification messages. Thus, you can update TButton no matter what input combination is used to select / TButton ListView items.

+4
source

All Articles