0

我在 Delphi 中同时使用所有者绘图和数据列表视图,如果我在第一次以编程方式更改所选行后立即使用 shift 箭头选择,我注意到一个奇怪的问题。

考虑以下窗口,我尝试用最少的代码显示问题:

Shift Down 选择搞砸了

这是复制问题的最小 Delphi 代码:

unit Main;

//--------------------------------------------------------------------------------------------------
//    I N T E R F A C E
//--------------------------------------------------------------------------------------------------

interface uses Classes,
               ComCtrls,
               Controls,
               Dialogs,
               ExtCtrls,
               Forms,
               Graphics,
               Messages,
               StdCtrls,
               SysUtils,
               Variants,
               Windows;

//--------------------------------------------------------------------------------------------------
//    T Y P E      D E F I N I T I O N S
//--------------------------------------------------------------------------------------------------

type TMainForm = class(TForm)

    listView             : TListView;

    bottomPanel          : TPanel;

        position10Button : TButton;

    procedure FormCreate(
                  sender : TObject);

    //----------------------------------------------------------------------------------------------
    //    LIST VIEW EVENT HANDLERS
    //----------------------------------------------------------------------------------------------

    procedure ListViewData(
                  sender : TObject;
                  item   : TListItem);

    procedure ListViewDrawItem(
                  sender : TCustomListView;
                  item   : TListItem;
                  rect   : TRect;
                  state  : TOwnerDrawState);

    //----------------------------------------------------------------------------------------------
    //    POSITION BUTTON HANDLER
    //----------------------------------------------------------------------------------------------

    procedure Position10ButtonClick(
                  sender : TObject);

private

    //----------------------------------------------------------------------------------------------
    //    WINDOWS MESSAGE HANDLERS
    //----------------------------------------------------------------------------------------------

    procedure WMMeasureItem(
                  var msg : TWMMeasureItem);    message WM_MEASUREITEM;

private

    //----------------------------------------------------------------------------------------------
    //    DRAWING
    //----------------------------------------------------------------------------------------------

    procedure DrawHighlightRect(
                  canvas : TCanvas;
                  rect   : TRect;
                  color  : TColor);

end;

//--------------------------------------------------------------------------------------------------
//    G L O B A L     V A R I A B L E S
//--------------------------------------------------------------------------------------------------

var MainForm : TMainForm;

//--------------------------------------------------------------------------------------------------
//    I M P L E M E N T A T I O N
//--------------------------------------------------------------------------------------------------

implementation uses CommCtrl;

{$R *.dfm}

//--------------------------------------------------------------------------------------------------
//    F O R M     C R E A T E
//--------------------------------------------------------------------------------------------------

procedure TMainForm.FormCreate(
                        sender : TObject);
begin
  //  Set double buffering for listview.

  listView.doubleBuffered := TRUE;

  //  Set listview count: 20 lines.

  listView.items.count := 20;

  //  Set focus on listview.

  WINDOWS.SetFocus(
              listView.handle);
end;

//--------------------------------------------------------------------------------------------------
//    FORM CONTROLS EVENT HANDLERS
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//    LIST VIEW EVENT HANDLERS
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//    L I S T V I E W     D A T A
//--------------------------------------------------------------------------------------------------

procedure TMainForm.ListViewData(
                        sender : TObject;
                        item   : TListItem);

begin
  if item = NIL then EXIT;

  item.caption := SYSUTILS.IntToStr(item.index);
end;

//--------------------------------------------------------------------------------------------------
//    L I S T V I E W     D R A W     I T E M
//--------------------------------------------------------------------------------------------------

procedure TMainForm.ListViewDrawItem(
                        sender : TCustomListView;
                        item   : TListItem;
                        rect   : TRect;
                        state  : TOwnerDrawState);

  const TEXT_MARGIN = 7;

  var drawRect : TRect;

begin
  //  Draw focus rectangle for selected item.

  if item.selected then
    begin
      drawRect := rect;

      Inc( drawRect.top,   1);
      Dec( drawRect.bottom,1);

      DrawHighlightRect(
          sender.canvas,
          drawRect,
          clBlack);
    end;

  //  Prepare brush to draw text.

  sender.canvas.brush.style := bsClear;

  //  Draw text.

  drawRect       := rect;
  drawRect.left  := TEXT_MARGIN;

  WINDOWS.DrawText(
              sender.canvas.handle,
              PCHAR(item.caption),
              Length( item.caption),
              drawRect,
              DT_SINGLELINE or
              DT_LEFT       or
              DT_VCENTER);
