As is well known in AX 3.0 it's unable to scroll objects (first of all Groups etc.) in the form. This feature is present since AX4 and supported, as far as I know by axScroll.dll.
Honestly, I'm not a big worshipper of using side components. On the other hand I've been thinking about how to implement scrolling feature in AX3.
About one year ago there was published very interesting project Capturando el WindowProc desde Axapta by Manekaze
In this project the author showed a possibility to trap keyboard events using API in AX. So why don't capture the scrollbar events.
What we have to do: 1. Apply scrollable style for the window which we are going to scroll. 2. Trap scroll events in AX. 3. Scroll the window according scrollbar positions.
Let's go.
- Apply scrollable style for the window which we are going to scroll
Setting scrollable style for the window is pretty easy:
WinApi::showScrollbar(handle, #SB_BOTH, true);
By the way we need to set some parameters for vertical and horizontal scrollbars
// pos pSize min max WinApi::setScrollInfo(handle, 0, 20, 0, 100, true, #SB_HORZ); WinApi::setScrollInfo(handle, 0, 20, 0, 100, true, #SB_VERT);and get back these parameters
cScrollInfoH = WinApi::getScrollInfo(handle, #SB_HORZ); xOffset = conpeek(cScrollInfoH, #pos); xScroll = conpeek(cScrollInfoH, #maxValue); xSize = conpeek(cScrollInfoH, #pageSize); cScrollInfoV = WinApi::getScrollInfo(handle, #SB_VERT); yOffset = conpeek(cScrollInfoV, #pos); yScroll = conpeek(cScrollInfoV, #maxValue); ySize = conpeek(cScrollInfoV, #pageSize);In my case page size of each scrollbar is 20, minimum scrolling position is 0 and maximum scrolling position is 100.
- Scroll the window according scrollbar positions
Here I don't like to reinvent something new and I used standard - Trapping scroll events
I've designed it as a new class xApplyScrolling
// Begin of xApplyScrolling class -->
By the way there was modificated a bit TrucosAx_TrapperWndProc class. In method processMessages() in our case we need to work with class object, not with FormRun object. Here are modifications: int Scroll_SetVert(ScrollInfo_t* pScroll, HWND hWnd, WORD wScrollCmd, WORD wScrollPos)
{
int nScrollAmt ;
switch (wScrollCmd)
{
case SB_TOP:
nScrollAmt = -pScroll->yOffset ;
break ;
case SB_BOTTOM:
nScrollAmt = pScroll->yScroll - pScroll->yOffset ;
break ;
case SB_PAGEUP:
nScrollAmt = -pScroll->ySize ;
break ;
case SB_PAGEDOWN:
nScrollAmt = pScroll->ySize ;
break ;
case SB_LINEUP:
nScrollAmt = -pScroll->yChar ;
break ;
case SB_LINEDOWN:
nScrollAmt = pScroll->yChar ;
break ;
case SB_THUMBPOSITION:
nScrollAmt = wScrollPos - pScroll->yOffset ;
break ;
default:
return ( FALSE ) ;
}
if ((pScroll->yOffset + nScrollAmt) > pScroll->yScroll)
nScrollAmt = pScroll->yScroll - pScroll->yOffset ;
if ((pScroll->yOffset + nScrollAmt) < 0)
nScrollAmt = -pScroll->yOffset ;
ScrollWindow( hWnd, 0, -nScrollAmt, 0, 0 ) ;
pScroll->yOffset = pScroll->yOffset + nScrollAmt ;
SetScrollPos( hWnd, SB_VERT, pScroll->yOffset, TRUE ) ;
return ( TRUE ) ;
} // end of Scroll_SetVert()
int Scroll_SetHorz(ScrollInfo_t* pScroll, HWND hWnd, WORD wScrollCmd, WORD wScrollPos)
{
int nScrollAmt ;
switch (wScrollCmd)
{
case SB_TOP:
nScrollAmt = -pScroll->xOffset ;
break ;
case SB_BOTTOM:
nScrollAmt = pScroll->xScroll - pScroll->xOffset ;
break ;
case SB_PAGEUP:
nScrollAmt = -pScroll->xSize ;
break ;
case SB_PAGEDOWN:
nScrollAmt = pScroll->xSize ;
break ;
case SB_LINEUP:
nScrollAmt = -pScroll->xChar ;
break ;
case SB_LINEDOWN:
nScrollAmt = pScroll->xChar ;
break ;
case SB_THUMBPOSITION:
nScrollAmt = wScrollPos - pScroll->xOffset ;
break ;
default:
return ( FALSE ) ;
}
if ((pScroll->xOffset + nScrollAmt) > pScroll->xScroll)
nScrollAmt = pScroll->xScroll - pScroll->xOffset ;
if ((pScroll->xOffset + nScrollAmt) < 0)
nScrollAmt = -pScroll->xOffset ;
ScrollWindow( hWnd, -nScrollAmt, 0, 0, 0 ) ;
pScroll->xOffset = pScroll->xOffset + nScrollAmt ;
SetScrollPos( hWnd, SB_HORZ, pScroll->xOffset, TRUE ) ;
return ( TRUE ) ;
} // end of Scroll_SetHorz()
In my WinApi class I haven't found scrollWindow() method. And I've added it into WinApi class
// Created by GRR
client static int scrollWindow(int hwnd,
int XAmount,
int YAmount)
{
DLL _winApiDLL = new DLL('USER32');
DLLFunction _scrollWindow = new DLLFunction(_winApiDLL, 'ScrollWindow');
_scrollWindow.returns(ExtTypes::DWord);
_scrollWindow.arg(ExtTypes::DWord,
ExtTypes::DWord,
ExtTypes::DWord,
ExtTypes::DWord,
ExtTypes::DWord);
return _scrollWindow.call(hwnd, XAmount, YAmount, 0,0);
}
Now we are ready for third step
#define.minValue(1)
#define.maxValue(2)
#define.pageSize(3)
#define.pos(4)
#define.xChar(5)
#define.yChar(5)
class xApplyScrolling
{
#WinApi
TrucosAx_TrapperWndProc WndProcHScroll, WndProcVScroll;
HWND handle;
container cScrollInfoH, cScrollInfoV;
int xOffset, yOffset;
int xScroll, yScroll;
int xSize, ySize;
}
//In new() method we are installing hooks for WM_HSCROLL and WM_VSCROLL separately
void new(FormRun _element, FormControl _formControl)//HWND _HWnd
{
handle = _formControl.hWnd();
WndProcHScroll = new TrucosAx_TrapperwndProc(_element, handle, true);
WndProcHScroll.InstallHook(#WM_HSCROLL, 'scrlMessageH');
WndProcHScroll.setObject(this);
WndProcVScroll = new TrucosAx_TrapperwndProc(_element, handle, true);
WndProcVScroll.InstallHook(#WM_VSCROLL, 'scrlMessageV');
WndProcVScroll.setObject(this);
this.setScrollable();
}
//Here we set scrollable style for the window
protected void setScrollable()
{
;
WinApi::showScrollbar(handle, #SB_BOTH, true);
// pos pSize min max
WinApi::setScrollInfo(handle, 0, 20, 0, 100, true, #SB_HORZ);
WinApi::setScrollInfo(handle, 0, 20, 0, 100, true, #SB_VERT);
cScrollInfoH = WinApi::getScrollInfo(handle, #SB_HORZ);
xOffset = conpeek(cScrollInfoH, #pos);
xScroll = conpeek(cScrollInfoH, #maxValue);
xSize = conpeek(cScrollInfoH, #pageSize);
cScrollInfoV = WinApi::getScrollInfo(handle, #SB_VERT);
yOffset = conpeek(cScrollInfoV, #pos);
yScroll = conpeek(cScrollInfoV, #maxValue);
ySize = conpeek(cScrollInfoV, #pageSize);
}
//Here we scroll the window
protected str translateHCode(int _nScrollCode)
{
int nScrollAmt ;
str strCode;
;
switch (_nScrollCode)
{
case #SB_BOTTOM: // Scrolls to the lower right.
//nScrollAmt = pScroll->xScroll - pScroll->xOffset;
nScrollAmt = xScroll - xOffset;
strCode = 'SB_BOTTOM';
break;
case #SB_ENDSCROLL: // Ends scroll.
strCode = 'SB_ENDSCROLL';
break;
case #SB_LINELEFT: // Scrolls left by one unit.
nScrollAmt = -#xChar;
strCode = 'SB_LINELEFT';
break;
case #SB_LINERIGHT: // Scrolls right by one unit.
nScrollAmt = #xChar;
strCode = 'SB_LINERIGHT';
break;
case #SB_PAGELEFT: // Scrolls left by the width of the window.
//nScrollAmt = -pScroll->xSize;
nScrollAmt = -xSize;
strCode = 'SB_PAGELEFT';
break;
case #SB_PAGERIGHT: // Scrolls right by the width of the window.
//nScrollAmt = pScroll->xSize;
nScrollAmt = xSize;
strCode = 'SB_PAGERIGHT';
break;
case #SB_THUMBPOSITION: // Scrolls to the absolute position. The current position is specified by the nPos parameter.
//nScrollAmt = wScrollPos - pScroll->xOffset;
nScrollAmt = -xSize;
strCode = 'SB_THUMBPOSITION';
break;
case #SB_THUMBTRACK: // Drags scroll box to the specified position. The current position is specified by the nPos parameter.
strCode = 'SB_THUMBTRACK';
break;
case #SB_TOP: // Scrolls to the upper left.
//nScrollAmt = -pScroll->xOffset;
nScrollAmt = -xOffset;
strCode = 'SB_TOP';
break;
default:
//return int2HEX(_nScrollCode);
nScrollAmt = _nScrollCode/0x10000 - xOffset;
strCode = int2HEX(_nScrollCode);
}
if ((xOffset + nScrollAmt) > xScroll)
nScrollAmt = xScroll - xOffset ;
if ((xOffset + nScrollAmt) < 0)
nScrollAmt = -xOffset ;
// ScrollWindow( hWnd, -nScrollAmt, 0,) ;
WinApi::scrollWindow(handle, -nScrollAmt, 0);
xOffset+=nScrollAmt ;
// SetScrollPos( hWnd, SB_HORZ, pScroll->xOffset, TRUE ) ;
WinApi::setScrollPos(handle, #SB_HORZ, xOffset, true);
WinApi::invalidateRect(handle);
return strCode;
}
protected str translateVCode(int _nScrollCode)
{
int nScrollAmt ;
str strCode;
;
switch (_nScrollCode)
{
case #SB_BOTTOM: // Scrolls to the lower right.
nScrollAmt = yScroll - yOffset;//nScrollAmt = pScroll->yScroll - pScroll->yOffset ;
strCode = 'SB_BOTTOM';
break;
case #SB_ENDSCROLL: // Ends scroll.
return 'SB_ENDSCROLL';
break;
case #SB_LINEDOWN: // Scrolls one line down.
nScrollAmt = #yChar;//nScrollAmt = pScroll->yChar;
strCode = 'SB_LINEDOWN';
break;
case #SB_LINEUP: // Scrolls one line up.
nScrollAmt = -#yChar;//nScrollAmt = -pScroll->yChar;
strCode = 'SB_LINEUP';
break;
case #SB_PAGEDOWN: // Scrolls one page down.
nScrollAmt = ySize;//nScrollAmt = pScroll->ySize;
strCode = 'SB_PAGEDOWN';
break;
case #SB_PAGEUP: // Scrolls one page up.
nScrollAmt = -ySize;//nScrollAmt = -pScroll->ySize;
strCode = 'SB_PAGEUP';
break;
case #SB_THUMBPOSITION: // Scrolls to the absolute position. The current position is specified by the nPos parameter.
//nScrollAmt = wScrollPos - pScroll->yOffset;
nScrollAmt = -ySize;
strCode = 'SB_THUMBPOSITION';
break;
case #SB_THUMBTRACK: // Drags scroll box to the specified position. The current position is specified by the nPos parameter.
return 'SB_THUMBTRACK';
break;
case #SB_TOP: // Scrolls to the upper left.
nScrollAmt = -yOffset;// nScrollAmt = -pScroll->yOffset;
strCode = 'SB_TOP';
break;
default:
nScrollAmt = _nScrollCode/0x10000 - yOffset;
strCode = int2HEX(_nScrollCode);
}
if ((yOffset + nScrollAmt) > yScroll)
nScrollAmt = yScroll - yOffset;
if ((yOffset + nScrollAmt) < 0)
nScrollAmt = -yOffset;
WinApi::scrollWindow(handle, 0, -nScrollAmt);
// pScroll->yOffset = pScroll->yOffset + nScrollAmt ;
// SetScrollPos( hWnd, SB_VERT, pScroll->yOffset, TRUE ) ;
yOffset+=nScrollAmt;
WinApi::setScrollPos(handle, #SB_VERT, yOffset, true);
WinApi::invalidateRect(handle);
return strCode;
}
// Here we process scrolling messages
protected void scrlMessageH()
{
map MMsgH;
;
MMsgH = WndProcHScroll.GetMsg();
this.translateHCode(MMsgH.lookup('WPARAM'));
}
protected void scrlMessageV()
{
map MMsgV;
;
MMsgV = WndProcVScroll.GetMsg();
this.translateVCode(MMsgV.lookup('WPARAM'));
}
// Here we finalize our class
void finalize()
{
WndProcHScroll.RemoveMainProc();
WndProcVScroll.RemoveMainProc();
}
// <-- End of xApplyScrolling class class TrucosAx_TrapperWndProc extends TrucosAx_TrapperBase
{
#define.CodeSize(159) // Total bytes de codigo
#define.InternalDataSize(16)
#define.MaxDataelements(100) // Si se cambia se debe cambiar el codigo asm
#Define.DataSize(16) // +16 bytes de buffer
#Define.offsetcounter(#CodeSize + 4)
#Define.offsetlastmsg(#CodeSize + 8)
#Define.offsetCallwndproc(#CodeSize + 12)
map hooks;
#define.maxHooks(20) //si se cambia se debe cambiar el codigo asm
Binary HooksBuffer;
Object object;//Created by GRR
}
//Created by GRR
void setObject(Object _object)
{
;
object = _object;
}
protected void processMessages()
{ map Mmsg;
int msgId;
void Ejecuta( str methodname )
{
str func = @"void FireHook(object obj)
{
;
obj.%1();
}";
func = strfmt(func,methodname);
//RunBuf(func, xelement); //Commented by GRR
// Created by GRR -->
if (object)
RunBuf(func, object);
else
RunBuf(func, xelement);
// Created by GRR <--
}
;
do
{
MMsg = this.GetMsg();
if (! MMsg) continue;
MsgId = MMsg.lookup('MSG');
if (hooks.exists(MsgId))
{
Ejecuta(hooks.lookup(MsgId));
// Si se queda el mensaje debe devolver algo en 'RESULT' en el MMSG
if ( ! MMsg.exists('RESULT'))
this.CallbackOriginalProc(MMsg);
}
} while (MMsg && this.nextmessage());
}
That's all.
To work with scrollable objects we need drop a line in run() method:
public void run()
{
super();
applyScrolling = new xApplyScrolling(element, element.control(control::HRMPersonal));
}
// don't forget to finalize ApplyScrolling class
public void close()
{
applyScrolling.finalize();
super();
}
// and declare applyScrolling variable in ClassDeclaration
xApplyScrolling applyScrolling;
Following picture demonstrates scrolling TabPage:HRMPersonal EmplTable form
P.S. This project is raw enough. For example it doesn't support form resizing.
Another words... to be continued.
Copyright © 2008 Ruslan Goncharov
13 comments:
Hello Ruslan.
I just added your blog to our list.
Please visit us, although you probably would not understand a word (It's a Brazilian Blog!)
www.axaptabrasil.com.br
Great Blog.
André Santos
Developer
One question: What are you using for posting code like this?
It looks great!
Dyakuyu
In two words:
1. I use GeSHi - Generic Syntax Highlighter
http://qbnz.com/highlighter/
2. In Formatting tab of my template
'convert line breaks' is set to yes
This month I'm planning to explain how to publish nice snippets in blogger
Thanks a lot for inviting.
You were right.
Unfortunately Portuguese for me is 'terra incognita'
Hope there will be the articles in english too.
By the way I'd like to recommend http://axforum.info/
http://axforum.info/forums/forumdisplay.php?f=92
Axforum in English. Here you may ask the question and get the answer as soon as possible
http://axforum.info/forums/forumdisplay.php?f=86
Microsoft Dynamics AX Blogs
Muchas gracias
Oppps....
'convert line breaks' is set to NO
Hello :)
I'm Manel Querol from TrucosAx.com (I usually sign as Manekaze or Mkz at forums )
When I did the wndprocTRapper class I thought anybody was going to use it :P
Very nice job !
Your blog has many interesting articles ( I will add a link in my site )
Kind Regards,
Mkz.
Hi
I was impressed by that project very much. Respect!
Thanks a lot for the Capturando and for http://www.trucosax.com/ too
Hope for co-operation in future.
Ruslan Goncharov
Amazing, I'm trying to add auto scrolling to a formTreeControl, and still struggling a little on where to put Scroll_SetVert functions...
Do you have an XPO for this?
Thanks
forget it, I found my own method...
Thanks
FYI :
http://www.axaptapedia.com/FormTreeControl_AutoScrolling_when_draging_over_top_and_bottom
Wow! I'll waiting for the article
Hello, i'm trying to put a vertical scroll bar in a group which have the hide property activate. But it doesn't work. I can see my scroll bar with 'showScrollBar' but when I do my 'setScrollInfo', nothing happen. My scroll bar is always the same, even if I change properties... And if I click on its buttons or drag my bar nothing move. Can you help me ?
Sorry for my bad english.
Hi Ruslan,
Your solution is great! But I have one question on your WinAPI.SetScrollInfo method. I am running Axapta 3.0 SP3 and the WinAPI.SetScrollInfo only accepts 6 parameters while your code is passing 7 parameters to it.
Can you indicate which version of Axapta this was written for? And maybe post the X++ code within the WinAPI.SetScrollInfo method?
Thanks!
CK
// The seventh parameter
#WinApi
#define.structSize(28)
client static int setScrollInfo(int hwnd,
int pos,
int pageSize,
int minValue,
int maxValue,
boolean redraw,
int fnBar = #SB_CTL // scroll bar flag // Created by GRR
)
{
DLL _winApiDLL = new DLL('USER32');
DLLFunction _setScrollInfo = new DLLFunction(_winApiDLL, 'SetScrollInfo');
Binary _scrollInfo = new Binary(#structSize);
_scrollInfo.dWord(#offset0, #structSize);
_scrollInfo.dWord(#offset4, #SIF_ALL);
_scrollInfo.dWord(#offset8, minValue);
_scrollInfo.dWord(#offset12, maxValue);
_scrollInfo.dWord(#offset16, pageSize);
_scrollInfo.dWord(#offset20, pos);
_setScrollInfo.returns(ExtTypes::DWord);
_setScrollInfo.arg(ExtTypes::DWord,
ExtTypes::DWord,
ExtTypes::Pointer,//:DWord,
ExtTypes::DWord);
return _setScrollInfo.call(hwnd, fnBar, _scrollInfo, (redraw?1:0)); // Created by GRR
}
Post a Comment