自做ActiveX表格控件,数十万表格的绘制如何实现

时间:2022-12-27 21:05:30
自做了个ActiveX表格控件,在IE下使用,需要显示的数据可能有数十万行以
上。根据我做的测试,ActiveX控件能绘制的坐标范围在[-32767, 32768]之间,超
过此坐标范围的就无法显示。也可以说最大能使用的坐标范围是0到65536之间,
如果每行按占用10个点计,也只能绘制出6千多行,离几十万行的目标还差的太
远。我知道分页的方式可以实现,但客户不同意。
    我还想到使用坐标映射的方式,我发现填充了数十万行数据的表格,超过几千
行之后,后面的内容虽然不能显示出来,但滚动条却可以继续往下滚动。我想可以
把数十万行表格的坐标范围映射到滚动条的范围,然后在滚动时,将要显示的表格
范围通过坐标映射,绘制到显示区,如此只需要一屏的坐标范围即可,不知此想法
是否可行,如果可行,如何在ActiveX中自己设置IE的滚动条范围,并能响应滚动
时的消息,以便绘制。或者能有其它的解决方法也可以,需要确实可行的,能实现
的。
    这个问题也可以总结为,如何突破系统GDI坐标范围的限制。

21 个解决方案

#1


发错位置了,不知版主能否转到VC/MFC里的ActiveX专区

#2


不好意思,刚开始用,不够熟悉,已经自己转了

#3


有没有高手给指点一下。

#4


该回复被版主删除

#5


上一个给你回复了,只绘制看得到的

#6


to teli_eurydice(哭泣的仙人掌。。。。) 
你的意思我明白,但我问的主要意思是坐标值超过65536的图形,绘制了也无法看到,显示不了。
怎么才能让坐标超过65536的图形也能看到

#7


当前窗口有多少行记录,其它的都放内存或动态生成。
每次滑动更新列表数据就可了,无须考虑(坐标范围在[-32767, 32768])

#8


>>当前窗口有多少行记录,其它的都放内存或动态生成。
>>每次滑动更新列表数据就可了,无须考虑(坐标范围在[-32767, 32768])

滑动还是需要坐标的,没有坐标怎么知道滑动到记录的什么地方?

#9


晕倒。。。
怎么这么执着于滑块位置和记录位置的对应关系呢???

比如说由1亿条记录,[-32767, 32768]和1亿条记录一一对应肯定不够,即使一个位置与一个屏幕(比如说是100条记录)也肯定不够。
那为什么不能是一个位置与1000条记录(10屏待显示数据)对应?
用户操作确定滑块位置以后定位显示与之对应的1000条记录中的前100条。如果用户点击翻页,则选择下100条记录显示,但是控制滑块位置不变即可

#10


to WingForce(初六,履霜,坚冰至。) 
我已经说过不能使用分页的方法,用分页是肯定可以解决的。

#11


要用滚动条*滚动的方式来浏览记录。好比我用记事本打开个很大的文件,然后用滚动条可以*滚动来浏览文本。

#12


要用滚动条*滚动的方式来浏览记录。好比我用记事本打开个很大的文件,然后用滚动条可以*滚动来浏览文本。
======================================================================================
"分页"为什么就不是*滚动了???

#13



要能一直滚动到头的那种连续的Flat方式

#14


要能一直滚动到头的那种连续的Flat方式
======================================================
还是不明白“分页”为什么就不能一直滚动到头。。。

#15


to WingForce(初六,履霜,坚冰至。) 
用户不用点击翻页按钮,只需用滚动条就可以浏览所有记录。
或许我还没理解你的方法,不知能否用简单的代码说明一下


我这有个简单的测试可以看到我说的限制。在ActiveX里画几道很长的竖线。
for(int i = 0; i < 4; ++i)
{
    MoveToEx(hdc, 10 * (i + 1), 0, NULL);
    LineTo(hdc, 10 * (i + 1), (32678 / 2) * (i+1));
}
显示时可以看到,第一条线是可以看到末端的,其它的就看不到了

#16


