Glide DataGrid scrolling question

Was anyone able to implement Glide DataGrid scrolling by sliding finger across the screen instead of using scrollbar? The scrolbas are not touchscreen friendly, so I’m currently using the buttons for paging in my app but it would be nice to have a finger scrolling also.

Just asking before looking into it myself as I don’t want to reinvent something that was already done.

I could post the code for my ScrollableControl base class if you want to adapt it.

Yes, it will definitely help.

@ EvoMotors - Keep in mind, this is not written for Glide so you will need to do some alteration.

Notes:

  1. If scrolling is needed it will automatically display the scrollbar for 1 second and then hide it.
  2. When you scroll (just by touchmove) scrollbar(s) will again automatically appear and disappear after you let go.
  3. Scroll needs are determined by RequiredHeight/Width and AvailableHeight/Width.
  4. Available height/width is the actual size of the control
  5. Required height/width is the area needed to display the entire contents, it is up to you to set these values.
using System;
using System.Threading;

using Microsoft.SPOT;

namespace Skewworks.Artifact
{
	[Serializable]
	public class ScrollableControl : Control
	{

		#region Variables

		// Scrolling
		private int _rqWidth;
		private int _rqHeight;
		private int _scrX, _scrY;
		private bool _needsX, _needsY;
		private int _sqW, _sqH;
		internal bool _showScroll;
		private bool _bMoving;
		private int _horzW, _horzSize, _horzX;
		private int _vertH, _vertSize, _vertY;
		private Thread _thAutoHide;
		private long _killTime;

		#endregion

		#region Properties

		public int MaxScrollX
		{
			get { return _rqWidth; }
		}

		public int MaxScrollY
		{
			get { return _rqHeight; }
		}

		protected int RequiredHeight
		{
			get { return _rqHeight; }
			set
			{
				if (_rqHeight == value)
					return;
				_rqHeight = value;
				UpdateNeeds();
			}
		}

		protected int RequiredWidth
		{
			get { return _rqWidth; }
			set
			{
				if (_rqWidth == value)
					return;
				_rqWidth = value;
				UpdateNeeds();
			}
		}

		public bool Scrolling
		{
			get { return _bMoving; }
		}

		public virtual int ScrollX
		{
			get { return _scrX; }
			set
			{
				if (value < 0)
					value = 0;
				else if (value > _sqW)
					value = _sqW;

				if (!_needsX || _scrX == value)
					return;

				_scrX = value;
				SetAutoHide();
				Invalidate();
			}
		}

		public virtual int ScrollY
		{
			get { return _scrY; }
			set
			{
				if (value < 0)
					value = 0;
				else if (value > _sqH)
					value = _sqH;

				if (!_needsY || _scrY == value)
					return;

				_scrY = value;
				SetAutoHide();
				Invalidate();
			}
		}

		#endregion

		#region Touch & Mouse

		public override void MouseMove (Point pt, ref bool handled)
		{
			if (HIDMove(pt, ref handled))
				base.MouseMove(pt, ref handled);
		}

		public override void TouchMove (Point pt, ref bool handled)
		{
			if (HIDMove(pt, ref handled))
				base.TouchMove(pt, ref handled);
		}

		private bool HIDMove (Point e, ref bool handled)
		{
			if (Touching || MouseDown)
			{

				if (_needsX || _needsY)
				{
					bool doInvalidate = false;
					SetAutoHide();

					int dest = _scrY - (e.Y - LastTouch.Y);
					if (dest < 0)
						dest = 0;
					else if (dest > _rqHeight - Height)
						dest = _rqHeight - Height;
					if (_scrY != dest && dest >= 0)
					{
						_scrY = dest;
						doInvalidate = true;
					}

					dest = _scrX - (e.X - LastTouch.X);
					if (dest < 0)
						dest = 0;
					else if (dest > _rqWidth - Width)
						dest = _rqWidth - Width;
					if (_scrX != dest && dest > 0)
					{
						_scrX = dest;
						doInvalidate = true;
					}

					handled = true;
					LastTouch = e;
					if (doInvalidate)
					{
						_bMoving = true;
						UpdateValues();
					}
					else
						return true;
				}
				else
					return true;

			}

			return false;
		}

