過去ログ等漁ってみたのですが、解決法などが分からない為質問させていただきます。
スクロールバーについて質問です。
XP 2000では、正常に動作したのですが、98では以下のような現象が発生しています。
スクロールバーで古い行を表示しようとすると、ある一定のところまで正常に表示
されるのですが、それ以降古い行になると二重、三重に表示されてしまいます。
(行自体は、4000行以上あると発生します。)
(一定行までいくと、古い行の上に最新行がかぶるって感じです。表現が難しい・・・)
RESULT CxxxxView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
・
・
・
case WM_VSCROLL:
switch (LOWORD(wParam))
{
スクロールバーのアクションにより
移動量の設定
}
dy = max(-1 * si.nPos, min(dy, si.nMax - si.nPos));
if (dy != 0)
{
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nPos += dy;
pos = si.nPos;
SetScrollInfo(SB_VERT, &si,TRUE);
ScrollWindowEx(0, -dy * m_cyChar, NULL, NULL, NULL, NULL, 0);
InvalidateRect(NULL, TRUE);
UpdateWindow();
}
break;
・
・
・
分かりにくい文章と思いますが、アドバイス宜しくお願いします。
開発環境は、
Visual C++ 6.0 SP5
XP
です。
> XP 2000では、正常に動作したのですが、98では以下のような現象が発生しています。
> スクロールバーで古い行を表示しようとすると、ある一定のところまで正常に表示
> されるのですが、それ以降古い行になると二重、三重に表示されてしまいます。
> (行自体は、4000行以上あると発生します。)
> (一定行までいくと、古い行の上に最新行がかぶるって感じです。表現が難しい・・・)
4095行以上辺りでは?(フォントの縦サイズが16ピクセルの場合)
Win9X系ではGDIのサイズが16bit分までなので、その為に発生している問題でしょう。
MFCのVIEWのサイズもGDIの制限を受けるでしょうから、
現状ではどうにもならないかと…
# googleで検索してみたけど…明確に書かれているところが見つからない……
# ちなみに、仕事で使用しているツールが以前同様の問題を出したコトがあります。
# 内部に16bitコードが残っている関係で仕方ないモノだと思われますが。
このコードだとScrollWindowExでスクロールしている部分も
OnDrawのコードで上書きされてしまいませんか?
いや、OnDraw側がどうなっているかわからないですけれど。
OnEraseBkgndにも処理入れてませんか?
単純に無効にしていたり。
InvalidateRect(NULL, TRUE);だと
クライアント領域を全部無効化しているので
OnDrawでの描画対象にクライアント領域全体が指定されていると思います。
本来は、ScrollWindowExでスクロールして、新たに書き足す部分のみを
無効化して描画するのだと思うのですけれど。
瀬戸っぷ様、PATIO様アドバイス有難う御座います。
PATIO様のおっしゃるとおり、書き込みをする処理をWM_PAINTを
受け取ると行っています。
詳しく書きますとこのアプリは、ログを常に表示し続けるアプリを作ろうと
しています。
LRESULT CYxxxxView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
case WM_PAINT:
pDC = GetDC();
// フォント
oldFont = pDC->SelectObject(m_pFont);
// バックグラウンド
oldBkMode = pDC->SetBkMode(TRANSPARENT);
hdc = ::BeginPaint(this->m_hWnd, &ps);
if (m_YpsHostMessageArray.GetSize() > 0)
{
for (i = 0 ;i <= m_MArray.GetSize(); i++)
{
if (i + si.nPos < m_MArray.GetSize())
{
pDC->TextOut(0, i * m_cyChar, m_MArray.GetAt(i + si.nPos)->m_String);
}
}
}
GetClientRect(&rc);
dispno = rc.bottom / m_cyChar;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = m_MArray.GetSize();
si.nPage = dispno;
si.nPos = pos;
SetScrollInfo(SB_VERT, &si);
// 元に戻す
pDC->SetBkMode(oldBkMode);
pDC->SelectObject(oldFont);
ReleaseDC(pDC);
EndPaint(&ps);
break;
>OnEraseBkgndにも処理入れてませんか?
>単純に無効にしていたり。
OnEraseBkgndのヘルプを見たのですが、よく分からないのですが、これを入れる事に
よりどのような効果が期待できるのでしょうか?
>本来は、ScrollWindowExでスクロールして、新たに書き足す部分のみを
>無効化して描画するのだと思うのですけれど。
成るほど・・・。WM_PAINTのところも変えないといけないみたいですね。
そこらへんを修正してみてだめなようでしたら、瀬戸っぷ様の仰られるように
Win9x系は、諦めるしかないかもしれませんね。
すいません勘違いしてました。
>OnEraseBkgndにも処理入れてませんか?
>単純に無効にしていたり。
使っておりません。
というか、全部描画するならScrollWindowExでスクロールすること自体
必要ないと思います。
ScrollWindowExを使ってスクロールさせるのは結局、表示されている情報を
利用して描画する量を減らすために使います。
必ず、表示対象分を描画するのであれば必要ないでしょう。
WM_ERASEBKGNDの処理を行わないのであれば、普通に背景を消していると思います。
m_YpsHostMessageArrayとm_MArrayの関係が良く分かりませんが、
m_MArrayが全てのログを溜め込んでいる配列なら、
for (i = 0 ;i <= m_MArray.GetSize(); i++)
{
if (i + si.nPos < m_MArray.GetSize())
{
pDC->TextOut(0, i * m_cyChar, m_MArray.GetAt(i + si.nPos)->m_String);
}
}
このループは何かおかしいような気がします。
このループの終了判定は、画面内に表示できる行数分あれば十分です。
毎回、表示できる分のログを描画しても十分なスピードが出ているのであれば、
ScrollWindowExを使って処理を複雑にするより、毎回、一画面分描画する作りの
方がすっきりして良いかもしれません。
ところでWM_VSCROLLのWPARAMで通知されるnPosは、16ビットの縛りがあったと
思いますが、省略されているコードの中にGetScrollInfoしている部分があるのでしょ
うか?
ちょっと気になったので。
アドバイス有難う御座います。
m_YpsHostMessageArrayとm_MArrayは、同じです。名前が長いので書き込みの際に短く
したのですが、漏れがあっただけです。
このm_MArrayは、表示文字列を格納している配列です。
GetScrollInfoについては、WindowProcに入った直後に行っています。
PATIO様が仰っているループで、僕としては表示する1ページを再度書き込んで
いるいつもりです。
ループの終了条件は、これでうまくいくと思っているのですけど問題あるので
しょうか?
ScrollWindowExを抜き、毎回1ページ書き直すように修正しましたが、やはりだめ
みたいです。(縦18ピクセルで3742行以上さかのぼると発生)
瀬戸っぷ様が仰ったように仕様なのかなぁとか思っていますが、出来るだけ
全てのログを表示するようにしたいので、もう少しがんばってみようと思います。
何かアドバイス等ありましたら、宜しくお願いします。
瀬戸っぷ様、PATIO様いろいろアドバイス有難う御座います。
結局9x系のみ、表示行を制限することにしまいした。
ありがとうございました。