Игнорировать щелчок по флажку TListView

У меня есть TListview, где я хочу использовать флажки, чтобы указать, произошло ли событие с элементом в списке.

Я могу прочитать и установить статус флажка, но я действительно хочу отключить возможность пользователя изменять статус с помощью щелчка мыши.

Для TCheckList я могу установить обратное состояние checked с помощью OnClickCheck.

То же самое не работает для TListview. В этот момент я вижу, что флажок установлен в OnMouseDown, но не могу отключить прохождение клика.

procedure TMF.ListViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
   MyHitTest : THitTests;
begin
   MyHitTest := (Sender as TListView).GetHitTestInfoAt(X,Y);
   if htOnStateIcon in MyHitTest then
      (Sender as TListView).OnMouseDown := nil;
end;

Предложения?


person Dan Kelly    schedule 28.10.2013    source источник
comment
Помогает ли установка для свойства Enabled TListView значения false?   -  person Keith Miller    schedule 28.10.2013
comment
Неа. Мне нужно, чтобы элементы были доступны для выбора по отдельности (я использую Rowselect), просто состояние флажков должно быть под моим контролем   -  person Dan Kelly    schedule 28.10.2013
comment
Вы боретесь с системой здесь. Оно не хочет позволять вам делать это. На вашем месте я бы искал решение, не связанное со встроенными флажками.   -  person David Heffernan    schedule 28.10.2013
comment
Я не совсем понимаю. У меня нет проблем с добавлением и удалением флажков с кодом, пока пользователь не может (не) проверить ОТКЛЮЧЕННЫЙ TListView. Что такое ViewStyle вашего списка? Кроме того, есть события OnChange и OnChanging, которые выглядят именно так, как вы хотите.   -  person Günther the Beautiful    schedule 28.10.2013
comment
@Gunther Я использую vsReport - я предполагаю, что и вы, и Дэвид предлагаете мне установить собственные флажки, а не использовать флажки по умолчанию. Оба из них отвечают на мой вопрос, что нет никакого способа сделать это как отправлено.   -  person Dan Kelly    schedule 28.10.2013
comment
Я бы не стал выкатывать собственные галочки. Я бы просто добавил еще один столбец и сообщил о статусе в текстовом виде в этом столбце. Возможно, вы могли бы использовать символ Unicode для галочки: ✓   -  person David Heffernan    schedule 28.10.2013
comment
Я все еще не вижу проблемы. У вас есть TListView с CheckBoxes=True и ViewStyle=vsReport. Вы хотите иметь возможность устанавливать флажки по коду, но список должен оставаться доступным пользователю только для чтения. Верно? Вы можете использовать событие OnChanging или OnChange, чтобы вернуть состояние Checked TListItems к тому, каким вы хотите его видеть. Таким образом, вы даже можете сделать так, чтобы только определенные флажки отображались только для чтения.   -  person Günther the Beautiful    schedule 29.10.2013
comment
@GünthertheBeautiful Я провел небольшой тест, и проблема с OnChange заключается в том, что он вызывается, даже если вы не меняете элемент (Delphi XE). Пример, когда пользователь использует клавиши со стрелками на клавиатуре для перехода от одного элемента к другому. Не уверен, что это жесткая ошибка.   -  person EMBarbosa    schedule 29.10.2013


Ответы (3)


Используйте событие Onchanging и установите AllowChange в False.

procedure TForm1.ListView1Changing(Sender: TObject; Item: TListItem;
  Change: TItemChange; var AllowChange: Boolean);
begin
  AllowChange := False;
end;

Обновление: OP хочет, чтобы пользователь мог выбрать элемент. Так что, возможно, небольшой хак с использованием события OnItemChecked может помочь.

procedure TForm1.ListView1ItemChecked(Sender: TObject; Item: TListItem);
begin
  if TComponent(Sender).Tag = 0 then
  begin
    TComponent(Sender).Tag := 1;
    Item.Checked := not Item.Checked;
    TComponent(Sender).Tag := 0;
  end;
end;

Обновление 2: проблема с использованием этого трюка заключается в том, что вы должны отключить его, прежде чем изменять статус любого элемента. Например:

Procedure LoadListViewItems;
begin
  //Let's permit modification in ListView Items.
  ListView1.OnItemChecked := nil;
  try
    //put Load Items code Here!
  finally
    //User cannot change Items statuses 
    ListView1.OnItemChecked := ListView1ItemChecked;
  end;
end;
person EMBarbosa    schedule 28.10.2013
comment
Вы должны проверить параметр Change, прежде чем слепо запрещать все изменения: if Change = ctState then AllowChange := False; - person Remy Lebeau; 29.10.2013
comment
@RemyLebeau, кроме того, это был только один пример, я не понимаю, почему. Если он не хочет вносить изменения в любое время, какая разница? - person EMBarbosa; 29.10.2013
comment
Это было настоящей головной болью для меня. OP, возможно, хотел, чтобы это было постоянным правилом, в то время как я хотел, чтобы это было в режиме только для чтения моего приложения, где пользователь проверяет, с какими элементами он хочет что-то сделать, а затем нажимает кнопку, которая перебирает все элементы и делает это что-то, в течение которого пользователю не разрешено переключать эти флажки. Так что я получил гораздо больше кода, чем вы. - person Jerry Dodge; 14.06.2019
comment
@JerryDodge Да! Это, как сказал Дэвид в комментарии, борьба с системой. Поэтому для выполнения простых вещей требуется больше кода. :( - person EMBarbosa; 19.06.2019

Вы можете подключить оконный процесс, чтобы принудительно проверить состояние элемента до того, как произойдет какая-либо обработка событий VCL:

  TForm1 = class(TForm)
    ...
  private
    fLVWndProc: TWndProc;
  end;


  procedure TForm1.FormCreate(Sender: TObject);
  begin
    // Save the original window proc and install the hook

    fLVWndProc := Listview1.WindowProc;
    Listview1.WindowProc := LVWndProcHook;
  end;



  procedure TForm1.LVWndProcHook(var aMessage: TMessage) ;
  var
    notify: PNMListView;
    bItemState: Boolean;
  begin
    if (aMessage.Msg = CN_NOTIFY)
     and (PNMHdr(aMessage.LParam).Code = LVN_ITEMCHANGED) then
    begin
      notify := PNMListView(aMessage.LParam);

      if ((notify.uChanged and LVIF_STATE) <> 0) then
      begin
        // Determine actual item state and re-apply it before continuing
        bItemState := GetUnderlyingItemState(notify.iItem);
        ListView_SetCheckState(notify.hdr.hwndFrom, notify.iItem, bItemState);
      end;
    end;

    //original ListView message handling
    fLVWndProc(aMessage) ;
  end;
person Deltics    schedule 29.10.2013

Или вы можете сделать так:

procedure TForm1.ListItemChecked(Sender: TObject; Item: TListItem);
begin
 if not CheckBoxesEnabled then begin
  List.OnItemChecked:=nil;
  Item.Checked:=not Item.Checked;
  List.OnItemChecked:=ListItemChecked;
 end;
end;

List — это ваш TListView, а CheckBoxesEnabled — логическая переменная, которая включает или отключает флажки.

person Marus Nebunu    schedule 25.05.2017