		public override void TouchUp (Point pt, ref bool handled)
		{
			base.TouchUp(pt, ref handled);
			_bMoving = false;
		}

		#endregion

		#region Scrollbar Methods

		public override void Render (Bitmap dest, int x, int y, int width, int height, bool flush = false)
		{
			if (_showScroll)
				RenderScrollbar(dest, x, y, width, height);
			if (flush)
				Kernel.Flush(dest, x, y, width, height);
		}

		private void AutoHideScrollbar ()
		{
			while (true)
			{
				Thread.Sleep(100);
				if (DateTime.Now.Ticks >= _killTime)
				{
					_showScroll = false;
					Invalidate();
					break;
				}
			}

			_thAutoHide = null;
		}

		internal void RenderScrollbar (Bitmap dest, int x, int y, int width, int height)
		{

			if (_needsX)
			{
				int y2 = y + height - 4;
				dest.DrawRectangle(0, 0, x, y2, _horzW, 4, 0, 0, Colors.Black, 0, 0, Colors.Black, 0, 0, 128);
				dest.DrawRectangle(0, 0, x + _horzX, y2, _horzSize, 4, 0, 0, Colors.White, 0, 0, Colors.White, 0, 0, 128);
			}

			if (_needsY)
			{
				int x2 = x + width - 4;
				dest.DrawRectangle(0, 0, x2, y, 4, _vertH, 0, 0, Colors.Black, 0, 0, Colors.Black, 0, 0, 128);
				dest.DrawRectangle(0, 0, x2, y + _vertY, 4, _vertSize, 0, 0, Colors.White, 0, 0, Colors.White, 0, 0, 128);
			}
		}

		protected void UpdateScrollbar (bool invalidate = true)
		{
			UpdateNeeds(invalidate);
		}

		private void UpdateNeeds (bool invalidate = true)
		{
			bool nX = false;
			bool nY = false;

			if (_rqWidth > Width)
				nX = true;
			if (_rqHeight > Height)
				nY = true;

			if (nX || nY)
			{
				_needsX = nX;
				_needsY = nY;
				_sqW = _rqWidth - Width;
				_sqH = _rqHeight - Height;
				SetAutoHide();
				UpdateValues(invalidate);
			}
		}

		private void UpdateValues (bool invalidate = true)
		{
			int w = Width;
			int h = Height;

			if (_needsX && _needsY)
			{
				w -= 4;
				h -= 4;
			}

			if (_needsX)
			{
				_horzW = w;
				_horzSize = _horzW - (_sqW / 4);
				if (_horzSize < 8)
					_horzSize = 8;
				_horzX = (int) ((_horzW - _horzSize) * ((float) _scrX / (float) _sqW));
			}

			if (_needsY)
			{
				_vertH = h;
				_vertSize = _vertH - (_sqH / 4);
				if (_vertSize < 8)
					_vertSize = 8;
				_vertY = (int) ((_vertH - _vertSize) * ((float) _scrY / (float) _sqH));
			}

			if (invalidate)
				Invalidate();
		}

		private void SetAutoHide ()
		{
			if (!_showScroll)
				_showScroll = true;
			_killTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond;
			UpdateValues(false);
			if (_thAutoHide != null && _thAutoHide.IsAlive)
				return;
			_thAutoHide = new Thread(AutoHideScrollbar);
			_thAutoHide.Start();
		}

		#endregion

	}
}

Example control deriving from ScrollableControl

using System;

using Microsoft.SPOT;
using Microsoft.SPOT.Presentation.Media;

namespace Skewworks.Artifact
{
	[Serializable]
	public class ListBox : ScrollableControl
	{

		#region Variables

		private ListItem[] _items;
		private bool _showChk, _showImg, _zebra;
		private int _selIndex, _lineHeight;
		private Font _font;
		private Color _brdr, _bkg, _fore, _selBkg, _selFore;

		#endregion

		#region Constructors

		public ListBox (string name, Font font, int x, int y, int width, int height)
			: base()
		{
			Name = name;
			_font = font;
			Defaults();
			Bounds = new Rect(x, y, width, height);
			UpdateLayout();
		}

		public ListBox (string name, Font font, Alignment align)
			: base()
		{
			Name = name;
			_font = font;
			this.Align = align;
			Defaults();
			UpdateLayout();
		}

