Как заставить вертикальную полосу прокрутки всегда быть видимой из AutoScroll в WinForms?

Использование VS2010 и .NET 4.0 с C # и WinForms:

Я всегда хочу, чтобы вертикальная полоса прокрутки отображалась для моей панели как отключенная полоса прокрутки (когда она не нужна, и как активная, когда ее можно использовать.

Так что это как гибрид AutoScroll. Я пробовал использовать VScrollBars, но не могу понять, где их разместить, чтобы эта работа работала.

По сути, у меня есть пользовательский элемент управления, который действует как «Документ» элементов управления, его размер меняется, поэтому при использовании автопрокрутки он работает отлично. Полоса прокрутки появляется, когда элемент управления пользователя не подходит, и пользователь может переместить его вверх.

По сути, это похоже на веб-браузер. Однако перерисовка элементов управления занимает много времени (это формы с множеством полей и кнопок и т. Д. В группах в сетке на панели: P

Так или иначе, когда автопрокрутка включает вертикальную полосу прокрутки, перерисовка окна занимает некоторое время. Я хотел бы ВСЕГДА показывать вертикальную полосу прокрутки, как указано выше (с функцией включения / отключения).

Если у кого-то есть помощь, я прочитал много сообщений по теме автопрокрутки, но никто не спросил, о чем я прошу, и я не могу придумать решение.


person user1104203    schedule 31.12.2011    source источник
comment
Это до смешного сложно сделать. Код, управляющий полосами прокрутки, является частным методом в ScrollableControl и не может их переопределить. Попытка подделать его, закрепив VScrollBar на панели, которая скрыта при необходимости прокрутки, вызвала сбои, от которых я не мог избавиться. Я сдался.   -  person Hans Passant    schedule 01.01.2012
comment
Ганс прав. Если вы хотите, чтобы ваша вещь работала надежно, не доверяйте приведенным ниже ответам. Логика стандартных полос прокрутки WS_HSCROLL и WS_VSCROLL уже безнадежно испорчена в Win32 API (и это то, что используется в ScrollableControl). Даже если вы попытаетесь исправить это на этом уровне, он будет полон глюков; Windows настаивает на получении контроля над полосами прокрутки. Ответы, размещенные здесь, в лучшем случае хрупкие и будут вызывать нежелательные побочные эффекты, такие как мерцание или проблемы с макетом. Если вы хотите сделать это правильно, вам придется самому написать много кода.   -  person dialer    schedule 08.02.2021


Ответы (5)


C # Версия ответа Component_Tech

using System.Runtime.InteropServices; 

public class MyUserControl : UserControl
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

    private enum ScrollBarDirection
    {
        SB_HORZ = 0,
        SB_VERT = 1,
        SB_CTL = 2,
        SB_BOTH = 3
    }

    public MyUserControl()
    {
        InitializeComponent();
        ShowScrollBar(this.Handle, (int) ScrollBarDirection.SB_VERT, true);
    }
}
person fiat    schedule 09.10.2014
comment
Для этого требуется: using System.Runtime.InteropServices; - person Bill; 14.10.2015
comment
У меня это сработало только тогда, когда я поместил его в VisibleChanged (). Похоже, что это вступает в силу только в том случае, если элемент управления является видимым (?!) - person Art Swri; 20.10.2016
comment
Это работает хорошо, но стиль полосы прокрутки, когда она не нужна, сильно отличается от того, когда прокрутка возможна. - person user276648; 09.04.2019

Вы можете использовать функцию автоматической прокрутки панели, вам просто нужно отправить ей сообщение Windows, чтобы отобразить вертикальную полосу прокрутки:

<DllImport("user32.dll")> _
Public Shared Function ShowScrollBar(ByVal hWnd As System.IntPtr, ByVal wBar As Integer, ByVal bShow As Boolean) As Boolean
End Function

Private Const SB_VERT As Integer = 1


Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ShowScrollBar(Panel1.Handle, SB_VERT, True)
End Sub

Полоса прокрутки будет отображаться и выглядеть так, как если бы ее можно было прокручивать, но она ничего не сделает, пока не будет действительно готова к прокрутке. Если вы отключите его, он не будет повторно включен автоматически, поэтому, вероятно, это лучший подход.

Кроме того, чтобы повысить производительность при изменении размера, вы можете вызвать SuspendLayout на панели перед обновлением и ResumeLayout, когда закончите.

person competent_tech    schedule 31.12.2011
comment
Я пытался это сделать, но это не имело значения. Макет приостановки / возобновления. Отличается ли этот метод от простого перехода .Visible = true к объекту VerticalScroll? Потому что это не работает правильно. Полоса прокрутки не модифицируется. - person user1104203; 01.01.2012
comment
Да, этот метод отличается от установки Visible = true (это то, что я пробовал сначала, но .Net полностью игнорирует его). - person competent_tech; 01.01.2012

Что сработало для меня, так это переопределение вызова CreateParams и включение стиля WS_VSCROLL.

public class VerticalFlowPanel : FlowLayoutPanel
{
    protected override CreateParams CreateParams
    {
        get
        {
            var cp = base.CreateParams;
            cp.Style |= 0x00200000; // WS_VSCROLL
            return cp;
        }
    }
}

Логика AutoScroll теперь будет регулировать границы прокрутки, не скрывая полосу прокрутки.

person BradJ    schedule 17.08.2016
comment
Хороший и простой обходной путь, но, к сожалению, логика AutoScroll обновляет только границы прокрутки и не отключает полосу прокрутки, когда все содержимое помещается внутрь. Есть ли способ реализовать поведение включения / выключения поверх этого? - person glopes; 30.08.2016

Вот что для меня решило. Мой случай состоит в том, что у меня панель зажата между другими тремя панелями без какой-либо степени свободы в любом направлении. Мне нужно было, чтобы эта панель была настолько большой, чтобы вся структура выходила за пределы моего экрана с разрешением 1920x1080. Решение на самом деле очень простое. Для панели, для которой требуются полосы прокрутки, установите для свойства AutoScroll значение true. Затем добавьте к нему еще один элемент управления в крайнем правом крайнем нижнем положении (правое нижнее положение). Выбранный мной элемент управления - это ярлык, который я сделал невидимым ... И все. Теперь моя панель занимает ограниченную область, но я могу прокрутить до нужного мне размера и использовать его для нужного мне размера. Если вам нужны только горизонтальные полосы прокрутки, добавьте невидимый элемент управления снаружи слева, для вертикальных - только внизу, внизу.

Фактический размер панели - это тот размер, которым вы ограничиваете ее при отображении, но виртуальный размер определяется невидимым элементом управления.

person Gogu CelMare    schedule 11.08.2016

Этот код будет рисовать отключенную вертикальную полосу прокрутки всякий раз, когда встроенная полоса прокрутки Panel невидима. Коды предполагают, что

AutoScroll = true;
AutoSize   = false;

Следующий код оптимизирован по скорости. В OnPaint () этого делается как можно меньше.

Унаследовать класс от Panel. Добавьте эти переменные-члены:

// NOTE: static variables are not thread safe. 
// But as we have only one GUI thread this does not matter.
static IntPtr mh_ScrollTheme   = IntPtr.Zero;
static int    ms32_ScrollWidth = SystemInformation.VerticalScrollBarWidth;

Win32.RECT mk_ScrollTop;
Win32.RECT mk_ScrollBot;   // coordinates of top scrollbar button
Win32.RECT mk_ScrollShaft; // coordinates of bottom scrollbar button

Затем переопределите OnSizeChanged:

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);

    Win32.RECT k_ScrollBar = new Win32.RECT(ClientRectangle);
    k_ScrollBar.Left = k_ScrollBar.Right - ms32_ScrollWidth;

    mk_ScrollTop   = new Win32.RECT(k_ScrollBar);
    mk_ScrollBot   = new Win32.RECT(k_ScrollBar);
    mk_ScrollShaft = new Win32.RECT(k_ScrollBar);

    int s32_Upper = k_ScrollBar.Top    + ms32_ScrollWidth;
    int s32_Lower = k_ScrollBar.Bottom - ms32_ScrollWidth;

    mk_ScrollTop  .Bottom = s32_Upper;
    mk_ScrollBot  .Top    = s32_Lower;
    mk_ScrollShaft.Top    = s32_Upper;
    mk_ScrollShaft.Bottom = s32_Lower;
}

