【问题标题】:Ownerdraw TListBox child controls are not moved by scrollingOwnerdraw TListBox 子控件不通过滚动移动
【发布时间】:2015-05-23 05:39:39
【问题描述】:
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
begin
  inherited;
  TListBox(Control).Canvas.FillRect(Rect);
  TListBox(Control).Canvas.TextOut(Rect.Left+5, Rect.Top+8, TListBox(Control).Items[Index]);
  if odSelected in State then
  begin
    Button.Left:=Rect.Right-80;
    Button.Top:=Rect.Top+4;
    Button.Visible:=true;
    Button.Invalidate;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.DoubleBuffered:=true;
  ListBox1.ItemHeight:=30;
  ListBox1.Style:=lbOwnerDrawFixed;
  Button:=TButton.Create(ListBox1);
  Button.Parent:=ListBox1;
  Button.DoubleBuffered:=true;
  Button.Visible:=false;
  Button.Width:=50;
  Button.Height:=20;
  Button.Caption:='BTN';
end;

重绘问题仅在使用 ScrollBar 或向我的 ListBox 发送 WM_VSCROLL 消息时存在。当我使用键盘箭头或鼠标单击更改选择时,所有内容通常都会绘制。当所选项目通过滚动可见并且不离开可见区域时,问题也不存在。

我认为 Button.Top 属性在 DrawItem 调用之前仍然具有旧值,并且稍后会更改(例如更改为 -30px)。

【问题讨论】:

  • TListBox 没有 OnScroll 事件。消息 WM_VSCROLL 只能在覆盖的 windpoc 中捕获

标签: delphi tlistbox


【解决方案1】:

问题是您正在使用OnDrawItem 事件来更改 UI(在这种情况下,定位按钮)。不要那样做,该活动仅用于绘画。

我建议你:

  1. 子类化 ListBox 以处理 WM_VSCROLL 消息并让您的消息处理程序根据需要重新定位按钮。

    var
      PrevListBoxWndProc: TWndMethod;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      PrevListBoxWndProc := ListBox1.WindowProc;
      ListBox1.WindowProc := ListBoxWndProc;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      ListBox1.WindowProc := PrevListBoxWndProc;
    end;
    
    procedure TForm1.PositionButton(Index: Integer);
    var
      R: TRect;
    begin
      if Index <= -1 then
        Button.Visible := False
      else
      begin 
        R := ListBox1.ItemRect(Index);
        Button.Left := R.Right - 80;
        Button.Top := R.Top + 4;
        Button.Visible := True;
      end;
    end;
    
    var
      LastIndex: Integer = -1;
    
    procedure TForm1.ListBox1Click(Sender: TObject);
    var
      Index: Integer;
    begin
      Index := ListBox1.ItemIndex;
      if Index <> LastIndex then
      begin
        LastIndex := Index;
        PositionButton(Index);
      end;
    end;
    
    procedure TForm1.ListBoxWndProc(var Message: TMessage);
    begin
      PrevListBoxWndProc(Message);
      if Message.Msg = WM_VSCROLL then
        PositionButton(ListBox1.ItemIndex);
    end;
    
  2. 完全摆脱TButton。使用OnDrawItem 将按钮的图像 (您可以使用DrawFrameControl()DrawThemeBackground() )直接绘制到ListBox 上,然后使用OnMouseDown/UpOnClick 事件来检查鼠标是否在“按钮”上,如果是,则根据需要采取相应的行动。

    var
      MouseX: Integer = -1;
      MouseY: Integer = -1;
    
    procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
        Rect: TRect; State: TOwnerDrawState);
    var
      R: TRect;
      P: TPoint;
      BtnState: UINT;
    begin
      TListBox(Control).Canvas.FillRect(Rect);
      TListBox(Control).Canvas.TextOut(Rect.Left+5, Rect.Top+8, TListBox(Control).Items[Index]);
      if not (odSelected in State) then Exit;
      R := Rect(Rect.Right-80, Rect.Top+4, Rect.Right-30, Rect.Top+24);
      P := Point(MouseX, MouseY);
      BtnState := DFCS_BUTTONPUSH;
      if PtInRect(R, P) then BtnState := BtnState or DFCS_PUSHED;
      DrawFrameControl(TListBox(Control).Canvas.Handle, R, DFC_BUTTON, BtnState);
      InflateRect(R, -4, -4);
      DrawText(TListBox(Control).Canvas.Handle, 'BTN', 3, R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
    end;
    
    procedure TForm1.ListBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      if Button <> mbLeft then Exit;
      MouseX := X;
      MouseY := Y;
      ListBox1.Invalidate;
    end;
    
    procedure TForm1.ListBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      if Button <> mbLeft then Exit;
      MouseX := -1;
      MouseY := -1;
      ListBox1.Invalidate;
    end;
    
    procedure TForm1.ListBox1Click(Sender: TObject);
    var
      P: TPoint;
      R: TRect;
      Index: Integer;
    begin
      P := Point(MouseX, MouseY);
      Index := ListBox1.ItemAtPos(P, True);
      if (Index = -1) or (Index <> ListBox1.ItemIndex) then Exit;
      R := ListBox1.ItemRect(Index);
      R := Rect(R.Right-80, R.Top+4, R.Right-30, R.Top+24);
      if not PtInRect(R, P) then Exit;
      // click is on selected item's "button", do something...
    end;
    

【讨论】:

    猜你喜欢
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-29
    相关资源
    最近更新 更多