鼠标悬停/结束时避免TListView绘图/更新

时间:2013-10-17 15:22:38

标签: delphi

我有TListView进行了一些修改。它包括每行的一些图标(几个,取决于项目),以及在满足某些条件时行的背景的可能性。

似乎渲染得很好。但是当我将鼠标移到窗口上时出现问题,似乎行正在重新渲染,这会产生不必要的延迟,更重要的是,它似乎会混淆可视化。如果我做某事(比如选择一行),它应该只重新绘制。

如何强行停止(鼠标悬停时看似令人耳目一新的行)?目前我正在使用AdvancedCustomDrawItem来绘制。窗口对选择的项目作出反应也需要一秒钟,这似乎很无聊。

基本上,每行都有DrawText()并在Sender.Canvas上绘制图像。这无疑是一个缓慢的进展,但它现在有效,如果它只是在我将鼠标悬停在它们上面时似乎没有重绘行!事实上,如果我使用Aero主题,当您将鼠标悬停在它们上方时,这些行会变黑。

以下是AdvancedCustomDrawItem上的事件代码:

procedure TfrmJobQueue.ListView1AdvancedCustomDrawItem(Sender: TCustomListView;
  Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
  var DefaultDraw: Boolean);
const
  DT_ALIGN: array[TAlignment] of integer = (DT_LEFT, DT_RIGHT, DT_CENTER);
var
  r: TRect;
  SL: TStringList;
  TypeName: string;
  I: Integer;
  TypeState: integer;
  x1,x2: Integer;
  S: string;
begin
  if Stage = cdPostPaint then begin
    // Ways I tried to avoid it; but failed.
    if cdsHot in State then
      exit;
    if cdsNearHot in State then
      exit;
    if cdsOtherSideHot in State then
      exit;
    if cdsMarked in State then
      exit;
    if cdsIndeterminate in State then
      exit;
    Sender.Canvas.Brush.Style := bsSolid;
    if FRepLines.Items[Item.Index].IsAutoReport then begin
      Sender.Canvas.Font.Color  := clBlack;
      Sender.Canvas.Brush.Color := clSkyBlue;
    end else begin
      Sender.Canvas.Font.Color  := clBlack;
      Sender.Canvas.Brush.Color := clWhite;
    end;
    if cdsSelected in State then begin
      Sender.Canvas.Font.Color  := clWhite;
      Sender.Canvas.Brush.Color := clNavy;
    end;
    R := Item.DisplayRect(drBounds);
    Sender.Canvas.FillRect(R);
    Sender.Canvas.Brush.Style := bsClear;
    if cdsFocused in State then
      DrawFocusRect(Sender.Canvas.Handle, R);
    x1 := 0;
    x2 := 0;
    for i := 0 to TListView(Sender).Columns.Count - 1 do
    begin
      inc(x2, Sender.Column[i].Width);
      r.Left := x1;
      r.Right := x2;
      if i = 0 then
    S := Item.Caption
      else
    S := Item.SubItems[i-1];
      if DT_ALIGN[Sender.Column[i].Alignment] = DT_LEFT then
    S := '  ' + S;
      DrawText(Sender.Canvas.Handle,
    S, length(S), r,
    DT_SINGLELINE or DT_ALIGN[Sender.Column[i].Alignment] or
      DT_VCENTER or DT_END_ELLIPSIS);
      x1 := x2;
    end;
    r := Item.DisplayRect(drIcon);
    SL := TStringList.Create;
    SL.CommaText := FRepLines.Value(Item.Index, 'TypeState');
    r.Left := Sender.Column[0].Width + Sender.Column[1].Width + Sender.Column[2].Width + Sender.Column[3].Width
      + Sender.Column[4].Width;
    for I := 0 to SL.Count - 1 do begin
      if GetTypeImagesIndex(SL.Names[I]) = -1 then
        continue;
      // FRepLines is a collection of items containing more information about each row.
      if FRepLines.Value(Item.Index, 'State') <> '1' then begin // no error
        TypeName := SL.Names[I];
        TypeState := StrToIntDef(SL.Values[TypeName], 0);
        // State*Images are TImageList.
        if TypeState = 0 then
          StateWaitingImages.Draw(Sender.Canvas, r.Left + 17*I, r.Top, 
            GetTypeImagesIndex(TypeName))
        else
          StateDoneImages.Draw(Sender.Canvas, r.Left + 17*I, r.Top, 
            GetTypeImagesIndex(TypeName));
        CreateIconToolTip(StrToIntDef(FRepLines.Value(Item.Index, 'RepJob'), -1), 
          TypeName, r.Left + 17*I, ListView1.ViewOrigin.Y + r.Top, 
          Format(TranslateString('RepQTypeState'),
          [TranslateString(Format('RepQTypeStateN%s', [TypeName])),
           TranslateString(Format('RepQTypeState-%d', [TypeState]))]));
      end;
    end;
  end;
end;

代码的一些解释:

列表是报告列表(报告队列)。我正在介绍“AutoReports”(或UI中的预定报告)的概念,我想用浅蓝色背景(clSkyBlue)突出显示。

除了该背景,它还在状态列上绘制了一些图标,指示报告所处的阶段,以及报告的格式(PDF,Excel和HTML等格式),以及是否已打印和/或通过电子邮件发送。如果此类事件已已订购,则仅显示图标,因此图标数量可变。

等待状态图像是完成状态图像的灰色版本。我还尝试创建一些代码,因此当我将鼠标悬停在特定图标上时,它会显示一个工具提示消息。

因为代码速度相当迟钝,我怀疑我做的事情非常糟糕。

1 个答案:

答案 0 :(得分:3)

可能已启用HotTracking。这会导致项目在被鼠标悬停时重绘,因此鼠标下的项目可以以不同方式呈现。绘图时你可能忽略了hottrack状态。这可能是造成黑暗的原因。

您应该分析您的代码以找到真正的瓶颈。绘图代码需要快速。我在ListView中做了很多自定义绘图,它的行为并不像你描述的那样慢。

更新:考虑重新编写代码以在OnAdvancedCustomDrawSubItem事件中绘制单个列,而不是在OnAdvancedCustomDrawItem事件中执行所有操作。此外,您不需要手动计算每个列的边界,而是可以使用ListView_GetSubItemRect()。最后,您泄露了TStringList