how do I avoid this unwanted behaviour with Delphi

2019-04-08 06:19发布

问题:

Included is a small project demonstrating my problem. I have a TPageControl aligned to the main form. On each of two tabsheets I have panels client aligned. On each of those panels I have 2 subpanels and a splitter. The LH panel and splitter is aligned left, the RH panel client-aligned.

Basically the problem is interaction between the 2 tabs. To demonstrate:

  • run the program
  • stretch the main form horizontally. Panel 3 will grow
  • move the splitter as far to the right as it will go. Panel 2 will grow, Panel 3 will shrink to its 10-pixel min-width constraint.
  • select tabsheet 2. Panel 5 is as designed, panel 6 grew when the main form was stretched
  • reduce the main form width to its original width. Panel 6 shrinks too much (undesirable)
  • click on tabsheet 1. Main form increases in width again (undesirable)

OK, the behaviour is probably explainable in terms of the rules of aligned panels, but can anybody suggest improvements to the operation?

unit Unit17;

interface

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

type
  TForm17 = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    Panel1: TPanel;
    Panel2: TPanel;
    Splitter1: TSplitter;
    Panel3: TPanel;
    Panel4: TPanel;
    Splitter2: TSplitter;
    Panel5: TPanel;
    Panel6: TPanel;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form17: TForm17;

implementation

{$R *.dfm}

end.


object Form17: TForm17
  Left = 0
  Top = 0
  Caption = 'Form17'
  ClientHeight = 254
  ClientWidth = 314
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object PageControl1: TPageControl
    Left = 0
    Top = 0
    Width = 314
    Height = 254
    ActivePage = TabSheet1
    Align = alClient
    Constraints.MinWidth = 30
    TabOrder = 0
    ExplicitWidth = 480
    object TabSheet1: TTabSheet
      Caption = 'TabSheet1'
      ExplicitWidth = 281
      ExplicitHeight = 165
      object Panel1: TPanel
        Left = 0
        Top = 0
        Width = 306
        Height = 226
        Align = alClient
        Caption = 'Panel1'
        TabOrder = 0
        ExplicitWidth = 109
        ExplicitHeight = 165
        object Splitter1: TSplitter
          Left = 151
          Top = 1
          Width = 12
          Height = 224
          ExplicitLeft = 145
        end
        object Panel2: TPanel
          Left = 1
          Top = 1
          Width = 150
          Height = 224
          Align = alLeft
          Caption = 'Panel2'
          Constraints.MinWidth = 10
          TabOrder = 0
        end
        object Panel3: TPanel
          Left = 163
          Top = 1
          Width = 142
          Height = 224
          Align = alClient
          Caption = 'Panel3'
          Constraints.MinWidth = 10
          TabOrder = 1
          ExplicitLeft = 141
          ExplicitWidth = 330
        end
      end
    end
    object TabSheet2: TTabSheet
      Caption = 'TabSheet2'
      ImageIndex = 1
      ExplicitWidth = 281
      ExplicitHeight = 165
      object Panel4: TPanel
        Left = 0
        Top = 0
        Width = 306
        Height = 226
        Align = alClient
        Caption = 'Panel4'
        TabOrder = 0
        ExplicitWidth = 109
        ExplicitHeight = 165
        object Splitter2: TSplitter
          Left = 149
          Top = 1
          Width = 11
          Height = 224
          ExplicitLeft = 141
        end
        object Panel5: TPanel
          Left = 1
          Top = 1
          Width = 148
          Height = 224
          Align = alLeft
          Caption = 'Panel5'
          Constraints.MinWidth = 10
          TabOrder = 0
        end
        object Panel6: TPanel
          Left = 160
          Top = 1
          Width = 145
          Height = 224
          Align = alClient
          Caption = 'Panel6'
          Constraints.MinWidth = 10
          TabOrder = 1
          ExplicitLeft = 141
          ExplicitWidth = 139
          ExplicitHeight = 163
        end
      end
    end
  end