你不用分页的办法?有什么意义呢?谁会在屏幕上看10w条记录?

#17


我是知道分页的方法的,但客户要求最好不用分页的。我就想试试看能否突破限制

#18


我写了一个例子,以做参考
#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "WindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default color as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Windows App",       /* Title Text */
           WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_MAXIMIZEBOX | WS_VSCROLL, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nFunsterStil);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */
#define TOTAL_LINE_CNT 10000
#define MAX_SCROLL_VAL 100

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
    char szTextBuf[128];

    TEXTMETRIC tm;
    
    static int iCharHigh;
    static int iLineCntInView;

    static int iCurStartLine;

    int iOldPos;    
    int iOldStartLine;
    RECT rt;
    RECT textrt; 
    int i;                  // loop counter 
    SCROLLINFO si; 
    
    switch (message)                  /* handle the messages */
    {
case WM_CREATE:

            hdc = GetDC (hWnd);  
            GetTextMetrics (hdc, &tm); 
            iCharHigh = tm.tmHeight + tm.tmExternalLeading;            
            ReleaseDC (hWnd, hdc);

            GetClientRect(hWnd, &rt);
            iLineCntInView    = ( rt.bottom -rt.top ) / iCharHigh;

            iCurStartLine = 0;

            si.cbSize = sizeof(si); 
            si.fMask  = SIF_POS | SIF_RANGE; 
            si.nPos   = 0;
            si.nMin   = 0;
            si.nMax   = MAX_SCROLL_VAL;
            SetScrollInfo(hWnd, SB_VERT, &si, TRUE);

            break;           
            
                case WM_VSCROLL: 
            
            si.cbSize = sizeof (si);
            si.fMask  = SIF_ALL;
            GetScrollInfo( hWnd, SB_VERT, &si );  
            
            iOldStartLine = iCurStartLine;
            iOldPos       = si.nPos;
            switch( LOWORD (wParam) ) 
            { 
                case SB_PAGEUP:

                     if ( iCurStartLine > iLineCntInView )
                         iCurStartLine -= iLineCntInView;
                     else
                         iCurStartLine = 0;
                     
                     break; 

                case SB_PAGEDOWN:

                     if ( iCurStartLine + iLineCntInView < TOTAL_LINE_CNT - iLineCntInView )
                         iCurStartLine += iLineCntInView;
                     else
                         iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;

                     break; 

                case SB_LINEUP: 
                     if ( iCurStartLine > 0 )
                         iCurStartLine --;

                     break; 

                case SB_LINEDOWN: 
                     if( iCurStartLine < TOTAL_LINE_CNT - iLineCntInView )
                         iCurStartLine ++;
                     else
                         iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;

                     break; 

                case SB_THUMBTRACK:                      
                     iCurStartLine = si.nTrackPos * ( TOTAL_LINE_CNT / MAX_SCROLL_VAL );
                     if( iCurStartLine > TOTAL_LINE_CNT )
                        iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;
                     break; 

            }
            
            if ( iCurStartLine != iOldStartLine )
            {
                si.nPos  = iCurStartLine / ( TOTAL_LINE_CNT / MAX_SCROLL_VAL );

                if ( iOldPos != si.nPos )
                {
                    si.fMask = SIF_POS;                    
                    SetScrollInfo(hWnd, SB_VERT, &si, TRUE); 
                }
                              
                ScrollWindow(hWnd, 0, iCharHigh * (iOldStartLine - iCurStartLine), NULL, NULL);
                UpdateWindow(hWnd);

            }  


            break;

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rt);
            textrt = rt;
            for( textrt = rt, i = iCurStartLine; textrt.top < rt.bottom; textrt.top += iCharHigh, i ++ )
            {
                sprintf( szTextBuf, "%d", i );
                DrawText(hdc, szTextBuf, strlen(szTextBuf), &textrt, DT_CENTER);
            }
    
EndPaint(hWnd, &ps);
break;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
            
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hWnd, message, wParam, lParam);
    }

    return 0;
}

#19


我人为设置了scollbar的范围:
#define MAX_SCROLL_VAL 100

