Question

I have a TListview where I want to use the checkboxes to indicate whether an event has happened to an item in the list.

I can read and set the checkbox status, but what I really want to do is disable the ability of the user to change the status using a mouse click.

For a TCheckList I can set the checked state to the inverse using OnClickCheck

The same doesn't work for a TListview. At them moment I can see that the checkbox has been targeted in OnMouseDown but can't disable the click from going through..

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;

Suggestions?

Was it helpful?

Solution

Use the event Onchanging and set AllowChange to False.

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

Update: OP want the user to be able to select the item. So, maybe, a little hack using OnItemChecked event can do.

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;

Update2: The problem using this trick is that you must disable it before you change any item status. For example:

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;

OTHER TIPS

You could hook the window proc to force the item checked state before any VCL event handling takes place:

  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;

Or you can do like this:

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 is your TListView, and CheckBoxesEnabled a boolean variable that enable or disable the checkboxes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top