Как реализовать кнопку закрытия для TTabsheet из TPageControl
Как я могу реализовать кнопку закрытия для TTabsheet TPageControl, как Firefox?
Редактировать:
Версия Delphi: Delphi 2010
ОС: Windows XP и выше
4 ответа
Теперь с поддержкой тем (включая Windows, UxTheme, Themes
единицы)!
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;
Похоже:
Это часто хорошая идея, чтобы реализовать это самостоятельно, как предлагали другие ответы. На тот случай, если вы уже используете Raize Components, эта функция поддерживается "из коробки". Просто установите TRzPageControl.ShowCloseButtonOnActiveTab := true
и обрабатывать OnClose
событие. Компонент заботится о размещении для различных макетов / ориентаций / форм / цветов вкладок.
[просто счастливый клиент]
В прошлом я просто поместил TBitBtn с графикой в верхнем правом углу TPageControl. Хитрость у родителя TBitBtn такая же, как у TPageControl, так что на самом деле его нет ни на одной из вкладок. Затем в клик даже для этой кнопки:
PageControl1.ActivePage.Free;
Когда текущий TTabControl освобождается, он уведомляет TPageControl, которому он принадлежит.
Я немного изменил этот пример: - создал класс TCloseTabSheet - у этого класса есть свойство OnClose: TNotifyEvent, которое будет вызываться, если назначено - если TabSheet из TPageControl не тот класс, тогда нет кнопки закрытия - если это тогда Button показал. Когда вы нажимаете кнопку закрытия, он вызывает OnClose - теперь вам не нужно управлять массивом FCloseButtonsRect, потому что эти Rects хранятся в 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.