Как использовать ScrollableControl с AutoScroll, установленным на false

У меня есть настраиваемый элемент управления, который масштабирует нарисованный на заказ холст документа.

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

Теперь я вручную устанавливаю свойства HorizontalScroll и VerticalScroll с AutoScroll, установленным в false, например, каждый раз, когда изменяется уровень масштабирования или размер клиента:

int canvasWidth = (int)Math.Ceiling(Image.Width * Zoom) + PageMargins.Horizontal;
int canvasHeight = (int)Math.Ceiling(Image.Height * Zoom) + PageMargins.Vertical;

HorizontalScroll.Maximum = canvasWidth;
HorizontalScroll.LargeChange = ClientSize.Width;

VerticalScroll.Maximum = canvasHeight;
VerticalScroll.LargeChange = ClientSize.Height;

if (canvasWidth > ClientSize.Width)
{
    HorizontalScroll.Visible = true;
}
else
{
    HorizontalScroll.Visible = false;
    HorizontalScroll.Value = 0;
}

if (canvasHeight > ClientSize.Height)
{
    VerticalScroll.Visible = true;
}
else
{
    VerticalScroll.Visible = false;
    VerticalScroll.Value = 0;
}

int focusX = (int)Math.Floor((FocusPoint.X * Zoom) + PageMargins.Left);
int focusY = (int)Math.Floor((FocusPoint.Y * Zoom) + PageMargins.Top);

focusX = focusX - ClientSize.Width / 2;
focusY = focusY - ClientSize.Height / 2;

if (focusX < 0)
    focusX = 0;
if (focusX > canvasWidth - ClientSize.Width)
    focusX = canvasWidth - ClientSize.Width;

if (focusY < 0)
    focusY = 0;
if (focusY > canvasHeight - ClientSize.Height)
    focusY = canvasHeight - ClientSize.Height;

if (HorizontalScroll.Visible)
    HorizontalScroll.Value = focusX;

if (VerticalScroll.Visible)
    VerticalScroll.Value = focusY;

В этом случае FocusPoint представляет собой структуру PointF, которая содержит координаты в растровом изображении, на котором сфокусирован пользователь (например, когда они прокручивают колесо мыши для увеличения, они фокусируются на текущем местоположении мыши в этой точке). время). Этот функционал работает в большинстве случаев.

Что не работает, так это полосы прокрутки. Если пользователь попытается выполнить прокрутку вручную, щелкнув любую полосу прокрутки, они оба будут возвращаться к 0. Я не устанавливаю их больше нигде в своем коде. Я попытался написать следующее в методе OnScroll():

if (se.ScrollOrientation == ScrollOrientation.VerticalScroll)
{
    VerticalScroll.Value = se.NewValue;
}
else
{
    HorizontalScroll.Value = se.NewValue;
}

Invalidate();

Но это вызывает очень неустойчивое поведение, включая пролистывание и прокрутку за пределы.

Как я должен написать код для OnScroll? Я попробовал base.OnScroll, но он ничего не сделал, пока для AutoScroll установлено значение false.


person Trevor Elliott    schedule 23.04.2012    source источник
comment
Я думаю, что вы были правы в первый раз. Сначала установите AutoScrollMinSize, а затем вызовите AutoScrollPosition. Используйте панель с двойной буферизацией для управления мерцанием.   -  person LarsTech    schedule 23.04.2012
comment
Мой элемент управления был дважды буферизован. Он никогда не мерцал, за исключением случаев, когда я одновременно менял AutoScrollMinSize и AutoScrollPosition.   -  person Trevor Elliott    schedule 23.04.2012


Ответы (1)


В итоге я реализовал свою собственную пользовательскую прокрутку, создав 3 дочерних элемента управления: HScrollBar, VScrollBar и Panel.

Я скрываю ClientSize и ClientRectangle следующим образом:

public new Rectangle ClientRectangle
{
    get
    {
        return new Rectangle(new Point(0, 0), ClientSize);
    }
}

public new Size ClientSize
{
    get
    {
        return new Size(
            base.ClientSize.Width - VScrollBar.Width,
            base.ClientSize.Height - HScrollBar.Height
        );
    }
}

Макет делается в OnClientSizeChanged:

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

    HScrollBar.Location = new Point(0, base.ClientSize.Height - HScrollBar.Height);
    HScrollBar.Width = base.ClientSize.Width - VScrollBar.Width;

    VScrollBar.Location = new Point(base.ClientSize.Width - VScrollBar.Width, 0);
    VScrollBar.Height = base.ClientSize.Height - HScrollBar.Height;

    cornerPanel.Size = new Size(VScrollBar.Width, HScrollBar.Height);
    cornerPanel.Location = new Point(base.ClientSize.Width - cornerPanel.Width, base.ClientSize.Height - cornerPanel.Height);
}

Каждая полоса прокрутки имеет свое событие Scroll, подписанное на следующее:

private void ScrollBar_Scroll(object sender, ScrollEventArgs e)
{
    OnScroll(e);
}

И, наконец, мы можем разрешить прокрутку событий MouseWheel следующим образом:

protected override void OnMouseWheel(MouseEventArgs e)
{
    int xOldValue = VScrollBar.Value;

    if (e.Delta > 0)
    {
        VScrollBar.Value = (int)Math.Max(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), 0);
        OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
    }
    else
    {
        VScrollBar.Value = (int)Math.Min(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), VScrollBar.Maximum - (VScrollBar.LargeChange - 1));
        OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
    }
}

Для пользовательского рисования вы должны использовать следующий оператор:

e.Graphics.TranslateTransform(-HScrollBar.Value, -VScrollBar.Value);

Это отлично работало без сбоев, присутствующих при использовании AutoScroll.

person Trevor Elliott    schedule 24.04.2012