		public ListBox (string name, Font font, Alignment align, Margin margin)
			: base()
		{
			Name = name;
			_font = font;
			this.Align = align;
			this.Margin = margin;
			Defaults();
			UpdateLayout();
		}

		#endregion

		#region Events

		public event OnSelectedIndexChanged SelectedIndexChanged;
		protected virtual void OnSelectedIndexChanged (object sender, int index)
		{
			if (SelectedIndexChanged != null)
				SelectedIndexChanged(sender, index);
		}

		#endregion

		#region Properties

		public Font Font
		{
			get { return _font; }
			set
			{
				if (_font == value)
					return;
				_font = value;
				UpdateLayout();
			}
		}

		public ListItem[] Items
		{
			get { return _items; }
			set
			{
				if (_items == value)
					return;
				_items = value;
				UpdateLayout();
			}
		}

		public int LineHeight
		{
			get { return _lineHeight; }
			set
			{
				if (_lineHeight == value)
					return;
				_lineHeight = value;
				UpdateLayout();
			}
		}

		public int SelectedIndex
		{
			get { return _selIndex; }
			set
			{
				if (_items == null)
					return;
				if (value > _items.Length - 1)
					value = _items.Length - 1;
				if (_selIndex == value)
					return;
				_selIndex = value;

				if (!ShowSelected())
					Invalidate();
				OnSelectedIndexChanged(this, _selIndex);
			}
		}

		public bool ShowCheckBoxes
		{
			get { return _showChk; }
			set
			{
				if (_showChk == value)
					return;
				_showChk = value;
				UpdateLayout();
			}
		}

		public bool ShowImages
		{
			get { return _showImg; }
			set
			{
				if (_showImg == value)
					return;
				_showImg = value;
				UpdateLayout();
			}
		}

		public bool ZebraStripes
		{
			get { return _zebra; }
			set
			{
				if (_zebra == value)
					return;
				_zebra = value;
				Invalidate();
			}
		}

		#endregion

		#region Public Methods

		public void AddItem (ListItem item)
		{
			// Update Array Size
			if (_items == null)
				_items = new ListItem[] { item };
			else
			{
				ListItem[] tmp = new ListItem[_items.Length + 1];
				Array.Copy(_items, tmp, _items.Length);
				tmp[tmp.Length - 1] = item;
				_items = tmp;
				tmp = null;
			}

			UpdateLayout();
			Invalidate();
		}

		public void AddItems (ListItem[] items)
		{
			if (items == null)
				return;

			this.Suspended = true;

			if (_items == null)
				_items = items;
			else
			{
				ListItem[] tmp = new ListItem[_items.Length + items.Length];
				Array.Copy(_items, tmp, _items.Length);
				Array.Copy(items, 0, tmp, _items.Length, items.Length);
				_items = tmp;
			}

			UpdateLayout();
			this.Suspended = false;
		}

		public void ClearItems ()
		{
			if (_items == null)
				return;
			_items = null;
			_selIndex = -1;
			UpdateLayout();
			Invalidate();
		}

		public void RemoveItem (ListItem item)
		{
			if (_items == null)
				return;

			for (int i = 0; i < _items.Length; i++)
			{
				if (_items[i] == item)
				{
					RemoveItemAt(i);
					return;
				}
			}
		}

		public void RemoveItemAt (int index)
		{
			if (_items == null || index < 0 || index >= _items.Length)
				return;

			if (_items.Length == 1)
			{
				ClearItems();
				return;
			}

			this.Suspended = true;

			ListItem[] tmp = new ListItem[_items.Length - 1];
			int c = 0;
			for (int i = 0; i < _items.Length; i++)
			{
				if (i != index)
					tmp[c++] = _items[i];
			}
			_items = tmp;

			UpdateLayout();
			this.Suspended = false;
		}

		#endregion

		#region Keyboard

		public override void KeyboardKey (KeyboardEventArgs e, ref bool handled)
		{
			if (!e.Pressed)
				return;

			switch (e.Key)
			{
				case Artifact.KeyboardKey.DownArrow:
					SelectedIndex += 1;
					break;
				case Artifact.KeyboardKey.UpArrow:
					SelectedIndex -= 1;
					break;
			}

			base.KeyboardKey(e, ref handled);
		}

