Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

General Purpose MultiSlider

0.00/5 (No votes)
26 May 2017 1  
Comprehensive Multi-Slider(-Range) that can add, delete and move arrows (thumbs)

Download MultiSlider_W10_.zip

(Visual Studio 2017)
Note: Only minor changes would be needed to convert for earlier versions.

Introduction

As its name suggests, MultiSlider is a UserControl capable of displaying more than one arrow in a track bar. I have kept most of the behaviour and appearance of the stock Slider control and also added minor enhancements.

  • New arrows may be added (optional) by the user by double-clicking on the trackbar.
  • Arrows may be removed (optional) by the user by Alt+Right-clicking on them.
  • Arrows have an IsMoveable property; if it is false then they can neither be moved nor deleted (visually denoted by a colored adorner circle on the arrow).
  • Arrows have an IsUser property; if it is true then the brushes used for coloring the plain arrow will be different. The user arrow and the standard arrow are otherwise treated the same.
  • AutoToolTipPlacement is catered for.

Comments

When tabbing through the main window's controls using the Tab key or Modifier+Cursor:

  1. If an arrow is selected, Shift+Cursor may be used to tab over any adjacent arrows.
  2. The Tab key will NOT tab over any adjacent arrows, but only over the Controls.
  3. When a MultiSlider control is tabbed to, depressing the Space key will activate the prior selected arrow.
  4. Control+Cursor acts as per normal when tabbing over controls.

There are a few multi-slider tools out there:

  1. https://www.codeproject.com/Articles/626132/WPF-MultiRangeSlider-Control

    This is dated 2013 and uses Windows 7 arrows, so is out of date for W10 style. Limited functionality although can add new arrows.

  2. http://avaloncontrolslib.codeplex.com/

    This is even older (2007).

  3. http://www.telerik.com/products/wpf/slider.aspx?utm_source=google&utm_medium=cpc&gclid=CjwKEAjwl9DIBRCG_e3DwsKsizsSJADMmJ11IH1yTbk4LCKPBdn0Q9rsFBB0G-P1IpyNywq5hAi4_BoCpYfw_wcB&gclsrc=aw.ds&dclid=CKqYk9fy59MCFcKg7QodaLcK5g

    This looks as if it is for W7. I haven't investigated this one.

  4. http://wpftoolkit.codeplex.com/wikipage?title=RangeSlider&referringTitle=Home

    This is fixed at just 2 arrows so is not as flexible as desired, but looks quite good for what it is.

None of them are comprehensive enough for my use so I developed my own:

  • Event handling for the programmer is comprehensively catered for (see specs below).
  • I haven't included 'tick' graphics and capabilities (may be for a future date) but there is AutoToolTip capability.

Usage

There are two projects in the solution that will demonstrate the MultiSlider in action. The first, TestSimple, merely displays all the arrow types and use of overlay coloring with basic functionality. The second, TestMultiSlider, is more thorough, with:

  • Facilities for the user to manipulate arrows in all ways
  • Switches to change the states of the multi-sliders
  • A vertical and a horizontal oriented multi-slider
  • Readout of user actions taken in a TextBlock

Properties

  • AdornerColor - (get/set) - The color to use for the adorning circle of an arrow. If null is passed, the color is reset.
  • AdornerRatio - (get/set) - Set/get the ratio of the adorner circle's diameter relative to the Up arrow width. If null is passed, the ratio is reset.
  • ArrowAtIndex(int index) - Returns the ArrowData at position 'index' (base=0 starting from the Minimum end).
  • ArrowCount - (get) - The number of arrows in the trackbar.
  • ArrowType - (get/set) - MultiSlider.ArrowTypes enum. Values: LeftRight, UpDown or Rect.
  • AutoToolTipPlacement - (get/set) - Where to place the auto tool tip relative to the arrow.
  • AutoToolTipPrecision - (get/set) - Prints the arrow's Value to N decimal places.
  • AutoToolTipSigFigs - (get/set) - Prints the arrow's Value in N significant figures if N > 0 and takes precedence over AutoToolTipPrecision.
  • CanDelAddArrows - (get/set) - Whether the user can Add/Delete arrows with the mouse.
  • HelpText - (get) - A string containing basic formatted text giving the user instructions. Suitable for adding to and displaying in a MessageBox.
  • Minimum - (get/set) - Minimum value of an arrow in the trackbar. The setter deletes all existing arrows and the ArrowDeleted event is not fired.
  • Maximum - (get/set) - Maximum value of an arrow in the trackbar. The setter deletes all existing arrows and the ArrowDeleted event is not fired.
  • Orientation - (get/set) - Specifies either a vertical or horizontal axis for the trackbar.
  • ReverseDirection - (get/set) - Whether Maximum is at the maximum or minimum end of the trackbar (vice versa for Minimum).
  • SchemeOverlayColor - (get,set) - Overlay color to use on top of the trackbar and plain arrows (suggested alpha=10%).
  • SmallChange - (get/set) - The small incremental Value to add/subtract to/from the current arrow's value when using the keyboard cursor keys to move with.
  • LargeChange - (get/set) - The large incremental Value to add/subtract to/from the current arrow's value when using the keyboard cursor keys (+Alt key) to move with.
  • Value - (get/set) - The value for the first arrow on the trackbar. If the trackbar is empty, you get DoubleNaN; or the trackbar is ignored for set. This is useful for a non-deletable single-arrow slider.

