Pergunta

Estou escrevendo um aplicativo no .NET que usa o AutoScroll para um painel de layout em uma caixa de diálogo. Parece que sempre que redimenso a janela para que as barras de rolagem vertical apareçam, a barra de rolagem horizontal também aparece automaticamente também. Olhando de perto, a segunda barra de rolagem agora me permite rolar a janela por 16 pixels (a largura da outra barra de rolagem). Portanto, o Windows parece pensar que eu preciso de uma área de cliente que seja pelo menos tão larga quanto antes da aparição da barra de rolagem vertical.

Se agora eu redimensione a janela para 16 pixels (para que minha área de janela seja tão larga quanto antes da aparição da barra de rolagem), a barra de rolagem desaparece. Agora, se eu redimensioná -lo de volta ao que era, ele ficará longe.

Então, parece -me que há um bug no sistema em que a largura mínima é de alguma forma pegajosa, mas o aumento do tamanho e a redução da janela (com o mouse, e sem ajustar as APIs relacionadas às barras de rolagem)

Alguém conhece uma solução alternativa ou estou fazendo algo para viajar pelo Windows?

Nenhuma solução correta

Outras dicas

Sim, acho que você já diagnosticou o problema corretamente. É um efeito colateral desagradável de, digamos, a barra de rolagem vertical que aparece e precisa de espaço, tornando a área do cliente disponível menor. Muito pequeno para se ajustar aos controles, agora também fazendo a barra de rolagem horizontal aparecer. Na verdade, é bi-estável, a barra horizontal pode piscar e desligar em certos casos.

Para evitar esse efeito, o mecanismo de layout teria que fazer vários passes através do layout, lidando com a mudança da área do cliente. No entanto, só faz um passe. O que parece sábio, esse pode ser um loop potencialmente sem fim. Não conheço uma correção decente para isso. Seu usuário provavelmente redimensionará a janela grande o suficiente para se livrar de pelo menos uma das barras de rolagem.

Este é um bug conhecido no Windows - aqui

A melhor maneira de corrigir isso é colocar o painel de layout da tabela automaticamente dentro de outro painel que é atracado no formulário principal e definido com automóveis = true

Portanto, você não está mais usando o tablelayoutPanel para rolar, que é buggy, você usa o painel para rolar e o tablelayoutPanel está dentro do painel

Não notei exatamente o comportamento que você descreve, mas encontrei situações em que a aparência da barra de rolagem vertical torna necessária uma barra de rolagem horizontal.

Você pode definir o conteúdo do painel para permitir a largura da barra de rolagem, por exemplo, se eu tiver um ListBox em um Panel:

listBox1.Width = panel2.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth;

Hth

Acabei de encontrar esse problema. A correção que usei foi definir Scrollable para false e então para true. Aqui está um exemplo com um ListView Resize evento:

private void myListView_Resize(object sender, EventArgs e)
{
 this.SuspendLayout();

 //Code to do various resizing stuff

 //Force Scrollbar Recalculation
 myListView.Scrollable = false;
 myListView.Scrollable = true;
 this.ResumeLayout(false);
 this.PerformLayout();
}

Se Scrollable Nem sempre é verdadeiro, você pode tornar o recálculo condicional.

Apesar de ser uma pergunta antiga, ainda é um problema no .NET 4. Tendo lido o máximo que consegui encontrar sobre o problema, transmiti uma combinação de soluções em uma classe auxiliar.

Primeiro, aqui está o resultado que estou atirando ... tenho um painel que contém uma variedade de controles. Os controles da criança e seus tamanhos podem mudar com base na atividade do usuário. Quero que o painel redimensione horizontalmente para que nunca haja uma barra de rolagem horizontal, mas se não houver espaço suficiente verticalmente, quero que a barra de rolagem vertical apareça. Além disso, a barra de rolagem vertical não pode cobrir nenhum dos meus filhos quando aparecer, e eu não quero deixar uma lacuna quando não for necessário.

Os dois 'bugs' que minha classe ajudante tenta corrigir são os primeiros, nunca mostram a barra de rolagem horizontal e, segundo, quando a barra de rolagem vertical aparecer, têm o aumento da largura do painel automaticamente para acomodá -lo.

Minhas suposições são de que o painel está definido como automaticamente e o AutoScroll, e os controles infantis também estão definidos como automaticamente.

A solução

A classe auxiliar se liga ao painel (manipulando a tinta e os eventos sizechanged) e faz duas coisas. Primeiro, desativa a barra de rolagem horizontal. Isso não é tão fácil quanto parece, e eu achei a solução para esse problema aqui Barra de rolagem horizontal Resposta por KBV Subrahmanyam. Segundo, em resposta à tinta e eventos sizechanged, bem como a um cronômetro de fundo, ele verifica se a propriedade visível da barra de rolagem vertical mudou. Nesse caso, a classe auxiliar altera a propriedade de preenchimento correta do painel para adicionar ou remover o espaço extra que a barra de rolagem exige. O uso dos vários eventos do painel e o timer são necessários porque o .NET expõe não Eventos para a barra de rolagem (uma grande falha de design IMHO).

