cos*_*sta 7 delphi vcl tlistview delphi-xe5
我需要启用或禁用按钮,具体取决于列表中是否至少选择了一行.
以下是重现此问题的代码.使用OnData事件填充列表,它允许选择多行.
我以为我可以使用OnSelectItem来检测用户何时更改选择,然后使用TListView SelCount函数来检测所选行的数量.
问题是当用户选择多行时,SelCount返回0.如果手动填充列表(即不通过OnData事件),这可以正常工作.
有任何想法吗?
谢谢
更新:使用OnChange事件似乎可以解决问题.仍然有趣的是,当选择多行时(从SelectItem事件中),SelCount为什么返回0.
另一个更新:我发布了一个测试项目: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.
Run Code Online (Sandbox Code Playgroud)
形成:
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
Run Code Online (Sandbox Code Playgroud)
根本问题是,当您有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;
Run Code Online (Sandbox Code Playgroud)
因此,您需要同时使用OnSelectItem和OnDataStateChange处理所有可能的选择/取消选择状态更改。
最好的解决方案是不对TButton单个项目状态更改手动启用/禁用。TActionManager在窗体上放置一个,创建一个新的TAction并将其分配给该TButton.Action属性,然后使用该TAction.OnUpdate事件TAction基于当前启用/禁用该TListView.SelCount,例如:
procedure TForm3.MyActionUpdate(Sender: TObject);
begin
MyAction.Enabled := ListView1.SelCount > 0;
end;
Run Code Online (Sandbox Code Playgroud)
TButton每当主消息队列空闲时(包括在处理ListView通知消息之后),这将自动启用/禁用关联。这样,TButton无论使用哪种输入组合来选择/取消选择ListView项目,您都可以保持更新。