2023-09-24 17:26:46 -04:00
using System.ComponentModel ;
2017-11-27 13:11:20 -05:00
using System.Diagnostics ;
using System.Drawing ;
using System.Linq ;
using System.Reflection ;
using System.Windows.Forms ;
using Vanara.PInvoke ;
using static Vanara . PInvoke . ComCtl32 ;
using static Vanara . PInvoke . Macros ;
2019-08-27 18:03:21 -04:00
using static Vanara . PInvoke . User32 ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
namespace Vanara.Windows.Forms ;
/// <summary>
/// Extends the <see cref="TrackBar"/> class to provide full native-control functionality, including tick marks and value, and custom drawing.
/// </summary>
2023-09-29 13:58:35 -04:00
/// <seealso cref="TrackBar"/>
2023-03-31 11:47:53 -04:00
public class TrackBarEx : TrackBar
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
private bool autoTicks = true ;
private int cumulativeWheelData ;
private int lastValue ;
private bool limitThumbToSel , showSel ;
private int requestedDim ;
private int selMin , selMax , thumbLength = - 1 ;
2023-09-29 13:58:35 -04:00
private int [ ] ticks = new int [ 0 ] ;
2023-03-31 11:47:53 -04:00
/// <summary>Initializes a new instance of the <see cref="TrackBarEx"/> class.</summary>
public TrackBarEx ( )
{
SetStyle ( ControlStyles . SupportsTransparentBackColor , true ) ;
BackColorChanged + = ( o , a ) = > { if ( IsHandleCreated ) RecreateHandle ( ) ; } ;
requestedDim = PreferredDimension ;
}
2020-05-28 21:35:39 -04:00
/// <summary>
2023-03-31 11:47:53 -04:00
/// Occurs when the channel for a <see cref="TrackBarEx"/> needs to be drawn and the <see cref="OwnerDraw"/> property is set to <c>true</c>.
2020-05-28 21:35:39 -04:00
/// </summary>
2023-03-31 11:47:53 -04:00
[Category("Drawing")]
2023-09-29 13:58:35 -04:00
public event PaintEventHandler ? DrawChannel ;
2023-03-31 11:47:53 -04:00
/// <summary>
/// Occurs when the thumb for a <see cref="TrackBarEx"/> needs to be drawn and the <see cref="OwnerDraw"/> property is set to <c>true</c>.
/// </summary>
[Category("Drawing")]
2023-09-29 13:58:35 -04:00
public event PaintEventHandler ? DrawThumb ;
2023-03-31 11:47:53 -04:00
/// <summary>
/// Occurs when the ticks for a <see cref="TrackBarEx"/> need to be drawn and the <see cref="OwnerDraw"/> property is set to <c>true</c>.
/// </summary>
[Category("Drawing")]
2023-09-29 13:58:35 -04:00
public event PaintEventHandler ? DrawTics ;
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets a value indicating whether to draw ticks based on the <see cref="TrackBar.TickFrequency"/> interval.</summary>
/// <value><c>true</c> if automatic ticks should be shown; otherwise, <c>false</c>.</value>
[DefaultValue(true), Category("Appearance"), Description("Specifies whether to automatically draw tick marks at the frequency defined by TickFreqency.")]
public bool AutoTicks
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
get = > autoTicks ;
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( autoTicks = = value ) return ;
autoTicks = value ;
if ( IsHandleCreated ) RecreateHandle ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets the bounds of the channel (slider track).</summary>
/// <value>The channel bounds.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Rectangle ChannelBounds
{
get
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var r = new RECT ( ) ;
if ( IsHandleCreated )
SendMsg ( TrackBarMessage . TBM_GETCHANNELRECT , ref r ) ;
return r ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets a value indicating whether to limit thumb movement to the selection.</summary>
/// <value><c>true</c> if movement is limited to selection; otherwise, <c>false</c>.</value>
[DefaultValue(false), Category("Appearance"), Description("Indicates if thumb movement is limited to the selection range.")]
public bool LimitThumbToSelection
{
get = > limitThumbToSel ;
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( limitThumbToSel = = value ) return ;
limitThumbToSel = value ;
if ( ! showSel ) return ;
if ( Value < SelectionStart )
Value = SelectionStart ;
else if ( Value > SelectionEnd )
Value = SelectionEnd ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets a value indicating the horizontal or vertical orientation of the track bar.</summary>
[DefaultValue(Orientation.Horizontal), Category("Appearance"), Description("Indicates orientation of the trackbar.")]
public new Orientation Orientation
{
get = > base . Orientation ;
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
//valid values are 0x0 to 0x1
2023-09-29 13:58:35 -04:00
if ( value is not Orientation . Horizontal and not Orientation . Vertical )
2023-03-31 11:47:53 -04:00
throw new InvalidEnumArgumentException ( nameof ( Orientation ) , ( int ) value , typeof ( Orientation ) ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( base . Orientation ! = value )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( value = = Orientation . Horizontal )
Width = requestedDim ;
else
Height = requestedDim ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
base . Orientation = value ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( IsHandleCreated )
AdjustSize ( ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
/// <summary>Gets or sets a value indicating whether the control is drawn by the operating system or by code that you provide.</summary>
/// <value>
/// <c>true</c> if the control is drawn by code that you provide; <c>false</c> if the control is drawn by the operating system. The
/// default is <c>false</c>.
/// </value>
[DefaultValue(false), Category("Behavior"), Description("Specifies whether to custom draw the trackbar.")]
public bool OwnerDraw { get ; set ; }
/// <summary>Gets or sets the upper limit of the selection range this TrackBar is working with.</summary>
/// <value>
/// The logical position at which the selection ends. This value must be less than or equal to the value of the <see
/// cref="TrackBar.Maximum"/> property.
/// </value>
[DefaultValue(0), Category("Behavior"), Description("The ending logical position of the current selection range in a trackbar.")]
public int SelectionEnd
{
get = > selMax ;
set = > SetSelectionRange ( selMin , value ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets the lower limit of the selection range this TrackBar is working with.</summary>
/// <value>
/// The logical position at which the selection starts. This value must be greater than or equal to the value of the <see
/// cref="TrackBar.Minimum"/> property.
/// </value>
[DefaultValue(0), Category("Behavior"), Description("The starting logical position of the current selection range in a trackbar.")]
public int SelectionStart
{
get = > selMin ;
set = > SetSelectionRange ( value , selMax ) ;
}
/// <summary>
/// Gets or sets a value indicating whether to show the selection area defined by <see cref="SelectionStart"/> and <see cref="SelectionEnd"/>.
/// </summary>
/// <value><c>true</c> if showing selection area; otherwise, <c>false</c>.</value>
[DefaultValue(false), Category("Appearance"), Description("Indicates if the TaskBar shows a selection range.")]
public bool ShowSelection
{
get = > showSel ;
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( showSel = = value ) return ;
showSel = value ;
if ( IsHandleCreated ) RecreateHandle ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets the bounds of the thumb slider in its current position.</summary>
/// <value>The thumb bounds.</value>
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Rectangle ThumbBounds
{
get
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var r = new RECT ( ) ;
if ( IsHandleCreated )
SendMsg ( TrackBarMessage . TBM_GETTHUMBRECT , ref r ) ;
return r ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets the length of the thumb, overriding the default.</summary>
/// <value>
/// The length of the thumb. This is the vertical length if the orientation is horizontal and the horizontal length if the
/// orientation is vertical.
/// </value>
[Category("Appearance"), Description("The length of the slider in a trackbar.")]
public int ThumbLength
{
get = > IsHandleCreated ? SendMsg ( TrackBarMessage . TBM_GETTHUMBLENGTH ) . ToInt32 ( ) : ( thumbLength = = - 1 ? ( TickStyle = = TickStyle . Both ? 21 : 19 ) : thumbLength ) ;
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( thumbLength = = value ) return ;
thumbLength = value ;
if ( IsHandleCreated ) RecreateHandle ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Gets or sets an array that contains the positions of the tick marks for a trackbar.</summary>
/// <value>
/// The elements of the array specify the logical positions of the trackbar's tick marks, not including the first and last tick
/// marks created by the trackbar. The logical positions can be any of the integer values in the trackbar's range of minimum to
/// maximum slider positions.
/// </value>
[Category("Appearance"), Description("Indicates the logical values of the trackbar where ticks are drawn.")]
[Browsable(false)]
//[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
//[Editor(typeof(ArrayEditor), typeof(UITypeEditor))]
//[TypeConverter(typeof(ArrayConverter))]
public int [ ] TickPositions
{
get
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var tickCnt = 0 ;
if ( ! IsHandleCreated | | TickStyle = = TickStyle . None | | 0 = = ( tickCnt = SendMsg ( TrackBarMessage . TBM_GETNUMTICS ) . ToInt32 ( ) ) )
return new int [ 0 ] ;
var ptr = SendMsg ( TrackBarMessage . TBM_GETPTICS ) ;
return ptr . ToIEnum < int > ( tickCnt - 2 ) . OrderBy ( i = > i ) . Distinct ( ) . ToArray ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( value ! = null & & value . Length > 0 )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( TickStyle = = TickStyle . None ) throw new ArgumentException ( "Tick positions cannot be set when TickStyle is None." ) ;
if ( value . Min ( ) < Minimum | | value . Max ( ) > Maximum ) throw new ArgumentOutOfRangeException ( nameof ( TickPositions ) , "All values must be between Minimum and Maximum range values." ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
ticks = value ? ? new int [ 0 ] ;
if ( IsHandleCreated )
RecreateHandle ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <inheritdoc/>
protected override CreateParams CreateParams
{
get
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var cp = base . CreateParams ;
cp . Style | = ( int ) TrackBarStyle . TBS_NOTIFYBEFOREMOVE ;
cp . Style = showSel ? cp . Style | ( int ) TrackBarStyle . TBS_ENABLESELRANGE : cp . Style & ~ ( int ) TrackBarStyle . TBS_ENABLESELRANGE ;
cp . Style = BackColor = = Color . Transparent ? cp . Style | ( int ) TrackBarStyle . TBS_TRANSPARENTBKGND : cp . Style & ~ ( int ) TrackBarStyle . TBS_TRANSPARENTBKGND ;
cp . Style = thumbLength > = 0 ? cp . Style | ( int ) TrackBarStyle . TBS_FIXEDLENGTH : cp . Style & ~ ( int ) TrackBarStyle . TBS_FIXEDLENGTH ;
cp . Style = autoTicks & & TickStyle ! = TickStyle . None ? cp . Style | ( int ) TrackBarStyle . TBS_AUTOTICKS : cp . Style & ~ ( int ) TrackBarStyle . TBS_AUTOTICKS ;
return cp ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
/// <inheritdoc/>
2023-09-24 17:26:46 -04:00
protected override Size DefaultSize = > new ( 104 , PreferredDimension ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private int PreferredDimension
{
get
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var track = TickStyle = = TickStyle . None ? 6 : ( TickStyle = = TickStyle . Both ? 22 : 14 ) ;
return ThumbLength + track ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Sets the starting and ending positions for the available selection range in a trackbar.</summary>
/// <param name="rangeMin">The starting logical position for the selection range.</param>
/// <param name="rangeMax">The ending logical position for the selection range.</param>
/// <param name="redrawAll">
/// If this parameter is TRUE, the trackbar is redrawn after the selection range is set. If this parameter is FALSE, the message
/// sets the selection range but does not redraw the trackbar.
/// </param>
public void SetSelectionRange ( int rangeMin , int rangeMax , bool redrawAll = true )
{
if ( rangeMin = = selMin & & rangeMax = = selMax ) return ;
if ( rangeMin < 0 ) throw new ArgumentOutOfRangeException ( nameof ( rangeMin ) ) ;
if ( rangeMax < 0 | | rangeMax < rangeMin ) throw new ArgumentOutOfRangeException ( nameof ( rangeMax ) ) ;
if ( rangeMin = = rangeMax )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
ShowSelection = false ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
else
{
ShowSelection = true ;
if ( rangeMin < Minimum ) rangeMin = Minimum ;
if ( rangeMin > Maximum ) rangeMax = Maximum ;
}
selMin = rangeMin ;
selMax = rangeMax ;
SendMsg ( TrackBarMessage . TBM_SETSELEND , 0 , rangeMax ) ;
SendMsg ( TrackBarMessage . TBM_SETSELSTART , redrawAll ? 1 : 0 , rangeMin ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Adjusts the size of the control.</summary>
protected virtual void AdjustSize ( )
{
if ( IsHandleCreated )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var saveDim = requestedDim ;
try
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( Orientation = = Orientation . Horizontal )
Height = AutoSize ? PreferredDimension : saveDim ;
else
Width = AutoSize ? PreferredDimension : saveDim ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
finally
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
requestedDim = saveDim ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <inheritdoc/>
protected override void OnAutoSizeChanged ( EventArgs e )
{
base . OnAutoSizeChanged ( e ) ;
AdjustSize ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Raises the <see cref="E:DrawChannel"/> event.</summary>
/// <param name="pe">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
/// <returns>
/// If overwritten, the method should return <c>true</c> to indicate that all drawing has been done by the method and the system
/// should not draw this item. If this method returns <c>false</c>, the system will draw the item.
/// </returns>
protected virtual bool OnDrawChannel ( PaintEventArgs pe )
{
DrawChannel ? . Invoke ( this , pe ) ;
return DrawChannel ! = null ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Raises the <see cref="E:DrawThumb"/> event.</summary>
/// <param name="pe">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
/// <returns>
/// If overwritten, the method should return <c>true</c> to indicate that all drawing has been done by the method and the system
/// should not draw this item. If this method returns <c>false</c>, the system will draw the item.
/// </returns>
protected virtual bool OnDrawThumb ( PaintEventArgs pe )
{
DrawThumb ? . Invoke ( this , pe ) ;
return DrawThumb ! = null ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Raises the <see cref="E:DrawTics"/> event.</summary>
/// <param name="pe">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
/// <returns>
/// If overwritten, the method should return <c>true</c> to indicate that all drawing has been done by the method and the system
/// should not draw this item. If this method returns <c>false</c>, the system will draw the item.
/// </returns>
protected virtual bool OnDrawTics ( PaintEventArgs pe )
{
DrawTics ? . Invoke ( this , pe ) ;
return DrawTics ! = null ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <inheritdoc/>
protected override void OnHandleCreated ( EventArgs e )
{
base . OnHandleCreated ( e ) ;
if ( showSel )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
SendMsg ( TrackBarMessage . TBM_SETSELEND , 0 , selMax ) ;
SendMsg ( TrackBarMessage . TBM_SETSELSTART , 1 , selMin ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( thumbLength > = 0 )
SendMsg ( TrackBarMessage . TBM_SETTHUMBLENGTH , thumbLength ) ;
SendMsg ( TrackBarMessage . TBM_CLEARTICS ) ;
if ( ticks ! = null & & TickStyle ! = TickStyle . None )
foreach ( var t in ticks )
SendMsg ( TrackBarMessage . TBM_SETTIC , 0 , t ) ;
AdjustSize ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Raises the <see cref="Control.MouseWheel"/> event.</summary>
/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
protected override void OnMouseWheel ( MouseEventArgs e )
{
const int WHEEL_DELTA = 120 ;
2023-09-29 13:58:35 -04:00
if ( e is HandledMouseEventArgs hme )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( hme . Handled ) return ;
hme . Handled = true ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( ( ModifierKeys & ( Keys . Shift | Keys . Alt ) ) ! = 0 | | MouseButtons ! = MouseButtons . None )
return ; // Do not scroll when Shift or Alt key is down, or when a mouse button is down.
2019-11-24 19:08:47 -05:00
2023-03-31 11:47:53 -04:00
var wheelScrollLines = SystemInformation . MouseWheelScrollLines ;
if ( wheelScrollLines = = 0 ) return ; // Do not scroll when the user system setting is 0 lines per notch
2019-11-24 19:08:47 -05:00
2023-03-31 11:47:53 -04:00
Debug . Assert ( cumulativeWheelData > - WHEEL_DELTA , "cumulativeWheelData is too small" ) ;
Debug . Assert ( cumulativeWheelData < WHEEL_DELTA , "cumulativeWheelData is too big" ) ;
cumulativeWheelData + = e . Delta ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
var partialNotches = cumulativeWheelData / ( float ) WHEEL_DELTA ;
2019-11-24 19:08:47 -05:00
2023-03-31 11:47:53 -04:00
if ( wheelScrollLines = = - 1 )
wheelScrollLines = TickFrequency ;
2019-11-24 19:08:47 -05:00
2023-03-31 11:47:53 -04:00
// Evaluate number of bands to scroll
var scrollBands = ( int ) ( wheelScrollLines * partialNotches ) ;
2019-11-24 19:08:47 -05:00
2023-03-31 11:47:53 -04:00
if ( scrollBands ! = 0 )
{
int absScrollBands ;
if ( scrollBands > 0 )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
absScrollBands = scrollBands ;
Value = Math . Min ( absScrollBands + Value , showSel & & limitThumbToSel ? selMax : Maximum ) ;
cumulativeWheelData - = ( int ) ( scrollBands * ( WHEEL_DELTA / ( float ) wheelScrollLines ) ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
else
2019-11-24 19:08:47 -05:00
{
2023-03-31 11:47:53 -04:00
absScrollBands = - scrollBands ;
Value = Math . Max ( Value - absScrollBands , showSel & & limitThumbToSel ? selMin : Minimum ) ;
cumulativeWheelData - = ( int ) ( scrollBands * ( WHEEL_DELTA / ( float ) wheelScrollLines ) ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
if ( e . Delta ! = Value )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
OnScroll ( EventArgs . Empty ) ;
OnValueChanged ( EventArgs . Empty ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Raises the <see cref="E:System.Windows.Forms.TrackBar.ValueChanged"/> event.</summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> that contains the event data.</param>
protected override void OnValueChanged ( EventArgs e )
{
base . OnValueChanged ( e ) ;
Debug . WriteLine ( $">> TB ValueChg={Value} from {lastValue}" ) ;
lastValue = Value ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Sends the supplied message and parameters to the underlying TRACKBAR system control.</summary>
/// <param name="msg">The Windows message identifier.</param>
/// <param name="wParam">The wParam.</param>
/// <param name="lParam">The lParam.</param>
/// <returns>The result value defined for the message.</returns>
protected IntPtr SendMsg ( TrackBarMessage msg , int wParam = 0 , int lParam = 0 ) = > IsHandleCreated ? SendMessage ( Handle , ( uint ) msg , ( IntPtr ) wParam , ( IntPtr ) lParam ) : IntPtr . Zero ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <inheritdoc/>
protected override void SetBoundsCore ( int x , int y , int width , int height , BoundsSpecified specified )
{
requestedDim = Orientation = = Orientation . Horizontal ? height : width ;
if ( AutoSize )
{
if ( Orientation = = Orientation . Horizontal )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( ( specified & BoundsSpecified . Height ) ! = BoundsSpecified . None )
height = PreferredDimension ;
}
else
{
if ( ( specified & BoundsSpecified . Width ) ! = BoundsSpecified . None )
width = PreferredDimension ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
// Call base method on Control and not TrackBar
2023-09-29 13:58:35 -04:00
typeof ( Control ) . GetMethod ( "SetBoundsCore" , BindingFlags . Instance | BindingFlags . NonPublic ) ? . InvokeNotOverride ( this , x , y , width , height , specified ) ;
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <inheritdoc/>
protected override void WndProc ( ref Message m )
{
var msg = ( WindowMessage ) m . Msg ;
//Debug.WriteLine($"TB WndProc: Msg={msg}");
if ( msg = = ( WindowMessage . WM_NOTIFY | WindowMessage . WM_REFLECT ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( OwnerDraw )
2017-11-27 13:11:20 -05:00
{
2023-09-29 13:58:35 -04:00
var hdr = ( NMHDR ) m . GetLParam ( typeof ( NMHDR ) ) ! ;
2023-03-31 11:47:53 -04:00
if ( hdr . code = = ( int ) CommonControlNotification . NM_CUSTOMDRAW )
2017-11-27 13:11:20 -05:00
{
2023-09-29 13:58:35 -04:00
var cd = ( NMCUSTOMDRAW ) m . GetLParam ( typeof ( NMCUSTOMDRAW ) ) ! ;
2023-03-31 11:47:53 -04:00
Debug . WriteLine ( $"{new TimeSpan(Environment.TickCount)} TBCustDraw: {cd.dwDrawStage}, {cd.dwItemSpec.ToInt32()}, {cd.uItemState}, {(Rectangle)cd.rc}" ) ;
m . Result = new IntPtr ( ( int ) CustomDraw ( ref cd ) ) ;
return ;
2017-11-27 13:11:20 -05:00
}
}
}
2023-09-29 13:58:35 -04:00
else if ( msg is ( WindowMessage . WM_HSCROLL | WindowMessage . WM_REFLECT ) or ( WindowMessage . WM_VSCROLL | WindowMessage . WM_REFLECT ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var pos = Value ;
var code = ( TrackBarScrollNotification ) LOWORD ( m . WParam ) ;
Debug . WriteLine ( $"TB_SCROLL: pos={pos}, lastPos={lastValue}, code={code}, sel={showSel}, limit={limitThumbToSel}, selMin={selMin}, selMax={selMax}" ) ;
if ( showSel & & limitThumbToSel )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( pos > selMax ) SendMsg ( TrackBarMessage . TBM_SETPOS , 1 , selMax ) ;
else if ( pos < selMin ) SendMsg ( TrackBarMessage . TBM_SETPOS , 1 , selMin ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( code ! = TrackBarScrollNotification . TB_THUMBPOSITION & & code ! = TrackBarScrollNotification . TB_ENDTRACK & & lastValue ! = Value )
{
var e = new ScrollEventArgs ( ( ScrollEventType ) code , lastValue , pos , m . Msg = = ( int ) WindowMessage . WM_HSCROLL ? ScrollOrientation . HorizontalScroll : ScrollOrientation . VerticalScroll ) ;
OnScroll ( e ) ;
OnValueChanged ( EventArgs . Empty ) ;
}
return ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
base . WndProc ( ref m ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private CustomDrawResponse CustomDraw ( ref NMCUSTOMDRAW cd )
{
switch ( cd . dwDrawStage )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
case CustomDrawStage . CDDS_PREPAINT :
return CustomDrawResponse . CDRF_NOTIFYITEMDRAW ; // | CustomDrawResponse.CDRF_NOTIFYPOSTPAINT;
case CustomDrawStage . CDDS_ITEMPREPAINT :
switch ( ( TrackBarCustomDraw ) cd . dwItemSpec . ToInt32 ( ) )
{
case TrackBarCustomDraw . TBCD_CHANNEL :
using ( var g = Graphics . FromHdc ( ( IntPtr ) cd . hdc ) )
{
if ( OnDrawChannel ( new PaintEventArgs ( g , cd . rc ) ) )
return CustomDrawResponse . CDRF_SKIPDEFAULT ;
}
break ;
case TrackBarCustomDraw . TBCD_THUMB :
using ( var g = Graphics . FromHdc ( ( IntPtr ) cd . hdc ) )
{
if ( OnDrawThumb ( new PaintEventArgs ( g , cd . rc ) ) )
return CustomDrawResponse . CDRF_SKIPDEFAULT ;
}
break ;
case TrackBarCustomDraw . TBCD_TICS :
using ( var g = Graphics . FromHdc ( ( IntPtr ) cd . hdc ) )
{
if ( OnDrawTics ( new PaintEventArgs ( g , cd . rc ) ) )
return CustomDrawResponse . CDRF_SKIPDEFAULT ;
}
break ;
}
break ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
return CustomDrawResponse . CDRF_DODEFAULT ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private void ResetThumbLength ( )
{
thumbLength = - 1 ;
if ( IsHandleCreated ) RecreateHandle ( ) ;
}
2020-05-28 21:35:39 -04:00
2023-03-31 11:47:53 -04:00
private void ResetTickPositions ( ) = > TickPositions = new int [ 0 ] ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private IntPtr SendMsg ( TrackBarMessage msg , ref RECT rect ) = > SendMessage ( Handle , ( uint ) msg , IntPtr . Zero , ref rect ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private bool ShouldSerializeThumbLength ( ) = > thumbLength > = 0 ;
private bool ShouldSerializeTickPositions ( ) = > ticks ! = null & & ticks . Length > 0 ;
2017-11-27 13:11:20 -05:00
}