tags:

views:

15

answers:

1

I'm using a Matrix to set a transformation on my Graphics object during OnPaint. I would like the scrollbars to translate the matrix when scrolled; something like this in OnScroll:

float offset = se.NewValue - se.OldValue;

if (offset == 0)
{
    return;
}

if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
    this.TransformMatrix.Translate(-offset, 0);
}
else
{
    this.TransformMatrix.Translate(0, -offset);
}

I'm pretty sure that both WM_VSCROLL and WM_HSCROLL send a WM_PAINT because the control is redrawn without me actually calling Refresh(). I find that this painting isn't very fluid and is jittery and doesn't draw with the right transformation it seems. Should I intercept the WM_V/HSCROLL messages and manually set the scrollbar's properties (like position etc) with SetScrollInfo? If not, what should I do?

A: 

I ended up catching WM_V/HSROLL and doing the painting on my own. It works great!

Here's what I'm using if anyone wants it:

    protected override CreateParams CreateParams
    {
        get
        {
            var p = base.CreateParams;
            p.Style |= NativeMethods.WindowStyles.WS_BORDER;
            p.Style |= NativeMethods.WindowStyles.WS_HSCROLL;
            p.Style |= NativeMethods.WindowStyles.WS_VSCROLL;

            return p;
        }
    }

    private Point ScrollPosition
    {
        get
        {
            var info = new NativeMethods.ScrollInfo();
            info.cbSize = (uint)Marshal.SizeOf(info);
            info.fMask = NativeMethods.ScrollInfoMask.SIF_POS;

            NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_HORZ, out info);
            int x = info.nPos;

            NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_VERT, out info);
            int y = info.nPos;

            return new Point(x, y);
        }
        set
        {
            var info = new NativeMethods.ScrollInfo();
            info.cbSize = (uint)Marshal.SizeOf(info);
            info.fMask = NativeMethods.ScrollInfoMask.SIF_POS;
            info.nPos = value.X;
            NativeMethods.SetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_HORZ, ref info, true);

            info.nPos = value.Y;
            NativeMethods.SetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_VERT, ref info, true);
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        // Clear the background color.
        e.Graphics.Clear(Color.White);

        e.Graphics.Transform = this.TransformMatrix.Clone();

        // Do drawing here.
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        // Do nothing.
    }

    private void OnScroll(ScrollEventArgs se)
    {

        float offset = se.NewValue - se.OldValue;

        if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll)
        {
            this.TransformMatrix.Translate(-offset, 0);
        }
        else
        {
            this.TransformMatrix.Translate(0, -offset);
        }

        this.Refresh();

    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case NativeMethods.WindowMessages.WM_HSCROLL:
            case NativeMethods.WindowMessages.WM_VSCROLL:
                this.WmScroll(ref m);
                m.Result = IntPtr.Zero;
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    private void WmScroll(ref Message m)
    {

        int smallChange = 20;

        uint sb = NativeMethods.SBFlags.SB_HORZ;
        var orientation = ScrollOrientation.HorizontalScroll;

        if(m.Msg == NativeMethods.WindowMessages.WM_VSCROLL)
        {
            sb = NativeMethods.SBFlags.SB_VERT;
            orientation = ScrollOrientation.VerticalScroll;
        }

        // Get current scroll Page and Range.
        var info = new NativeMethods.ScrollInfo();
        info.cbSize = (uint)Marshal.SizeOf(info);
        info.fMask = NativeMethods.ScrollInfoMask.SIF_PAGE | NativeMethods.ScrollInfoMask.SIF_POS;
        NativeMethods.GetScrollInfo(this.Handle, sb, out info);

        int newValue = info.nPos;
        var type = ScrollEventType.SmallDecrement;

        switch (Unmanaged.LoWord(m.WParam))
        {
            case NativeMethods.SBCommands.SB_BOTTOM:
                type = ScrollEventType.Last;
                break;

            case NativeMethods.SBCommands.SB_ENDSCROLL:
                type = ScrollEventType.EndScroll;
                break;

            case NativeMethods.SBCommands.SB_LINEDOWN:
                newValue += smallChange;
                type = ScrollEventType.SmallIncrement;
                break;

            case NativeMethods.SBCommands.SB_LINEUP:
                newValue -= smallChange;
                type = ScrollEventType.SmallDecrement;
                break;

            case NativeMethods.SBCommands.SB_PAGEDOWN:
                newValue += (int)info.nPage;
                type = ScrollEventType.LargeIncrement;
                break;

            case NativeMethods.SBCommands.SB_PAGEUP:
                newValue -= (int)info.nPage;
                type = ScrollEventType.LargeDecrement;
                break;

            case NativeMethods.SBCommands.SB_THUMBPOSITION:
                type = ScrollEventType.ThumbPosition;
                break;

            case NativeMethods.SBCommands.SB_THUMBTRACK:
                newValue = Unmanaged.HiWord(m.WParam);
                type = ScrollEventType.ThumbTrack;
                break;

            case NativeMethods.SBCommands.SB_TOP:
                type = ScrollEventType.First;
                break;
        }

        var newInfo = new NativeMethods.ScrollInfo();
        newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
        newInfo.fMask = NativeMethods.ScrollInfoMask.SIF_POS;
        newInfo.nPos = newValue;

        NativeMethods.SetScrollInfo(this.Handle, sb, ref newInfo, false);

        int realNewValue = (orientation == ScrollOrientation.HorizontalScroll) ? this.ScrollPosition.X : this.ScrollPosition.Y;

        // Fire the scroll event.
        // TODO - Create a Scroll event.
        this.OnScroll(new ScrollEventArgs(type, info.nPos, realNewValue, orientation));
    }
TheCloudlessSky