end;

//--------------------------------------------------------------------------------------------------
//    P O S I T I O N     1 0     B U T T O N     C L I C K
//--------------------------------------------------------------------------------------------------

procedure TMainForm.Position10ButtonClick(
                        sender : TObject);
begin
  WINDOWS.SetFocus(
              listView.handle);

  //  Unselect all.

  listView.ClearSelection;

  //  Select and focus line 10.

  listview.items[10].selected := TRUE;
  listview.items[10].focused  := TRUE;
end;

//--------------------------------------------------------------------------------------------------
//    WINDOWS MESSAGE HANDLERS
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//    W M     M E A S U R E     I T E M
//--------------------------------------------------------------------------------------------------

procedure TMainForm.WMMeasureItem(
                        var msg : TWMMeasureItem);
begin
  inherited;

  //  Set height of list view items.

  if msg.IDCtl = listView.handle then msg.measureItemStruct^.itemHeight := 25;
end;

//--------------------------------------------------------------------------------------------------
//    D R A W     H I G H L I G H T     R E C T
//--------------------------------------------------------------------------------------------------

procedure TMainForm.DrawHighlightRect(
                        canvas : TCanvas;
                        rect   : TRect;
                        color  : TColor);

  var topLeft              : TPoint;
  var topRight             : TPoint;
  var bottomRight          : TPoint;
  var bottomLeft           : TPoint;

begin
  //  Prepare pen.

  canvas.pen.style := psSolid;
  canvas.pen.width := 1;
  canvas.pen.mode  := pmCopy;

  //  Compute outer rectangle points.

  topLeft.x     := rect.left;
  topLeft.y     := rect.top;

  topRight.x    := rect.right;
  topRight.y    := rect.top;

  bottomRight.x := rect.right;
  bottomRight.y := rect.bottom;

  bottomLeft.x  := rect.left;
  bottomLeft.y  := rect.bottom;

  //  Draw rectangle.

  canvas.pen.color := color;

  canvas.PolyLine( [ topLeft, topRight, bottomRight, bottomLeft, topLeft]);

  //  Compute inner rectangle points.

  topLeft.x     := rect.left   + 1;
  topLeft.y     := rect.top    + 1;

  topRight.x    := rect.right  - 1;
  topRight.y    := rect.top    + 1;

  bottomRight.x := rect.right  - 1;
  bottomRight.y := rect.bottom - 1;

  bottomLeft.x  := rect.left   + 1;
  bottomLeft.y  := rect.bottom - 1;

  //  Draw rectangle.

  canvas.pen.color := color;

  canvas.PolyLine( [ topLeft, topRight, bottomRight, bottomLeft, topLeft]);
end;

//--------------------------------------------------------------------------------------------------

end.

[编辑] 正如 Andreas Rejbrand 所指出的,非所有者绘制非所有者数据列表视图也存在问题。

unit Main;

//--------------------------------------------------------------------------------------------------
//    I N T E R F A C E
//--------------------------------------------------------------------------------------------------


interface uses Classes,
               ComCtrls,
               Controls,
               Dialogs,
               ExtCtrls,
               Forms,
               Graphics,
               Messages,
               StdCtrls,
               SysUtils,
               Variants,
               Windows;

//--------------------------------------------------------------------------------------------------
//    T Y P E      D E F I N I T I O N S
//--------------------------------------------------------------------------------------------------

type TMainForm = class(TForm)

    listView             : TListView;

    bottomPanel          : TPanel;

        position10Button : TButton;

    procedure FormCreate(
                  sender : TObject);

    //----------------------------------------------------------------------------------------------
    //    POSITION BUTTON HANDLER
    //----------------------------------------------------------------------------------------------

    procedure Position10ButtonClick(
                  sender : TObject);

end;

//--------------------------------------------------------------------------------------------------
//    G L O B A L     V A R I A B L E S
//--------------------------------------------------------------------------------------------------

var MainForm : TMainForm;

//--------------------------------------------------------------------------------------------------
//    I M P L E M E N T A T I O N
//--------------------------------------------------------------------------------------------------

implementation uses CommCtrl;

{$R *.dfm}

//--------------------------------------------------------------------------------------------------
//    F O R M     C R E A T E
//--------------------------------------------------------------------------------------------------