И при необходимости раскрасьте полосу прокрутки:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    if (VScroll)
        return; // The 'real' scrollbar is visible
            
    if (mh_ScrollTheme == IntPtr.Zero)
        mh_ScrollTheme = Win32.OpenThemeData(Handle, "SCROLLBAR");

    if (mh_ScrollTheme == IntPtr.Zero)
        return; // The user has disabled themes

    // Draw the disabled vertical scrollbar.
    IntPtr h_DC = e.Graphics.GetHdc();

    // Draw shaft
    const int SBP_UPPERTRACKVERT = 7;
    const int SCRBS_DISABLED     = 4;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_UPPERTRACKVERT, SCRBS_DISABLED, ref mk_ScrollShaft, IntPtr.Zero);

    // Draw top button
    const int SBP_ARROWBTN       = 1;
    const int ABS_UPDISABLED     = 4;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_ARROWBTN, ABS_UPDISABLED,   ref mk_ScrollTop,  IntPtr.Zero);

    // Draw lower button
    const int ABS_DOWNDISABLED   = 8;
    Win32.DrawThemeBackground(mh_ScrollTheme, h_DC, SBP_ARROWBTN, ABS_DOWNDISABLED, ref mk_ScrollBot,  IntPtr.Zero);

    e.Graphics.ReleaseHdc(h_DC);
}
person Elmue    schedule 18.08.2020