Board index » cppbuilder » Accessing the scroll bar within the ListBox

Accessing the scroll bar within the ListBox


2006-09-06 12:16:24 AM
cppbuilder46
Hi, I am having bizarre refresh when using the Listbox in OwnerDraw with
variable (lbOwnerDrawVariable) when scrolling more than 1 line.
I fixed some of the problem when scrolling with the mousewheel by
trapping the OnMouseWheelEvent in the Form and now I am trying to trap
the mousclicks on the scroll bar (not on the thumb).
I subclassed TListBox, and I am trapping the mouse clicks (msg ID 161)
in the virtual "void Dispatch " of my new ListBox class. My problem is
that I cannot find how or where to look to find out the position of the
thum or how is the scroll bar implemented in the ListBox.
I would like the ListBox to scroll down 1 item at the time (multipple
time) when the user clicks on the scroll bar in the "empty area" (the
area between the top or down arrow and the thumb) to fake a page down or
page up.
I have looked in the stdctrl.pas but I can't seem to pinpoint the scroll
bar.
Any help is welcome!
Thank you
Simon
 
 

Re:Accessing the scroll bar within the ListBox

Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

I would like the ListBox to scroll down 1 item at the time
(multipple time) when the user clicks on the scroll bar in
the "empty area"
I subclassed a TListBox and added a message handler for
WM_VSCROLL and succesfully managed scrolling one item at
a time but after the first item is scrolled, the speed of
the scroll reverts to the default page scroll speed.
Then I added a Sleep after the scroll and it appears to be a
solution for you but if you don't like it, your other option
is to disable refresh for the control (WM_SETREDRAW), set the
top index, enable refresh and then refresh the control. No
muss, no fuss and no flicker.
Which ever approach you use, you'll need to subclass the
controls WndProc method so that you can catch WM_VSCROLL
or you'll need to subclass the control so that you can
override the WndProc method *or* use a message map as
demonstrated below:
//-------------------------------------------------------------
class TMyListBox : public TListBox
{
typedef TListBox inherited;
__published:
protected:
private:
MESSAGE void __fastcall WMVScroll( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( WM_VSCROLL, TMessage, WMVScroll )
END_MESSAGE_MAP( inherited )
__fastcall TMyListBox( TComponent* Owner );
};
//-------------------------------------------------------------
//-------------------------------------------------------------
MESSAGE void __fastcall TMyListBox::WMVScroll( TMessage &Message )
{
int ScrollCode = LOWORD( Message.WParam );
if( ScrollCode == SB_PAGEDOWN || ScrollCode == SB_PAGEUP )
{
SCROLLINFO SI = { 0 };
SI.cbSize = sizeof( SCROLLINFO );
SI.fMask = SIF_ALL;
::GetScrollInfo( Handle, SB_VERT, &SI );
int TopItem = ::SendMessage( Handle, LB_GETTOPINDEX, 0, 0 );
if( ScrollCode == SB_PAGEUP )
{
for( int x = 0; x < SI.nPage; ++x )
{
if( TopItem>0 )
{
--TopItem;
::SendMessage( Handle, LB_SETTOPINDEX, TopItem, 0 );
::Sleep( 110 );
}
else break;
}
}
else
{
// page down code goes here
}
}
else inherited::Dispatch(&Message);
}
//-------------------------------------------------------------
~ JD
 

Re:Accessing the scroll bar within the ListBox

JD wrote:
Quote
Simon Guertin < XXXX@XXXXX.COM >wrote:

>I would like the ListBox to scroll down 1 item at the time
>(multipple time) when the user clicks on the scroll bar in
>the "empty area"


I subclassed a TListBox and added a message handler for
WM_VSCROLL and succesfully managed scrolling one item at
a time but after the first item is scrolled, the speed of
the scroll reverts to the default page scroll speed.

Then I added a Sleep after the scroll and it appears to be a
solution for you but if you don't like it, your other option
is to disable refresh for the control (WM_SETREDRAW), set the
top index, enable refresh and then refresh the control. No
muss, no fuss and no flicker.

Which ever approach you use, you'll need to subclass the
controls WndProc method so that you can catch WM_VSCROLL
or you'll need to subclass the control so that you can
override the WndProc method *or* use a message map as
demonstrated below:

//-------------------------------------------------------------
class TMyListBox : public TListBox
{
typedef TListBox inherited;
__published:
protected:
private:
MESSAGE void __fastcall WMVScroll( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( WM_VSCROLL, TMessage, WMVScroll )
END_MESSAGE_MAP( inherited )
__fastcall TMyListBox( TComponent* Owner );
};
//-------------------------------------------------------------



//-------------------------------------------------------------
MESSAGE void __fastcall TMyListBox::WMVScroll( TMessage &Message )
{
int ScrollCode = LOWORD( Message.WParam );
if( ScrollCode == SB_PAGEDOWN || ScrollCode == SB_PAGEUP )
{
SCROLLINFO SI = { 0 };
SI.cbSize = sizeof( SCROLLINFO );
SI.fMask = SIF_ALL;
::GetScrollInfo( Handle, SB_VERT, &SI );
int TopItem = ::SendMessage( Handle, LB_GETTOPINDEX, 0, 0 );
if( ScrollCode == SB_PAGEUP )
{
for( int x = 0; x < SI.nPage; ++x )
{
if( TopItem>0 )
{
--TopItem;
::SendMessage( Handle, LB_SETTOPINDEX, TopItem, 0 );
::Sleep( 110 );
}
else break;
}
}
else
{
// page down code goes here
}
}
else inherited::Dispatch(&Message);
}
//-------------------------------------------------------------

~ JD

Thank you it looks very nice. I achieved the same goal by subclassing
TListBox also and by directly filtering the message in the "virtual
Dispatch(..)" method. I looked for the mouse clicks (id = 161..I don't
know the CONSTANT name for this ID.) and I checked if the click happened
in between the thumb and the arrow to do a TopIndex++ the number of time
of a nPage. Other wise I just Dispatch the message.
Thanks
Simon
 

{smallsort}

Re:Accessing the scroll bar within the ListBox

Here is my code:
void __fastcall TNomadListBox::Dispatch(void *Message)
{
TWMMouse * mouseMessage = (TWMMouse *)Message;
if(mouseMessage->Msg == 161)
{
TPoint p;
p.x = mouseMessage->XPos;
p.y = mouseMessage->YPos;
p = ScreenToClient(p);
int arrowHeight = GetSystemMetrics( SM_CYVSCROLL );
if( p.y <= (Height - arrowHeight) && p.y>= arrowHeight)
{
SCROLLINFO scrollInfo;
bool succeded, succeded2;
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_ALL;
succeded2 = GetScrollInfo(Handle, SB_VERT , &scrollInfo);
SCROLLBARINFO scrollBarInfo;
scrollBarInfo.cbSize = sizeof(SCROLLBARINFO);
succeded = GetScrollBarInfo(Handle, OBJID_VSCROLL,
&scrollBarInfo);
if(succeded && succeded2)
{
int pageSize = scrollInfo.nPage;
if(p.y>= scrollBarInfo.xyThumbTop && p.y <=
scrollBarInfo.xyThumbBottom)
{
TListBox::Dispatch(Message);
}
else if(p.y>scrollBarInfo.xyThumbBottom)
{
for(int i = 0; i < pageSize && TopIndex < Count; i++)
{
TopIndex++;
}
}
else
{
int pageSize = scrollInfo.nPage;
for(int i = 0; i < pageSize && TopIndex>0; i++)
{
TopIndex--;
}
}
}
else
TListBox::Dispatch(Message);
}
else
TListBox::Dispatch(Message);
}
else
{
TListBox::Dispatch(Message);
}
}
JD wrote:
Quote
Simon Guertin < XXXX@XXXXX.COM >wrote:

>I would like the ListBox to scroll down 1 item at the time
>(multipple time) when the user clicks on the scroll bar in
>the "empty area"


I subclassed a TListBox and added a message handler for
WM_VSCROLL and succesfully managed scrolling one item at
a time but after the first item is scrolled, the speed of
the scroll reverts to the default page scroll speed.

Then I added a Sleep after the scroll and it appears to be a
solution for you but if you don't like it, your other option
is to disable refresh for the control (WM_SETREDRAW), set the
top index, enable refresh and then refresh the control. No
muss, no fuss and no flicker.

Which ever approach you use, you'll need to subclass the
controls WndProc method so that you can catch WM_VSCROLL
or you'll need to subclass the control so that you can
override the WndProc method *or* use a message map as
demonstrated below:

//-------------------------------------------------------------
class TMyListBox : public TListBox
{
typedef TListBox inherited;
__published:
protected:
private:
MESSAGE void __fastcall WMVScroll( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( WM_VSCROLL, TMessage, WMVScroll )
END_MESSAGE_MAP( inherited )
__fastcall TMyListBox( TComponent* Owner );
};
//-------------------------------------------------------------



//-------------------------------------------------------------
MESSAGE void __fastcall TMyListBox::WMVScroll( TMessage &Message )
{
int ScrollCode = LOWORD( Message.WParam );
if( ScrollCode == SB_PAGEDOWN || ScrollCode == SB_PAGEUP )
{
SCROLLINFO SI = { 0 };
SI.cbSize = sizeof( SCROLLINFO );
SI.fMask = SIF_ALL;
::GetScrollInfo( Handle, SB_VERT, &SI );
int TopItem = ::SendMessage( Handle, LB_GETTOPINDEX, 0, 0 );
if( ScrollCode == SB_PAGEUP )
{
for( int x = 0; x < SI.nPage; ++x )
{
if( TopItem>0 )
{
--TopItem;
::SendMessage( Handle, LB_SETTOPINDEX, TopItem, 0 );
::Sleep( 110 );
}
else break;
}
}
else
{
// page down code goes here
}
}
else inherited::Dispatch(&Message);
}
//-------------------------------------------------------------

~ JD