要显示的数据是打印
#define TOTAL_LINE_CNT 10000
这么多行数据

#20


to WingForce(初六,履霜,坚冰至。) 
你的例子我试过了,跟我想的映射到滚动条的范围差不多。不过我比较执着于平滑的滚动,就是用鼠标拖着滚动时能做到像素级的移动。你的例子如果记录比scollbar的范围大得多的话,用鼠标滚动时就不是很方便,鼠标动一个像素,可能就会跳过很多行。不过这也是一个解决的方法。毕竟记录比较大。谢谢指点

好了结贴散分了。

#21


................

#1


发错位置了,不知版主能否转到VC/MFC里的ActiveX专区

#2


不好意思,刚开始用,不够熟悉,已经自己转了

#3


有没有高手给指点一下。

#4


该回复被版主删除

#5


上一个给你回复了,只绘制看得到的

#6


to teli_eurydice(哭泣的仙人掌。。。。) 
你的意思我明白,但我问的主要意思是坐标值超过65536的图形,绘制了也无法看到,显示不了。
怎么才能让坐标超过65536的图形也能看到

#7


当前窗口有多少行记录,其它的都放内存或动态生成。
每次滑动更新列表数据就可了,无须考虑(坐标范围在[-32767, 32768])

#8


>>当前窗口有多少行记录,其它的都放内存或动态生成。
>>每次滑动更新列表数据就可了,无须考虑(坐标范围在[-32767, 32768])

滑动还是需要坐标的,没有坐标怎么知道滑动到记录的什么地方?

#9


晕倒。。。
怎么这么执着于滑块位置和记录位置的对应关系呢???

比如说由1亿条记录,[-32767, 32768]和1亿条记录一一对应肯定不够,即使一个位置与一个屏幕(比如说是100条记录)也肯定不够。
那为什么不能是一个位置与1000条记录(10屏待显示数据)对应?
用户操作确定滑块位置以后定位显示与之对应的1000条记录中的前100条。如果用户点击翻页,则选择下100条记录显示,但是控制滑块位置不变即可

#10


to WingForce(初六,履霜,坚冰至。) 
我已经说过不能使用分页的方法,用分页是肯定可以解决的。

#11


要用滚动条*滚动的方式来浏览记录。好比我用记事本打开个很大的文件,然后用滚动条可以*滚动来浏览文本。

#12


要用滚动条*滚动的方式来浏览记录。好比我用记事本打开个很大的文件,然后用滚动条可以*滚动来浏览文本。
======================================================================================
"分页"为什么就不是*滚动了???

#13



要能一直滚动到头的那种连续的Flat方式

#14


要能一直滚动到头的那种连续的Flat方式
======================================================
还是不明白“分页”为什么就不能一直滚动到头。。。

#15


to WingForce(初六,履霜,坚冰至。) 
用户不用点击翻页按钮,只需用滚动条就可以浏览所有记录。
或许我还没理解你的方法,不知能否用简单的代码说明一下


我这有个简单的测试可以看到我说的限制。在ActiveX里画几道很长的竖线。
for(int i = 0; i < 4; ++i)
{
    MoveToEx(hdc, 10 * (i + 1), 0, NULL);
    LineTo(hdc, 10 * (i + 1), (32678 / 2) * (i+1));
}
显示时可以看到,第一条线是可以看到末端的,其它的就看不到了

#16


你不用分页的办法?有什么意义呢?谁会在屏幕上看10w条记录?

#17


我是知道分页的方法的,但客户要求最好不用分页的。我就想试试看能否突破限制

#18


我写了一个例子,以做参考
#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "WindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default color as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Windows App",       /* Title Text */
           WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_MAXIMIZEBOX | WS_VSCROLL, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nFunsterStil);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */
#define TOTAL_LINE_CNT 10000
#define MAX_SCROLL_VAL 100

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
    char szTextBuf[128];

    TEXTMETRIC tm;
    
    static int iCharHigh;
    static int iLineCntInView;

    static int iCurStartLine;

    int iOldPos;    
    int iOldStartLine;
    RECT rt;
    RECT textrt; 
    int i;                  // loop counter 
    SCROLLINFO si; 
    
    switch (message)                  /* handle the messages */
    {
case WM_CREATE:

            hdc = GetDC (hWnd);  
            GetTextMetrics (hdc, &tm); 
            iCharHigh = tm.tmHeight + tm.tmExternalLeading;            
            ReleaseDC (hWnd, hdc);

            GetClientRect(hWnd, &rt);
            iLineCntInView    = ( rt.bottom -rt.top ) / iCharHigh;

            iCurStartLine = 0;

            si.cbSize = sizeof(si); 
            si.fMask  = SIF_POS | SIF_RANGE; 
            si.nPos   = 0;
            si.nMin   = 0;
            si.nMax   = MAX_SCROLL_VAL;
            SetScrollInfo(hWnd, SB_VERT, &si, TRUE);

            break;           
            
                case WM_VSCROLL: 
            
            si.cbSize = sizeof (si);
            si.fMask  = SIF_ALL;
            GetScrollInfo( hWnd, SB_VERT, &si );  
            
            iOldStartLine = iCurStartLine;
            iOldPos       = si.nPos;
            switch( LOWORD (wParam) ) 
            { 
                case SB_PAGEUP:

                     if ( iCurStartLine > iLineCntInView )
                         iCurStartLine -= iLineCntInView;
                     else
                         iCurStartLine = 0;
                     
                     break; 

                case SB_PAGEDOWN:

                     if ( iCurStartLine + iLineCntInView < TOTAL_LINE_CNT - iLineCntInView )
                         iCurStartLine += iLineCntInView;
                     else
                         iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;

                     break; 

                case SB_LINEUP: 
                     if ( iCurStartLine > 0 )
                         iCurStartLine --;

                     break; 

                case SB_LINEDOWN: 
                     if( iCurStartLine < TOTAL_LINE_CNT - iLineCntInView )
                         iCurStartLine ++;
                     else
                         iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;

                     break; 

                case SB_THUMBTRACK:                      
                     iCurStartLine = si.nTrackPos * ( TOTAL_LINE_CNT / MAX_SCROLL_VAL );
                     if( iCurStartLine > TOTAL_LINE_CNT )
                        iCurStartLine = TOTAL_LINE_CNT - iLineCntInView;
                     break; 

            }
            
            if ( iCurStartLine != iOldStartLine )
            {
                si.nPos  = iCurStartLine / ( TOTAL_LINE_CNT / MAX_SCROLL_VAL );

                if ( iOldPos != si.nPos )
                {
                    si.fMask = SIF_POS;                    
                    SetScrollInfo(hWnd, SB_VERT, &si, TRUE); 
                }
                              
                ScrollWindow(hWnd, 0, iCharHigh * (iOldStartLine - iCurStartLine), NULL, NULL);
                UpdateWindow(hWnd);

            }  


            break;

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rt);
            textrt = rt;
            for( textrt = rt, i = iCurStartLine; textrt.top < rt.bottom; textrt.top += iCharHigh, i ++ )
            {
                sprintf( szTextBuf, "%d", i );
                DrawText(hdc, szTextBuf, strlen(szTextBuf), &textrt, DT_CENTER);
            }
    
EndPaint(hWnd, &ps);
break;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
            
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hWnd, message, wParam, lParam);
    }

    return 0;
}

#19


我人为设置了scollbar的范围:
#define MAX_SCROLL_VAL 100

要显示的数据是打印
#define TOTAL_LINE_CNT 10000
这么多行数据

#20


to WingForce(初六,履霜,坚冰至。) 
你的例子我试过了,跟我想的映射到滚动条的范围差不多。不过我比较执着于平滑的滚动,就是用鼠标拖着滚动时能做到像素级的移动。你的例子如果记录比scollbar的范围大得多的话,用鼠标滚动时就不是很方便,鼠标动一个像素,可能就会跳过很多行。不过这也是一个解决的方法。毕竟记录比较大。谢谢指点

好了结贴散分了。

#21


................