9
votes

Comment implémenter un bouton de fermeture pour une feuille de Tabecontrol

Comment puis-je implémenter un bouton de fermeture pour une feuille de ttabshets d'un tpagecontrol comme Firefox?

edit:
Version Delphi: Delphi 2010
OS: Windows XP et UP


2 commentaires

Quel style voulez-vous dire? Celui où chaque onglet a son propre bouton, ou le style où il y a un bouton à l'extrême droite qui s'applique à l'onglet est actuellement actif?


@Rob OUI, avec un bouton de fermeture sur chaque onglet


4 Réponses :


32
votes

Maintenant avec prise en charge de thème (Inclure Windows, UXTHEME, THÈMES CODE> Unités)!

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;


5 commentaires

Semble très gentil. La capture d'écran est une bonne touche! Malheureusement, il ne semble que convient uniquement à Windows 2000 ou à XP non thématique? Vista / 7. Avez-vous une version qui utilise l'API sur le thème?


J'ai testé votre code et ça fonctionne bien. Donc, je sélectionne votre code comme réponse. Mais à cause des autres fidutres, j'ai besoin de décider d'utiliser un TJVTabar de Jedi JVCl avec un littel modifié TJVTABBarXPPetager.


Quelle est la fonction inrange? Je suppose que c'est une fonction spécifique D2010 que je n'ai pas dans D2009. Y a-t-il une fonction similaire dans D2009 Avaisialble?


Cela fonctionne bien sur Delphi 2007 pour utiliser la fonction InRange, vous devez ajouter l'unité "Math" à la section Utilisations du formulaire.


Ne montre pas les images d'onglet Contrôle de la page. A besoin de plus de travail.



3
votes

Ce que j'ai fait dans le passé, il suffit de mettre un TBITBTN avec un graphique dans le coin supérieur droit du TPAGECONTROL. L'astuce I Le parent du TBITBTN est identique à la tpagecontrol, de sorte que ce n'est pas réellement sur l'une des feuilles de tabulation. Ensuite, dans le clic même pour ce bouton:

PageControl1.ActivePage.Free;


0 commentaires

6
votes

C'est souvent une bonne idée de la mettre en œuvre vous-même, car les autres réponses ont suggéré. Juste au cas où vous utilisez déjà Composants RATIAD , cependant, cette fonctionnalité est supportée "hors de la boîte". Il suffit de définir trzpagecontrol.showclosebuttononactivetab: = true et gérer le osez "/ code> événement". Le composant s'occupe de la mise en place d'une variété de dispositions / orientations / formes / couleurs / couleurs.

[juste un client heureux]


0 commentaires

2
votes

J'ai changé un peu cet exemple: - Création de la classe TCloTabsheet - cette classe a la propriété surClose: TnotifyEvent, qui sera appelé si attribué - Si tabsheet of of tpagecontrol n'est pas cette classe, il n'y a pas de bouton de fermeture - S'il est alors le bouton montré. Lorsque vous appuyez sur le bouton Fermer, il appelle l'application - Maintenant, vous n'avez pas besoin de contrôler le tableau FCLOSEBUTONSRECT, car ces rects stockés à TClosetabsheet xxx


0 commentaires