2014-03-07 49 views
6

我需要啓用或禁用某個按鈕,具體取決於列表中是否至少選擇了一行。TListView SelCount報告虛擬列表中的項目數量錯誤

下面是重現此問題的代碼。該列表使用OnData事件填充,並允許選擇多行。

我認爲我可以使用OnSelectItem來檢測用戶何時更改選擇,然後使用TListView SelCount函數來檢測選定行的數量。

問題是,當用戶選擇多行時,SelCount返回0。如果手動填充列表(即不通過OnData事件),這可以正常工作。

任何想法?

感謝

更新:使用OnChange事件,而不是似乎這樣的伎倆。如果選擇多行(從SelectItem事件中),爲什麼SelCount返回0,仍然會很有趣。

另一個更新:我發佈一個測試項目:https://dl.dropboxusercontent.com/u/35370420/TestListView2.zip以及截圖:

enter image description here

重現此問題運行應用程序,選擇項目1,然後按Shift +單擊項目2。該按鈕被禁用。我的意圖是隻要列表中至少有一個項目被選中,就可以動態地啓用該按鈕。如果沒有選定的項目,該按鈕被禁用。

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 
+0

fyi - 我無法重現Delphi 7中的錯誤。 –

+0

-1在XE5中,問題中的代碼按預期工作,而不像您所描述的那樣。 –

+0

@DavidHeffernan:你運行過什麼操作系統?我正在使用最新更新的Windows 7 64位和XE5。您可以從這裏下載該項目:https://dl.dropboxusercontent.com/u/35370420/TestListView2.zip。我使用sysinternals DebugView來查看輸出。當我在列表中選擇多個項目時,只會觸發一個SelectItem事件並且SelCount爲0.如果我只選擇一個項目,則會觸發兩個事件。我在Windows 7 64位的另一臺計算機上測試了該項目,行爲相同。 – costa

回答

4

根本的問題是,當你SHIFT +點擊多個項目,您將無法獲得對具有項目的任何OnSelectItem事件成爲被選中。 SHIFT +點擊將導致所有列表視圖項目首先被取消選中,然後在選擇新項目之前觸發單個OnSelectItem事件,其中Item=nilSelected=False。在該事件發生時,TListView.SelCount確實爲0,因此您禁用了您的按鈕,但之後沒有進一步的OnSelectItem事件告訴您已選擇了新項目,因此您不需再次檢查SelCount以重新啓用該按鈕。

當單個項目在選定狀態和未選定狀態之間更改狀態時,或者整個ListView中的所有項目更改爲相同的選定/未選狀態時,將觸發OnSelectItem事件以回覆LVN_ITEMCHANGED通知。但是,在虛擬模式下,當連續多個項目同時更改爲相同狀態時,Windows可以爲該項目範圍發送單個LVN_ODSTATECHANGED通知。 TListLiew不會觸發OnSelectItem當它接收LVN_ODSTATECHANGED,它觸發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; 

所以你需要同時使用OnSelectItemOnDataStateChange來處理所有可能的選擇/取消選擇狀態的變化。

最好的解決方案是不要在個別項目狀態更改時手動啓用/禁用TButton。在窗體上放置一個TActionManager,創建一個新的TAction並將其分配給TButton.Action屬性,然後使用TAction.OnUpdate事件啓用/禁用基於當前TListView.SelCountTAction,如:

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

這將自動啓用/每次主消息隊列空閒時關閉相關聯的TButton,包括處理完ListView通知消息後。這樣,無論使用什麼輸入組合來選擇/取消選擇ListView項目,都可以保持更新TButton