end 

回答1:

To get expected behavior remove the constraints (MinWidth) from your panels. These settings are currently ineffective anyway, since your splitters have a MinSize of '30' (the default, not stored).

edit (response to the comment): You cannot expect the 'MinWidth' constraint of a control that's at the right side of a splitter to adjust the size of the left-side control. That's only logical, the constraint is a property for the control that you set. All you'll achieve is that the form will deny shrinking if your control is already at its 'MinWidth', hence the undesirable behavior that you observe that the form is getting larger when you switch tabs. What you desire, you have to do with code - as Marjan told in his answer. There should be more than one way to achieve this, for instance, put the below to the Panel3's 'OnCanResize' event:

procedure TForm1.Panel3CanResize(Sender: TObject; var NewWidth,
  NewHeight: Integer; var Resize: Boolean);
begin
  if NewWidth < Splitter1.MinSize then
    Panel2.Width := Panel2.Width - Splitter1.MinSize + NewWidth;
end;


回答2:

Not necessarily a real answer, but a couple of remarks:

  • MinSize voor an alLeft aligned Splitter pertains to the control on the Splitter's left and right. Your Panel 6 is indeed resized to (slightly more than) its own minWidth (10) instead of the splitter's MinSize (30). You can more easily demonstrate this by adding two left aligned panels on each of your panels 2, 3, 5 and 6 and give them a width of 10 and 20 and a different color.

  • Selecting tabsheet one again after reducing the mainform's width, does grow the mainform (yikes) AND shows that now Panel3 has now also been reduced to its min width instead of the splitter's minSize.

The solution to the main form resizing? Dunno, but making sure your panels' minwidth is in sync with the splitters' minsize should remove the shrinking. And as Sertac says, I suspect that you simply need to opt for one or the other, but not both...

Update:

  • Setting the splitters' minSize to 30 and setting the panels' minWidth to 0. Takes away the mainform's resize, but does reduce the right hand panels to 0 width.

  • Setting the splitters' minSize to 30 and setting the panels' minWidth to 30, takes away the minimum width problems, but still resized the main form.

  • Setting the panels' minWidth to 30 and setting the splitters' minSize to 1 (the minimum) allows you to move the splitter all the way to the right and resizes the main form by the panels minWidth when you release the splitter. It does keep Panel6 from reducing to less than 30, but again the mainform resizes when you reselect tab 1.

It would seem that your best bet is to rely on the splitters' minSize and "manually" prevent the right hand panels from reducing to far by restricting the splitters' movement when it gets to far right. You can do this in the OnCanResize event of the splitters.

btw, using D2009



回答3:

void __fastcall TFMain::SplitterCanResize(TObject *Sender, int &NewSize, bool &Accept)
{
  TSplitter *S = (TSplitter *)Sender;
  for (int i = 0; Accept && i < S->Parent->ControlCount; i++) if (S->Parent->Controls[i]->Constraints->MaxHeight && S->Parent->Controls[i]->Align == S->Align && NewSize >= S->Parent->Controls[i]->Constraints->MaxHeight * 2) Accept = false;
}


回答4:

If while the form was wider you moved the splitter far to the right then reduced the form's width so it became narrower than the left-hand panel (and thus the splitter found itself 'outside' the form), what should the behaviour of your form be in this case? You have been asked about your criteria for desirable behaviour and all I can see in your answer so far is your understanding of undesirable behaviour.

Now I have been concerned a couple of times with possible side effects of resizing a form that has panels and splitters on it. I didn't investigate very much into it and so in particular I never knew before about the auto-resize effect like in your situations. Anyway, in order to prevent most (if not any) of the possible behavioral artefacts I considered using TScrollBox as a parent control to panels and splitters instead of TPanel.

I believe, this would change resizing of the form into resizing of the scroll box's client area, which would work fine for me in however few little projects of mine where I used splitters, if I employed tabsheets like you do. However I cannot know about your case. And I understand that this is more of a workaround than a solution to your problem.