Delphi TListView SelCount报告虚拟列表中的项目数错误

Delphi TListView SelCount报告虚拟列表中的项目数错误,delphi,vcl,delphi-xe5,tlistview,Delphi,Vcl,Delphi Xe5,Tlistview,我需要根据列表中是否至少选择了一行来启用或禁用按钮 下面是重现此问题的代码。该列表使用OnData事件填充,并允许选择多行 我认为可以使用OnSelectItem来检测用户何时更改选择,然后使用TListView SelCount函数来检测所选行的数量 问题在于,当用户选择多行时,SelCount返回0。如果列表是手动填充的(即,不是通过OnData事件填充的),则此选项可以正常工作 有什么想法吗 谢谢 更新:改用OnChange事件似乎可以解决这个问题。不过,理解为什么SelCount在选择多

我需要根据列表中是否至少选择了一行来启用或禁用按钮

下面是重现此问题的代码。该列表使用OnData事件填充,并允许选择多行

我认为可以使用OnSelectItem来检测用户何时更改选择,然后使用TListView SelCount函数来检测所选行的数量

问题在于,当用户选择多行时,SelCount返回0。如果列表是手动填充的(即,不是通过OnData事件填充的),则此选项可以正常工作

有什么想法吗

谢谢

更新:改用OnChange事件似乎可以解决这个问题。不过,理解为什么SelCount在选择多行(从SelectItem事件中)时返回0还是很有意思的

另一个更新:我发布了一个测试项目:以及一个屏幕截图:

要重现此问题,请运行应用程序,选择Item1,然后按住SHIFT键并单击Item2。该按钮已禁用。我的意图是动态启用按钮,只要列表中至少选择了一项。如果没有选定的项目,则按钮被禁用

PAS文件:

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.
表格:

对象格式3:t格式3
左=0
Top=0
标题='Form3'
ClientHeight=600
ClientWidth=952
颜色=clBtnFace
双缓冲=真
Font.Charset=默认字符集
Font.Color=clWindowText
字体高度=-11
Font.Name='Tahoma'
Font.Style=[]
OldCreateOrder=False
OnCreate=FormCreate
PixelsPerInch=96
text高度=13
对象列表视图1:TListView
左=168
Top=160
宽度=250
高度=150
列=<
项目
自动调整大小=真
标题=‘测试’
结束>
HideSelection=False
多选=真
OwnerData=True
TabOrder=0
ViewStyle=vsReport
OnData=ListView1Data
OnSelectItem=ListView1SelectItem
结束
对象按钮1:t按钮
左=168
顶部=120
宽度=75
高度=25
标题=‘一些动作’
已启用=错误
TabOrder=1
结束
结束

根本问题是,当按住SHIFT键并单击多个项目时,对于已选择的项目,将不会获得任何
OnSelectItem
事件。按住SHIFT键并单击可首先取消选中所有列表视图项目,触发单个
OnSelectItem
事件,其中
Item=nil
Selected=False
,然后选择新项目。在该事件发生时,
TListView.SelCount
实际上是0,因此您禁用了按钮,但没有进一步的
OnSelectItem
事件告诉您已选择了新项目,因此您不必再次选中
SelCount
来重新启用按钮

当单个项目在选定和未选定之间更改状态时,或者当整个ListView中的所有项目更改为相同的选定/未选定状态时,将触发
OnSelectItem
事件以响应通知。但是,在虚拟模式下,当多个连续项目同时更改为相同状态时,Windows可以为该范围的项目发送单个通知
tListLeaw
在接收到
LVN\u ODSTATECHANGED
时,不会触发选择项上的
OnDataStateChange
,而是触发
OnDataStateChange
,例如:

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

每当主消息队列空闲时,包括在处理ListView通知消息之后,将自动启用/禁用关联的
t按钮。这样,无论使用何种输入组合来选择/取消选择ListView项,您都可以保持
t按钮的更新。

fyi-我无法用Delphi 7再现错误。-1问题中的代码在XE5中按预期工作,而不是如您所述。@DavidHeffernan:您在哪个操作系统上运行它?我正在使用Windows 7 64位和XE5的最新更新。您可以从以下位置下载项目:。我使用sysinternals DebugView来查看输出。当我在列表中选择多个项目时,仅触发一个SelectItem事件,SelCount为0。如果我只选择一项,则会触发两个事件。我在另一台Windows 7 64位计算机上测试了该项目,其行为与此相同。此处为Win7 x64。当且仅当选择>0项时,按钮才启用。我想指出,控制属性(如启用)的正确方法是通过操作和操作列表/管理器。尽管我同意在这种情况下,操作列表/管理器肯定是正确的方法(因此,在这种情况下,实际的LV问题变得无关紧要),在其他一些情况下,Q和A都非常有价值。例如,您经常希望在状态栏中显示类似“%d个选定项”的内容。@AndreasRejbrand我仍然会使用
TAction.OnUpdate
事件进行此类显示,例如:
procedure TForm3.MyActionUpdate(发件人:TObject);begin StatusBar1.Panels[0]。文本:=格式(“%d个选定项”,[ListView1.SelCount]);结束我通常不会那样做,但我同意这有明显的好处。
procedure TForm3.ListView1DataStateChange(Sender: TObject; StartIndex, EndIndex: Integer; OldState, NewState: TItemStates);
begin
  if (NewState * [isSelected]) <> (OldState * [isSelected]) then
    Button1.Enabled := ListView1.SelCount > 0;
end;
procedure TForm3.MyActionUpdate(Sender: TObject);
begin
  MyAction.Enabled := ListView1.SelCount > 0;
end;