procedure TMainForm.FormCreate(
                        sender : TObject);

  var index   : integer;
  var newItem : TListItem;

begin
  //  Set double buffering for listview.

  listView.doubleBuffered := TRUE;

  for index := 0 to 19 do
    begin
      newItem := listview.items.Add;
      newItem.caption := SYSUTILS.IntToStr( index);
    end;

  //  Set focus on listview.

  WINDOWS.SetFocus(
              listView.handle);
end;

//--------------------------------------------------------------------------------------------------
//    FORM CONTROLS EVENT HANDLERS
//--------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------
//    P O S I T I O N     1 0     B U T T O N     C L I C K
//--------------------------------------------------------------------------------------------------

procedure TMainForm.Position10ButtonClick(
                        sender : TObject);
begin
  WINDOWS.SetFocus(
              listView.handle);

  //  Unselect all.

  listView.ClearSelection;

  //  Select and focus line 10.

  listview.items[10].selected := TRUE;
  listview.items[10].focused  := TRUE;
end;

//--------------------------------------------------------------------------------------------------

end.
4

2 回答 2

0

在您的ListViewDrawItem()处理程序中,if item.selected then应该是if odSelected in state then

而且,在您的Position10ButtonClick()处理程序中,您正在设置特定项目SelectedFocused属性,但您需要在OnData事件中执行这些分配,而您目前没有这样做。您需要将选择详细信息保存在某处,然后在OnData事件中应用该信息。当用户对当前选择进行更改时,您还需要处理该OnSelectItem事件并保存它为您提供的详细信息。

尝试这样的事情:

type
  MyListItemInfo = record
    Caption: String;
    Selected: Boolean;
    Focused: Boolean;
  end;

private
  MyListItems: array of MyListItemInfo;

procedure TMainForm.FormCreate(
                        Sender : TObject);
var
  I: Integer;
begin
  //  Set double buffering for listview.

  ListView.DoubleBuffered := True;

  //  Set listview count: 20 lines.

  SetLength(MyListItems, 20);

  for I := Low(MyListItems) to High(MyListItems) do
  begin
    MyListItems[I].Caption := SysUtils.IntToStr(I);
    MyListItems[I].Selected := False;
    MyListItems[I].Focused := False;
  end;

  ListView.Items.Count := Length(MyListItems);

  //  Set focus on listview.

  ListView.SetFocus;
end;

procedure TMainForm.ListViewSelectItem(
                        Sender  : TObject;
                        Item    : TListItem;
                        Selected: Boolean);
var
  I: Integer;
begin
  if Item <> nil then
  begin
    MyListItems[Item.Index].Selected := Selected;
    ListView.UpdateItems(Item.Index, Item.Index);
  end else
  begin
    for I := 0 to listView.Items.Count-1 do
      MyListItems[I].Selected := Selected;
    ListView.Invalidate;
  end;
end;

procedure TMainForm.ListViewData(
                        Sender : TObject;
                        Item   : TListItem);

begin
  Item.Caption := MyListItems[Item.Index].Caption;
  Item.Selected := MyListItems[Item.Index].Selected;
  item.Focused := MyListItems[Item.Index].Focused;
end;

procedure TMainForm.Position10ButtonClick(
                        Sender : TObject);
var
  I: Integer;
begin
  ListView.SetFocus;

  //  Unselect all.

  ListView.ClearSelection;

  for I := Low(MyListItems) to High(MyListItems) do
  begin
    MyListItems[I].Selected := False;
    MyListItems[I].Focused := False;
  end;

  //  Select and focus line 10.

  MyListItems[10].Selected := True;
  MyListItems[10].Focused := True;

  ListView.Invalidate;
end;
于 2020-05-21T18:51:04.553 回答
0

请注意,您的 Q 中的“最小”示例包含许多不必要的代码。您可以在没有所有者图纸和所有者数据的情况下重现此问题。只需在表单上放置一个新TListView控件,在 IDE 中添加一些项目,然后设置MultiSelectTrue. (*)

现在,诀窍是使用LVM_SETSELECTIONMARK消息或ListView_SetSelectionMark函数(在 Delphi 中):

ListView1.ClearSelection;
ListView1.ItemIndex := 10;
ListView_SetSelectionMark(ListView1.Handle, 10)

(*) 而且,当然,您需要启用双缓冲以避免所有可怕的视觉故障。

于 2020-05-21T18:54:04.213 回答