7

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

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

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

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

有任何想法吗?

谢谢

更新:使用 OnChange 事件似乎可以解决问题。理解为什么 SelCount 在选择多行时返回 0(从 SelectItem 事件中)仍然会很有趣。

另一个更新:我发布了一个测试项目:https ://dl.dropboxusercontent.com/u/35370420/TestListView2.zip以及截图:

在此处输入图像描述

要重现此问题,请运行应用程序,选择 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.

形式:

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
4

1 回答 1

5

根本问题是,当您使用SHIFT+Click多个项目时,您将不会收到OnSelectItem已被选中的项目的任何事件。导致所有列表视图项目首先被取消选择,在新项目被选中之前SHIFT+Click触发单个OnSelectItem事件Item=nil和。Selected=False在那个事件发生的时候,TListView.SelCount真的是 0,所以你禁用了你的按钮,但是没有进一步的OnSelectItem事件告诉你已经选择了新的项目,所以你不要SelCount再次检查以重新启用按钮。

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

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

因此,您需要同时使用OnSelectItemOnDataStateChange处理所有可能的选择/取消选择状态更改。

最好的解决方案是不要TButton手动启用/禁用单个项目状态更改。将 aTActionManager放在 Form 上,创建一个新的TAction并将其分配给TButton.Action属性,然后使用该TAction.OnUpdate事件来启用/禁用TAction基于 current 的TListView.SelCount,例如:

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

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

于 2014-03-07T20:05:25.990 回答