Uma vez que o ponto final é que você não pode fazer nada que altere o tamanho do painel enquanto lida com o evento Sizechanged. Coisas ruins (TM) acontece se você o fizer. Portanto, se eu precisar alterar o preenchimento devido a um evento Sizechanged, agende essa alteração para mais tarde.

De qualquer forma, aqui está o código da classe ajudante. Ele pressupõe que você tenha todas as declarações 'usando' apropriadas, incluindo uma para System.threading ...

/// <summary>
/// This class is intended to beat the AutoSize and AutoScroll features into submission!
/// 
/// Or, at least getting them to work the way I want them to (which may not be the way 
/// others think they should work).
/// 
/// This class will force a panel that has AutoSize enabled to actually increase its
/// width as appropriate when the AutoScroll Vertical scroll bar becomes visible.
/// I like this better than attempting to 'reserve' space for the Vertical scroll bar,
/// which wastes space when the scroll bar is not needed, and leaves ugly gaps in
/// your user interface.
/// </summary>
public class AutoScrollFixer
{
    /// <summary>
    /// This is the panel we are 'fixing'
    /// </summary>
    private Panel _panel;

    /// <summary>
    /// This field keeps track of the original value for
    /// the right padding property of the panel.
    /// </summary>
    private int _originalRightPadding = 0;

    /// <summary>
    /// We use this flag to prevent recursion problems.
    /// </summary>
    private bool _adjusting = false;

    /// <summary>
    /// This flag keeps track of the last known state of the scroll bar.
    /// </summary>
    private bool _lastScrollBarVisible = false;

    /// <summary>
    /// We use a timer to check the scroll bar state every so often.
    /// This is necessary since .NET (in another stunning piece of
    /// architecture from Microsoft) provides absolutely no events
    /// attached to the scroll bars of a panel.
    /// </summary>
    private System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();

    /// <summary>
    /// Construct an AutoScrollFixer and attach it to the provided panel.
    /// Once created, there is no particular reason to keep a reference 
    /// to the AutoScrollFixer in your code.  It will silently do its thing
    /// in the background.
    /// </summary>
    /// <param name="panel"></param>
    public AutoScrollFixer(Panel panel)
    {
        _panel = panel;
        _originalRightPadding = panel.Padding.Right;

        EnableVerticalAutoscroll(_panel);
        _lastScrollBarVisible = _panel.VerticalScroll.Visible;

        _panel.Paint += (s, a) =>
        {
            AdjustForVerticalScrollbar();
        };

        _panel.SizeChanged += (s, a) =>
        {
            //
            //  We can't do something that changes the size while handling
            //  a size change.  So, if an adjustment is needed, we will
            //  schedule it for later.
            //
            if (_lastScrollBarVisible != _panel.VerticalScroll.Visible)
            {
                AdjustLater();
            }
        };

        _timer.Tick += (s, a) =>
        {
            //
            //  Sadly, the combination of the Paint event and the SizeChanged event
            //  is NOT enough to guarantee that we will catch a change in the
            //  scroll bar status.  So, as a last ditch effort, we will check
            //  for a status change every 500 mSecs.  Yup, this is a hack!
            //
            AdjustForVerticalScrollbar();
        };

        _timer.Interval = 500;
        _timer.Start();
    }


    /// <summary>
    /// Enables AutoScroll, but without the Horizontal Scroll bar.
    /// Only the Vertical Scroll bar will become visible when necessary
    /// 
    /// This method is based on this StackOverflow answer ...
    /// https://stackoverflow.com/a/28583501/2175233
    /// </summary>
    /// <param name="panel"></param>
    public static void EnableVerticalAutoscroll( Panel panel )
    {
        panel.AutoScroll = false;
        panel.HorizontalScroll.Enabled = false;
        panel.HorizontalScroll.Visible = false;
        panel.HorizontalScroll.Maximum = 0;
        panel.AutoScroll = true;
    }


    /// <summary>
    /// Queue AdjustForVerticalScrollbar to run on the GUI thread after the current
    /// event has been handled.
    /// </summary>
    private void AdjustLater()
    {
        ThreadPool.QueueUserWorkItem((t) => 
        {
            Thread.Sleep(200);
            _panel.BeginInvoke((Action)(() =>
            {
                AdjustForVerticalScrollbar();
            }));
        });
    }


    /// <summary>
    /// This is where the real work gets done.  When this method is called, we will
    /// simply set the right side padding on the panel to make room for the
    /// scroll bar if it is being displayed, or reset the padding value to 
    /// its original value if not.
    /// </summary>
    private void AdjustForVerticalScrollbar()
    {
        if (!_adjusting)
        {
            try
            {
                _adjusting = true;

                if (_lastScrollBarVisible != _panel.VerticalScroll.Visible)
                {
                    _lastScrollBarVisible = _panel.VerticalScroll.Visible;

                    Padding p = _panel.Padding;
                    p.Right = _lastScrollBarVisible ? _originalRightPadding + System.Windows.Forms.SystemInformation.VerticalScrollBarWidth + 2 : _originalRightPadding;
                    _panel.Padding = p;
                    _panel.PerformLayout();
                }
            }

            finally
            {
                _adjusting = false;
            }
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top