Methods

  • CreateArrow(double value, bool isUser = false, bool isMoveable = true) - Create and show a new arrow. Returns an ArrowData.
    • "value">A value within Minimum and Maximum properties of the TrackBar.
    • "isUser">true: Use the arrow's User state brushes.
    • "isMoveable">false: The arrow can neither be moved nor deleted.
  • DeleteArrow(int index, bool fireEvent) - Removes the arrow at 'index' (from the trackbar canvas also. base=0 starting from the Minimum end). If 'fireEvent' = true, then the ArrowDeleted event is fired.
  • DeleteAllArrows(bool fireEvent) - Removes all arrows from the trackbar canvas. If 'fireEvent' = true, then the ArrowDeleted event is fired.
  • IsValidValue(value) - Whether the arg falls within the multi slider's Minimum/Maximum values.
  • ValueAtIndex(int index) - Return the Value at the valid arrow index (base 0 starting from the Minimum end) on the trackbar.

For the arrows (type ArrowData sent by the event handlers):

  • Index - (get) - Base 0. The nth arrow on the trackbar starting from the Minimum end.
  • IsMoveable - (get/set) - Whether an arrow can be moved/deleted. May be done on the fly, example in the event handlers below.
  • IsUser - (get/set) - Only the brushes to use when setting the 'plain' arrow brushes are different. May be done on the fly, for example, in the event handlers below.
  • Value - (get/set) - Value between {Minimum to Maximum}.

Events

  • ArrowCreated
  • ArrowRightClicked - If the Alt key is used for deleting an arrow, this will not be fired
  • ArrowScrolled
  • ArrowDeleted - ArrowSelected will fire also after this if the focus changes to an adjacent arrow
  • ArrowLeftClicked
  • ArrowSelected - Fired when an arrow receives keyboard focus (e.g. is clicked on or tabbed to). All pass their sender arg as an (object)ArrowData

Implementation Notes

If Minimum==Maximum => Behaviour is a little odd but acceptable.

There is a Conditional Compilation Symbol, MULTISLIDER, in the MultiSlider properties window; change this to prevent Console output when running the Debug version outside of the Debugger.

Construction of the MultiSlider Control

The control firstly has a Canvas representing the trackbar (as a Border) and its size is set to that of the user control. To this is first added a trackbar Border control. This Border's size is set relative to the width or height property of the MultiSlider. Then composite arrows, having been created and initialized, are added to the main trackbar Canvas.

An arrow consists of a Canvas which is added to the main trackbar; to this Canvas are added firstly an initialized Polygon describing the arrow, then an Ellipse (which acts as an adorner) and lastly a Label (which displays the AutoToolTip text). Whenever the arrow's visual position needs to be changed, it is the Canvas that is placed relative to the main trackbar Canvas; thus the Polygon's points are never accessed or changed.

Arrow Keyboard Focus using Shift+Cursor_key

Using Shift+Cursor key for tabbing over the arrows inside the control is achieved in method MultiSlider.Arrow_KeyDown().

Arrow Moving by Holding Mouse Down on the Trackbar

This is all done in the trackbar MouseLeftButtonDown/Up handlers. Inside TrackBar_MouseLeftButtonDown(). The predicate if(Mouse.LeftButton == MouseButtonState.Released) can't be used inside a loop because the reported Mouse button state remains unaltered when the mouse-button is released. The only technique that works is the following:

  • Deploy an iVar flag _mouseUp.
  • In TrackBar_MouseLeftButtonDown() set _mouseUp to false.
  • Write event handler, TrackBar_MouseLeftButtonUp() - sets _mouseUp to true.

Processing continuous holding mouse button down:

Write a BackgroundWorker.DoWork event handler, ProcessHoldingMouseDown(), with args set to the desired types to be passed by TrackBar_MouseLeftButtonDown(); this will do the mouse-button state detection and perform the arrow-moving operations.

In TrackBar_MouseLeftButtonDown() write the following code where the arrow moving is to be done:

BackgroundWorker threadBW = new BackgroundWorker();
threadBW.DoWork += (obj, e2) => ProcessHoldingMouseDown(mouseCoords, arrow);
threadBW.RunWorkerAsync();

And in ProcessHoldingMouseDown() the GUI main thread invoker is deployed several times, e.g., Dispatcher.Invoke(() => TrackBar.InitBounds(arrow));

Schematic Wiring

The behaviour of the MultiSlider requires quite intricate 'tuning' of the internal event handlers. Modify them at your own peril!

A Shape.Polygon

RenderBounds() doesn't take into account any Transforms (e.g., Scaling, Rotating) on the shape. Just includes the StrokeThickness, etc., so when the arrow’s Polygon is rendered, it exactly fits its containing Canvas.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here