Eto Scroll bar style

(Scrollable)
Can the scroll bar style be modified? Or is there any other possibility to reimplement a scroll component?

Are you asking about an Eto scrollbar?

– Dale

Yes!

I’d like to bump this topic. Is there a way to modify the style of the default scroll bar in Eto.Scrollable

The default one on Windows 10 is a bit bulky:

One can modify the WPF scrollbar style with a xaml style and then add that style to the System.Windows.Window.Resources.MergedDictionaries of the WPF window instance, or System.Windows.Application.Current.Resources.MergedDictionaries. Your plugin would only work on Windows with this code, so you’d either have to compile separately for Mac or only target Windows.

Hope this helps!

Thanks @curtisw!
Will give it a shot. I know it is probably a lot of work, but it would be awesome if we’d have more stylistic control in ETO. Currently, we’re forced to hack into platform-specific code rather than working with a high-level exposed property.

I know I am a bit late to the party. But here is a working example of how to use Eto Drawable for the custom scrollbar.
Once you dig into it, Drawable can create virtually everything you need. The only caveat is that eto uses resources for Control rendering which can be quite slow if your display card is not as powerful.

2 Likes

@Wiley Thank you!
This is almost the effect I want. I have tried to do it in the way you said before, but I don’t know how to achieve the rolling effect.

Do you mind posting the codes here? The codes I have intermingles a bit with other functionalities of our UI, so I am hesitatant to post it here.

If it is my own code, I have not successfully implemented it before, and I almost give up the code.
Can you provide an example? Even if it’s a small label that shows text scrolling out of the control, I’d love to.
:heart_eyes:

you can further expand the ScrollablePanel here by overriding OnPaint event, MouseDown, MouseMove…etc

    class ScrollablePanel : Drawable
    {
        //variables here
        public ScrollablePanel(Control control)
        {
            _control = control;
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            OnSizeChanged(e);
            _control.Width = _displayFrameWidth - Padding.Horizontal;
            _layout = new PixelLayout() { };
            _layout.Add(_control, 0, _currentLocation);
            Content = _layout;
        }
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (!activateScrollBar) return;

            var moveDelta = (int)e.Delta.Height * scrollFactor;
            _currentLocation += moveDelta;
            CheckScrollBarExceedsBounds(ref _currentLocation);

            _layout.Move(_control, new Point(0, _currentLocation));

        private bool CheckScrollBarExceedsBounds(ref int _location)
        {
            if (_location >= 1)
            {
                _location = 0;
                return true;
            }
            else if (_displayFrameHeight - _control.GetPreferredSize().Height - scrollBuffer > _location)
            {
                _location = _displayFrameHeight - (int)_control.GetPreferredSize().Height - scrollBuffer;
                return true;
            }
            else return false;
        }
3 Likes

@Wiley Thank yout very much!!!
The important thing is “PixelLayout”. I think I can make the effect I want. Really heartfelt thanks!
:smiling_face_with_three_hearts:

image
@Wiley
Another question, when I move the controls in PixelLayout, how can I display the remaining controls? Can you give me a hint? thank you!

@Easy
Try setting the Height of PixelLayout to -1.

Best,
Wiley

It doesn’t seem to work. It’s like being restricted by something. I’ll study it again. Anyway, thank you for your help!

try this:

foreach(var child in Children)
     if (child is Panel panel)
          panel.Height = -1;

This is the code used for testing. I don’t know what was done wrong.
I’ll try something else.

    public class TestScrollableView : Panel
    {
        public TestScrollableView()
        {
            var layout = new DynamicLayout() { Height = -1 };
            var dataLayout = new DynamicLayout { Height = -1 };
            for (int i = 0; i < 100; i++)
            {
                dataLayout.Add(new Label { Text = i.ToString() });
            }

            var sp = new ScrollablePanel(dataLayout);
            sp.Content = dataLayout;
            layout.Add(sp);

            Content = layout;
        }
    }

    class ScrollablePanel : Drawable
    {
        Control _control;
        PixelLayout _layout;

        //variables here
        public ScrollablePanel(Control control)
        {
            _control = control;
        }

        int _currentLocation = 0;
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            OnSizeChanged(e);
            _layout = new PixelLayout() { Height = -1 };
            _layout.Add(_control, 0, _currentLocation);
            Content = _layout;
            foreach (var child in Children)
                if (child is Panel panel)
                    panel.Height = -1;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            _currentLocation -= (int)e.Delta.Height * 5;
            _layout.Move(_control, 0, _currentLocation);
            _layout.Invalidate();
        }

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

I remember I had huge troubles working with sizing in Eto when I first began.
I do not know how that happens, but should be very easy to debug:

  1. Check the height of this using a debug session.
    var sp = new ScrollablePanel(dataLayout);

  2. Delete unnecessary lines such as:
    OnSizeChanged(e);
    _layout.Invalidate();

  3. Some great pointers for you in regards to Eto sizing:
    Always avoid working with sizing in OnLoad function. It gets confusing to calculate the actual preferred sizing when the Control hasn’t been loaded yet. Put the lines that work with sizing in either the constructor or in OnSizeChanged.

Hi,
I am actually trying to code something very similar today and your problem came to me.

This is how I solved it.

        protected override void OnPreLoad(EventArgs e)
{
            base.OnPreLoad(e);
            _control.SizeChanged += _control_SizeChanged;
}
        private void _control_SizeChanged(object sender, EventArgs e)
{
            var controlHeight = (int) _control.GetPreferredSize().Height;
            if (_displayFrameHeight >= 0 && controlHeight > _displayFrameHeight) Height = controlHeight;
}

I have a variable called _displayFrameHeight which I use to determine how big the scrollbar should be in proportion to the container bounds. _displayFrameHeight needs to be >=0 because the parent control is calculated after the children controls, and we need to make sure the size is correctly calculated before working out the Height for the ScrollablePanel.

I finally know what the reason is, because there is a Scrollable inside the RhinoPanel, which has an almost unlimited height, so no matter how high we set the size, it will not be limited! While I tested it in the window, it wasn’t wrapped with Scrollable, so its height was limited. If I did:

var sp = new ScrollablePanel(dataLayout);
            sp.Content = dataLayout;
sp.Height = (int)dataLayout.GetPreferredSize().Height;
var scrollable = new Scrollable{ Content = sp};
layout.Add(scrollable);

image

This is a success. Thanks for your help!
-Easy