		#endregion

		#region Mouse & Touch

		private void HIDDown (Point pt, ref bool handled)
		{
			if (!ScreenBounds.Contains(pt))
				return;

			pt.Y += ScrollY - Top;
			SelectedIndex = pt.Y / _lineHeight;
			handled = true;
		}

		public override void MouseButton (MouseEventArgs e, ref bool handled)
		{
			base.MouseButton(e, ref handled);
			if (e.Button == Artifact.MouseButton.Left && e.Pressed)
			{
				HIDDown(e.Location, ref handled);
			}

		}

		public override void MouseWheel (int change)
		{
			ScrollY -= change;
		}

		public override void TouchDown (Point pt, ref bool handled)
		{
			base.TouchDown(pt, ref handled);
			HIDDown(pt, ref handled);
		}

		#endregion

		#region GUI

		public override void Render (Bitmap dest, int x, int y, int width, int height, bool flush = false)
		{
			if (!ValidRenderState)
				return;

			dest.DrawRectangle(_brdr, 1, x, y, width, height, 0, 0, _bkg, 0, 0, _bkg, 0, 0, 256);

			if (_items == null)
			{
				RequiredHeight = 0;
				return;
			}

			dest.SetClippingRectangle(x + 1, y + 1, width - 2, height - 2);
			int y2 = y - ScrollY;
			int x2;
			Color c;

			for (int i = 0; i < _items.Length; i++)
			{
				if (y2 + _lineHeight >= y)
				{
					x2 = x + 4;
					c = _fore;

					if (i == _selIndex)
					{
						c = _selFore;
						dest.DrawRectangle(0, 0, x + 1, y2, width - 2, _lineHeight, 0, 0, _selBkg, 0, 0, _selBkg, 0, 0, 256);
					}

					if (_showChk)
					{
						dest.DrawRectangle(_brdr, 1, x2, y2 + (_lineHeight / 2) - 8, 16, 16, 0, 0, _bkg, 0, 0, _bkg, 0, 0, 256);
						x2 += 20;
					}

					if (_showImg && _items[i].Image != null)
					{
						dest.DrawImage(x2, y2 + (_lineHeight / 2 - _items[i].Image.Height / 2), _items[i].Image, 0, 0, _items[i].Image.Width, _items[i].Image.Height);
						x2 += _items[i].Image.Width + 4;
					}

					if (_items[i].Text != null)
						dest.DrawText(_items[i].Text, _font, c, x2, y2 + (_lineHeight / 2 - _font.Height / 2));
				}

				y2 += _lineHeight;

				if (y2 >= y + height)
					break;
			}

			RequiredHeight = _lineHeight * _items.Length + 2;
			dest.SetClippingRectangle(0, 0, dest.Width, dest.Height);

			base.Render(dest, x, y, width, height, flush);
		}

		#endregion

		#region Private Methods

		private void Defaults ()
		{
			_brdr = Colors.DarkGray;
			_bkg = Colors.White;
			_fore = Colors.CharcoalDust;
			_selBkg = Colors.LightBlue;
			_selFore = Colors.White;
		}

		private bool ShowSelected ()
		{
			int y = (_lineHeight * _selIndex) + _lineHeight - ScrollY;

			if (y < _lineHeight)
			{
				ScrollY += y - _lineHeight;
				return true;
			}
			else if (y > Height)
			{
				ScrollY += y - Height;
				return true;
			}


			return false;
		}

		private void UpdateLayout ()
		{
			int h;

			h = _font.Height + 4;
			if (_showChk)
				h = System.Math.Max(h, 20);
			if (_showImg && _items != null)
			{
				for (int i = 0; i < _items.Length; i++)
				{
					if (_items[i].Image != null)
						h = System.Math.Max(h, _items[i].Image.Height + 4);
				}
			}

			_lineHeight = h;
			Invalidate();
		}

		#endregion

	}
}

I’ve had to do the same with buttons as scrolling the datagrid with gestures or the scroll bars does not work. I’ve not dug into the code to find out why.

I am waiting for Skewworks to complete his Synth anyway :slight_smile: