Introduction
Have you ever searched for a nice way to select a subrange from an interval?
Yes? Have you read Ben Shneiderman's "Designing the user Interface" or seen Spotfire? No? Well, that doesn't matter,
because this control is just like the one described and used there.
The user can restrict a range in the interval [Min, Max] by sliding the right
and left arrow or choose an interval by clicking and dragging the interval
itself.
In addition the control can display yet another range, inside the interval
painted in yellow, which for example shows the range of values you really have
displayed elsewhere.
You can either use the control in a horizontal or vertical direction and
for more flexibility you can exchange the meaning of left and right.
General Usage
0. Add Sources
As Step 0 add the necessary source files to your project:
RangeSlider.h
RangeSlider.cpp
MemDC.h
1. Create Member
Then create a member variable
CRangeSlider c_RangeSlider;
in one of your projects classes.
2. Initialize and Create
Add a "Custom Control" to your dialog or formview template. Use "MFCRangeSlider" as window class (without quotes). You have to add a line to
DoDataExchange
of your class:
DDX_Control(pDX, IDC_YOURID, c_RangeSlider);
In OnInitialUpdate
or
OnInitDialog
add code to initialize the control:
c_RangeSlider.SetMinMax(m_Min,m_Max);
c_RangeSlider.SetRange(m_Left,m_Right);
c_RangeSlider.SetVisualMinMax(m_VisualMin, m_VisualMax);
Alternatively you can create and position the control in OnInitDialog
or OnInitialUpdate
:
CRect rc (10,10,300,30);
c_RangeSlider.Create(WS_CHILD |WS_VISIBLE|PBS_SMOOTH| WS_TABSTOP ,
rc, this, IDC_YOURID);
3. React on Changes
If the user then changes the arrow' positions, the
parent window will be sent a registered window message
RANGE_CHANGED
, so add to the parents message map:
ON_REGISTERED_MESSAGE(RANGE_CHANGED, OnRangeChange)
and in the message handler read the new positions out:
LRESULT CRangeCtrlDlg::OnRangeChange(WPARAM , LPARAM ) {
c_RangeSlider.GetRange(m_Left, m_Right);
return static_cast<LRESULT>(0);
}
CRangeSlider API
void Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd,
UINT nID, CCreateContext *pContext = NULL);
Create the Window at the given Position, with the given parent window, etc.
(that's all CWnd::Create
-stuff).
void SetMinMax(double min, double max);
void GetMinMax(double &min, double &max);
double GetMin(void);
double GetMax(void);
Set or read the values for the left and right edge of the control, that is
the interval within which the arrow-positions will be. Note that if you give
them in the wrong order, the control will exchange them (so
min
should be <
max
). The arrow positions (left and right) will
change, if they do not fit into the given interval.
void SetRange(double left, double right);
double GetLeft(void);
double GetRight(void);
void GetRange(double &left, double &right);
Set the position of the arrows. After the call the position will be valid,
i.e. if you give values left > right, the positions are set to a valid
position or if you give values outside of [min, max] the values will be
restricted to the interval.
Read out the positions of the left and right arrow.
void SetVisualMinMax(double VisualMin, double VisualMax);
double GetVisualMinMax(double &VisualMin, double &VisualMax);
double GetVisualMin(void);
double GetVisualMax(void);
Set the values for the "visual" range. If you give an interval not inside
[min, max] the "visual" range will be adjusted. Note that you have to enable
display by a call to SetVisualMode
Read out the values of the "visual" range. Note that you have to enable
display of the "visual" range by a call to SetVisualMode
Modes
void SetVisualMode(BOOL bVisualMinMax = TRUE);
BOOL GetVisualMode(void) { return m_bVisualMinMax; };
Set and read the status of the "VisualMode". Toggles display of the visual range.
void SetVerticalMode(BOOL bVerticalMode = TRUE);
BOOL GetVerticalMode(void);
Set Vertical Mode if the slider should display vertical (like a horizontal
or vertical
ProgressCtrl
).
You have to take care of the windows position and orientation for yourself.
void SetInvertedMode(BOOL bInvertedMode = TRUE);
BOOL GetInvertedMode(void);
If you set InvertedMode, Left and Right of the Control will be exchanged.
So the Left button then controls the value of "right" and vice versa.
The RANGE_CHANGED
Message
The lParam
is not
used. In wParam
you get one of the enum values:
enum _RangeMessages {
RS_LEFTCHANGED,
RS_RIGHTCHANGED,
RS_BOTHCHANGED,
};
Indicating which position has been changed. If you call
SetMinMax
in the message handler, a new
RANGE_CHANGED
messages might be sent to the parent, so beware of an endless loop and a stack
overflow. This message will not be sent, if you call
SetRange
.
Typically you should update your display and set a new "visual" range.
TO DO
- Make all colors configuration options.
- At the moment the whole window is filled by the control. It should
restrict itself to a reasonable depth.
License
You can choose whether you want to use the BSD License (without
advertising clause) or GPL V2 or above (see CRangeSlider.cpp).
History
Date
|
Change
|
2002-04-05 |
Feedback and visual range mode are now enabled as default in demo. |
2002-03-18 |
Drag at point of first LButtonDown. (Thanks to AnthonyJ). Changed algorithm for keyboard interaction (now reaches Min or Max). |
2002-03-11 |
Bug in Keyboard handling removed.
3D Buttons are now depressed. There is a feedback loop demonstrating
the VisualRange. |
2002-03-08 |
Added Vertical Mode. Arrow width is calculated.
You can invert left and right. Arrows are now 3D. |
2002-03-07 |
Support for "custom control" in resource editor.
Keyboard interaction. |
2002-03-06 |
Removed (?) resource allocation problem in OnPaint. |
2002-02-28 |
Initial release to codeproject |
Acknowledgement
This code uses the
CMemDC
class from Keith
Rule for flicker free drawing (it is in CMemDC.h).