2010-02-04 10 views
8

¿Cómo puedo implementar un botón de cerrar para un TTabsheet de un TPageControl como Firefox?Cómo implementar un botón de cerrar para un TTabsheet de un TPageControl

Editar:
Delphi Versión: Delphi 2010
SO: Windows XP y hasta

+0

versiones delphi? – ulrichb

+0

¿A qué estilo te refieres? ¿En el que cada pestaña tiene su propio botón o en el estilo donde hay un botón en el extremo derecho que se aplica a la pestaña que está activa actualmente? –

+0

@Rob Sí, con un botón de cerrar en cada pestaña –

Respuesta

31

Ahora con soporte temático (Windows, UxTheme, Themes incluyen unidades)!

type 
    TFormMain = class(TForm) 
    {...} 
    private 
    FCloseButtonsRect: array of TRect; 
    FCloseButtonMouseDownIndex: Integer; 
    FCloseButtonShowPushed: Boolean; 
    {...} 
    end; 

{...} 

procedure TFormMain.FormCreate(Sender: TObject); 
var 
    I: Integer; 
begin 
    PageControlCloseButton.TabWidth := 150; 
    PageControlCloseButton.OwnerDraw := True; 

    //should be done on every change of the page count 
    SetLength(FCloseButtonsRect, PageControlCloseButton.PageCount); 
    FCloseButtonMouseDownIndex := -1; 

    for I := 0 to Length(FCloseButtonsRect) - 1 do 
    begin 
    FCloseButtonsRect[I] := Rect(0, 0, 0, 0); 
    end; 
end; 

procedure TFormMain.PageControlCloseButtonDrawTab(Control: TCustomTabControl; 
    TabIndex: Integer; const Rect: TRect; Active: Boolean); 
var 
    CloseBtnSize: Integer; 
    PageControl: TPageControl; 
    TabCaption: TPoint; 
    CloseBtnRect: TRect; 
    CloseBtnDrawState: Cardinal; 
    CloseBtnDrawDetails: TThemedElementDetails; 
begin 
    PageControl := Control as TPageControl; 

    if InRange(TabIndex, 0, Length(FCloseButtonsRect) - 1) then 
    begin 
    CloseBtnSize := 14; 
    TabCaption.Y := Rect.Top + 3; 

    if Active then 
    begin 
     CloseBtnRect.Top := Rect.Top + 4; 
     CloseBtnRect.Right := Rect.Right - 5; 
     TabCaption.X := Rect.Left + 6; 
    end 
    else 
    begin 
     CloseBtnRect.Top := Rect.Top + 3; 
     CloseBtnRect.Right := Rect.Right - 5; 
     TabCaption.X := Rect.Left + 3; 
    end; 

    CloseBtnRect.Bottom := CloseBtnRect.Top + CloseBtnSize; 
    CloseBtnRect.Left := CloseBtnRect.Right - CloseBtnSize; 
    FCloseButtonsRect[TabIndex] := CloseBtnRect; 

    PageControl.Canvas.FillRect(Rect); 
    PageControl.Canvas.TextOut(TabCaption.X, TabCaption.Y, PageControl.Pages[TabIndex].Caption); 

    if not UseThemes then 
    begin 
     if (FCloseButtonMouseDownIndex = TabIndex) and FCloseButtonShowPushed then 
     CloseBtnDrawState := DFCS_CAPTIONCLOSE + DFCS_PUSHED 
     else 
     CloseBtnDrawState := DFCS_CAPTIONCLOSE; 

     Windows.DrawFrameControl(PageControl.Canvas.Handle, 
     FCloseButtonsRect[TabIndex], DFC_CAPTION, CloseBtnDrawState); 
    end 
    else 
    begin 
     Dec(FCloseButtonsRect[TabIndex].Left); 

     if (FCloseButtonMouseDownIndex = TabIndex) and FCloseButtonShowPushed then 
     CloseBtnDrawDetails := ThemeServices.GetElementDetails(twCloseButtonPushed) 
     else 
     CloseBtnDrawDetails := ThemeServices.GetElementDetails(twCloseButtonNormal); 

     ThemeServices.DrawElement(PageControl.Canvas.Handle, CloseBtnDrawDetails, 
     FCloseButtonsRect[TabIndex]); 
    end; 
    end; 
end; 

procedure TFormMain.PageControlCloseButtonMouseDown(Sender: TObject; 
    Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
var 
    I: Integer; 
    PageControl: TPageControl; 
begin 
    PageControl := Sender as TPageControl; 

    if Button = mbLeft then 
    begin 
    for I := 0 to Length(FCloseButtonsRect) - 1 do 
    begin 
     if PtInRect(FCloseButtonsRect[I], Point(X, Y)) then 
     begin 
     FCloseButtonMouseDownIndex := I; 
     FCloseButtonShowPushed := True; 
     PageControl.Repaint; 
     end; 
    end; 
    end; 
end; 

procedure TFormMain.PageControlCloseButtonMouseMove(Sender: TObject; 
    Shift: TShiftState; X, Y: Integer); 
var 
    PageControl: TPageControl; 
    Inside: Boolean; 
begin 
    PageControl := Sender as TPageControl; 

    if (ssLeft in Shift) and (FCloseButtonMouseDownIndex >= 0) then 
    begin 
    Inside := PtInRect(FCloseButtonsRect[FCloseButtonMouseDownIndex], Point(X, Y)); 

    if FCloseButtonShowPushed <> Inside then 
    begin 
     FCloseButtonShowPushed := Inside; 
     PageControl.Repaint; 
    end; 
    end; 
end; 

procedure TFormMain.PageControlCloseButtonMouseLeave(Sender: TObject); 
var 
    PageControl: TPageControl; 
begin 
    PageControl := Sender as TPageControl; 
    FCloseButtonShowPushed := False; 
    PageControl.Repaint; 
end; 

procedure TFormMain.PageControlCloseButtonMouseUp(Sender: TObject; 
    Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
var 
    PageControl: TPageControl; 
begin 
    PageControl := Sender as TPageControl; 

    if (Button = mbLeft) and (FCloseButtonMouseDownIndex >= 0) then 
    begin 
    if PtInRect(FCloseButtonsRect[FCloseButtonMouseDownIndex], Point(X, Y)) then 
    begin 
     ShowMessage('Button ' + IntToStr(FCloseButtonMouseDownIndex + 1) + ' pressed!'); 

     FCloseButtonMouseDownIndex := -1; 
     PageControl.Repaint; 
    end; 
    end; 
end; 

se parece a:

page control with buttons

+0

Se ve muy bien. ¡La captura de pantalla es un buen toque! Desafortunadamente, parece solo adecuado para Windows 2000 o XP no-temático? Vista/7. ¿Tienes una versión que usa la API de tematización? –

+1

@David M: hecho. – ulrichb

+0

He probado su código y funciona bien. Entonces selecciono tu código como respuesta. Pero debido a otras características que necesito, decido usar una TJvTabBar de Jedi JVCL con una pequeña modificación de TJvTabBarXPPainter. –

3

Lo que he hecho en el pasado es poner un TBitBtn con un gráfico en la esquina superior derecha de la TPageControl. El truco es que el padre del TBitBtn es el mismo que el TPageControl, por lo que no está realmente en una de las hojas de pestañas. Luego, en el clic, incluso para ese botón:

PageControl1.ActivePage.Free; 

Cuando se libera el TTabControl actual Notifica al TPageControl que lo posee.

6

A menudo es una buena idea implementar esto usted mismo, como han sugerido las otras respuestas. Por si acaso ya está usando Raize Components, esta característica es compatible "de fábrica". Simplemente configure TRzPageControl.ShowCloseButtonOnActiveTab := true y maneje el evento OnClose. El componente se encarga de la ubicación para una variedad de diseños/orientaciones/formas/colores de pestaña.

[sólo un cliente feliz]

0

He cambiado un poco este ejemplo: - creado clase TCloseTabSheet - esta clase tiene la propiedad OnClose: TNotifyEvent, que se llamará si se ha asignado - si TabSheet de de TPageControl no es esa clase, entonces no hay un botón de cierre - si es entonces el botón mostró. Cuando se pulsa el botón de cierre que llama OnClose - Ahora no necesitas para controlar la matriz FCloseButtonsRect, porque esto rects almacenado a TCloseTabSheet

unit Unit1; 

interface 

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

type TCloseTabSheet=class(TTabSheet) 
    private 
    protected 
    FCloseButtonRect: TRect; 
    FOnClose: TNotifyEvent; 
    procedure DoClose; virtual; 
    public 
    constructor Create(AOwner:TComponent); override; 
    destructor Destroy; override; 
property OnClose:TNotifyEvent read FOnClose write FOnClose; 
end; 

type 
    TMainForm = class(TForm) 
    PageControlCloseButton: TPageControl; 
    TabSheet1: TTabSheet; 
    TabSheet2: TTabSheet; 
    TabSheet3: TTabSheet; 
    procedure FormCreate(Sender: TObject); 
    procedure PageControlCloseButtonDrawTab(Control: TCustomTabControl; TabIndex: Integer; 
     const Rect: TRect; Active: Boolean); 
    procedure PageControlCloseButtonMouseDown(Sender: TObject; 
     Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
    procedure PageControlCloseButtonMouseMove(Sender: TObject; 
     Shift: TShiftState; X, Y: Integer); 
    procedure PageControlCloseButtonMouseLeave(Sender: TObject); 
    procedure PageControlCloseButtonMouseUp(Sender: TObject; 
     Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
    procedure CloseTabeProc(Sender: TObject); 
    private 
    FCloseButtonMouseDownTab: TCloseTabSheet; 
    FCloseButtonShowPushed: Boolean; 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    MainForm: TMainForm; 

implementation 

{$R *.dfm} 

constructor TCloseTabSheet.Create(AOwner:TComponent); 
begin 
    inherited Create(AOwner); 
    FCloseButtonRect:=Rect(0, 0, 0, 0); 
end; 

destructor TCloseTabSheet.Destroy; 
begin 
    inherited Destroy; 
end; 

procedure TCloseTabSheet.DoClose; 
begin 
    if Assigned(FOnClose) then FOnClose(Self); 
    Free; 
end; 

procedure TMainForm.CloseTabeProc(Sender: TObject); 
begin 
    ShowMessage('close'); 
end; 

procedure TMainForm.FormCreate(Sender: TObject); 
var I: Integer; 
    NT:TCloseTabSheet; 
begin 
    PageControlCloseButton.TabWidth := 150; 
    PageControlCloseButton.OwnerDraw := True; 
    NT:=TCloseTabSheet.Create(PageControlCloseButton); 
    NT.Caption:='TabSheet4'; 
    NT.PageControl:=PageControlCloseButton; 
    NT.OnClose:=CloseTabeProc; 

    FCloseButtonMouseDownTab := nil; 
end; 

procedure TMainForm.PageControlCloseButtonDrawTab(Control: TCustomTabControl; 
    TabIndex: Integer; const Rect: TRect; Active: Boolean); 
var 
    CloseBtnSize: Integer; 
    PageControl: TPageControl; 
    TabSheet:TCloseTabSheet; 
    TabCaption: TPoint; 
    CloseBtnRect: TRect; 
    CloseBtnDrawState: Cardinal; 
    CloseBtnDrawDetails: TThemedElementDetails; 
begin 
    PageControl := Control as TPageControl; 
    TabCaption.Y := Rect.Top + 3; 

    if Active then 
    begin 
    CloseBtnRect.Top := Rect.Top + 4; 
    CloseBtnRect.Right := Rect.Right - 5; 
    TabCaption.X := Rect.Left + 6; 
    end 
    else 
    begin 
    CloseBtnRect.Top := Rect.Top + 3; 
    CloseBtnRect.Right := Rect.Right - 5; 
    TabCaption.X := Rect.Left + 3; 
    end; 
    if PageControl.Pages[TabIndex] is TCloseTabSheet then 
    begin 
    TabSheet:=PageControl.Pages[TabIndex] as TCloseTabSheet; 
    CloseBtnSize := 14; 

    CloseBtnRect.Bottom := CloseBtnRect.Top + CloseBtnSize; 
    CloseBtnRect.Left := CloseBtnRect.Right - CloseBtnSize; 
    TabSheet.FCloseButtonRect := CloseBtnRect; 

    PageControl.Canvas.FillRect(Rect); 
    PageControl.Canvas.TextOut(TabCaption.X, TabCaption.Y, 
      PageControl.Pages[TabIndex].Caption); 

    if not ThemeServices.ThemesEnabled then 
    begin 
     if (FCloseButtonMouseDownTab = TabSheet) and FCloseButtonShowPushed then 
     CloseBtnDrawState := DFCS_CAPTIONCLOSE + DFCS_PUSHED 
     else 
     CloseBtnDrawState := DFCS_CAPTIONCLOSE; 

     Windows.DrawFrameControl(PageControl.Canvas.Handle, 
     TabSheet.FCloseButtonRect, DFC_CAPTION, CloseBtnDrawState); 
    end 
    else 
    begin 
     Dec(TabSheet.FCloseButtonRect.Left); 

     if (FCloseButtonMouseDownTab = TabSheet) and FCloseButtonShowPushed then 
     CloseBtnDrawDetails := ThemeServices.GetElementDetails(twCloseButtonPushed) 
     else 
     CloseBtnDrawDetails := ThemeServices.GetElementDetails(twCloseButtonNormal); 

     ThemeServices.DrawElement(PageControl.Canvas.Handle, CloseBtnDrawDetails, 
       TabSheet.FCloseButtonRect); 
    end; 
    end else begin 
    PageControl.Canvas.FillRect(Rect); 
    PageControl.Canvas.TextOut(TabCaption.X, TabCaption.Y, 
       PageControl.Pages[TabIndex].Caption); 
    end; 
end; 

procedure TMainForm.PageControlCloseButtonMouseDown(Sender: TObject; 
    Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
var 
    I: Integer; 
    PageControl: TPageControl; 
    TabSheet:TCloseTabSheet; 
begin 
    PageControl := Sender as TPageControl; 

    if Button = mbLeft then 
    begin 
    for I := 0 to PageControl.PageCount - 1 do 
    begin 
     if not (PageControl.Pages[i] is TCloseTabSheet) then Continue; 
     TabSheet:=PageControl.Pages[i] as TCloseTabSheet; 
     if PtInRect(TabSheet.FCloseButtonRect, Point(X, Y)) then 
     begin 
     FCloseButtonMouseDownTab := TabSheet; 
     FCloseButtonShowPushed := True; 
     PageControl.Repaint; 
     end; 
    end; 
    end; 
end; 

procedure TMainForm.PageControlCloseButtonMouseLeave(Sender: TObject); 
var 
    PageControl: TPageControl; 
begin 
    PageControl := Sender as TPageControl; 
    FCloseButtonShowPushed := False; 
    PageControl.Repaint; 
end; 

procedure TMainForm.PageControlCloseButtonMouseMove(Sender: TObject; 
    Shift: TShiftState; X, Y: Integer); 
var 
    PageControl: TPageControl; 
    Inside: Boolean; 
begin 
    PageControl := Sender as TPageControl; 

    if (ssLeft in Shift) and Assigned(FCloseButtonMouseDownTab) then 
    begin 
    Inside := PtInRect(FCloseButtonMouseDownTab.FCloseButtonRect, Point(X, Y)); 

    if FCloseButtonShowPushed <> Inside then 
    begin 
     FCloseButtonShowPushed := Inside; 
     PageControl.Repaint; 
    end; 
    end; 
end; 

procedure TMainForm.PageControlCloseButtonMouseUp(Sender: TObject; 
    Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 
var 
    PageControl: TPageControl; 
begin 
    PageControl := Sender as TPageControl; 

    if (Button = mbLeft) and Assigned(FCloseButtonMouseDownTab) then 
    begin 
    if PtInRect(FCloseButtonMouseDownTab.FCloseButtonRect, Point(X, Y)) then 
    begin 
     FCloseButtonMouseDownTab.DoClose; 
     FCloseButtonMouseDownTab := nil; 
     PageControl.Repaint; 
    end; 
    end; 
end; 

end. 
Cuestiones relacionadas