SetScrollSizesについて質問です。
CScrollViewで、ウインドウを作成し、そのウインドウにリアルタイムに
ログを表示し続けるアプリを作成しました。
しかし、Win98やwin95など16bit環境では、表示データが32768以上の時に
正常に表示されません。
いろいろ調べ、SetScrollInfoやGetScrollInfoを使えば32bitで扱えると
分かったのですが、実際どのように使ったらよいか分かりません。
表示は、OnDraw内でSetScrollSizeでデータをセットし、一行ずつ
表示するデータを配列関数で所持しているので、TextOutをループで回して
ログ表示を行っています。
void CxxxView::OnDraw(CDC* pDC)
{
CSize size;
size.cx = m_nMessageMaxLength * m_cxCaps;
size.cy = m_MessageArray.GetSize() * m_cyChar;
SetScrollSizes(MM_TEXT, size);
// 描画
int i; // カウンタ
int x = m_cxChar; // x 座標は固定
for(i = 0; i < m_MessageArray.GetSize(); i++)
{
int y = m_cyChar * i;
pDC->TextOut(x, y, m_MessageArray.GetAt(i)->m_String); // 描画
}
}
単純に、SetScrollSizesのところでSetScrollInfoで必要な値を設定してみたの
ですが、二重表示になったりスクロールバーが表示されなくなってしまったりしました。
また、OnVScroll関数内で、受け取ったメッセージによりSetScrollInfoのnPos
を設定し、戻り値にSetScrollInfoのnPosを渡したりとやってみたのですが、
うまくいきませんでした。
何かヒント等ありましたら、宜しく御願いします。
私も仕事柄、ログ用のプログラムを作成しますが、通常はファイルに落としながら
表示しています。
3万件以上も表示しておくのはナンセンスなのではないでしょうか。
私の場合、だいたい表示部は1000件から2000件程度で古いものは消しています。
又は、コンソール出力して、表示桁数をOSにまかせています。(画面のバッファサイズ)
3万件以上もあったら検索しづらいと思うのですが。
size.cy = m_MessageArray.GetSize() * m_cyChar;
でsize.cyを求めている為、実際には、2000行くらいしか表示されていません。
できるのでしたら、全部表示したいのですが、厳しいのですかねぇ・・・。
ログは自前で描画しなければならないのでしょうか?
既存のコントロールを使用してもいいのであれば、
リストボックスや、ReadOnlyのエディットボックスなどを使用すると
楽に実装できると思うのですが。
>ログは自前で描画しなければならないのでしょうか?
リストボックスな既存のコントロールを使用できれば、簡単なのですが
そういうわけにもいかないのが現状です。(修正範囲や現在の仕様を考えて)
SetScrollInfoやGetScrollInfoを使用すれば良いと、過去ログ等に
書いてあったのですが、CScrollViewで作成したウインドウは、無理なんで
しょうかねぇ・・・。
表示行数の制限をかけ、その制限を超えた場合古い行は表示しないように
するというのも考えたのですが、昔表示した文字列が残ったまま、
新しい文字列を表示しようとしてしまい、二重表示になってしまったりと
どれもうまくいっていない現状です;;
まず98/95で正常でないとなっていますが、Win2k/XPでは正常ですか?
異常な動作をするオペレーションはどれですか?
SB_BOTTOM/SB_ENDSCROLL/SB_LINEDOWN/SB_LINEUP/SB_PAGEDOWN
SB_PAGEUP/SB_THUMBPOSITION/SB_THUMBTRACK/SB_TOP
私はWin2kですがSB_THUMBPOSITION/SB_THUMBTRACKのあたりは期待する動作となりません
私の環境でSB_THUMBPOSITION/SB_THUMBTRACKをうまく処理するには下記のコードで
とりあえず問題がなさそうです
ビューのWM_VSCROLLを実装して、下記のように書き換えます
void CxxxView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (nSBCode == SB_THUMBTRACK) {
SCROLLINFO siVert;
memset(&siVert, 0, sizeof(siVert));
siVert.cbSize = sizeof(siVert);
if (GetScrollInfo(SB_VERT, &siVert, SIF_ALL)) {
nPos = siVert.nTrackPos;
}
} else if (nSBCode == SB_THUMBPOSITION) {
SCROLLINFO siVert;
memset(&siVert, 0, sizeof(siVert));
siVert.cbSize = sizeof(siVert);
if (GetScrollInfo(SB_VERT, &siVert, SIF_ALL)) {
nPos = siVert.nPos;
}
}
CScrollView::OnVScroll(nSBCode, nPos, pScrollBar);
}
気になった点
OnDraw()でSetScrollSizes()呼ぶのは変では?
SetScrollSizes()はログが増えたときに呼ぶべきだと思います
あとOnDraw()がよばれるごとに全行を描画しているのもどうかと思います
たとえば1千万行あったとしたら、再描画ごとに1千万回ループしますよ
次回質問するときはVCのバージョン、MFCの使用の有無、
今回の場合はCScrollViewから派生させたビューを使用していることを明記するよう注意しまし
ょう
ku様、ご指導有難う御座います。
開発環境なのですが、
XP
VC++6.0
を使用しています。
現在正常に動いていない環境としましては、Windows95、Windows98が確認できていま
す。XPと2kは、正常に動作しました。
本ソースは、CString配列sizeに1行ずつ値を格納しています。その為、行数が多くなっ
てしまったり表示フォントが大きくなると、size.cyやsize.cxが16bitで表せない値
(32768以上)になった時に、おかしな動きをします。(スクロールバーが最終行を指して
いるのに最終行が表示されなかったり、ループでログを書きこんでいるのですが、最終
行に正確に書き込みを行わなかったり)
>気になった点
>OnDraw()でSetScrollSizes()呼ぶのは変では?
>SetScrollSizes()はログが増えたときに呼ぶべきだと思います
書き込む直前に呼び出すほうが妥当だと考え、現在ここに入れました。
>あとOnDraw()がよばれるごとに全行を描画しているのもどうかと思います
>たとえば1千万行あったとしたら、再描画ごとに1千万回ループしますよ
そうなんですよね。しかし「他に適切なところがあるのか?」と言われると困ってしま
い、結局OnDrowに入っている現状です。
WM_VSCROLLの実装は、以前試してみたのですが、スクロールバーが表示されなくなって
しまいました。
void CxxxxxView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
SCROLLINFO ScrollData; //スクロールバーの状態
ScrollData.cbSize = sizeof(ScrollData);
ScrollData.fMask = SIF_ALL; //全ての情報を取得
GetScrollInfo(SB_VERT,&ScrollData);//縦スクロールバーの情報取得
switch(nSBCode)
{
case SB_LINEDOWN: // ▼がクリックされた
ScrollData.nPos++;
break;
case SB_LINEUP: // ▲がクリックされた
ScrollData.nPos--;
break;
case SB_PAGEDOWN: // ページダウン
ScrollData.nPos+=ScrollData.nPage;
break;
case SB_PAGEUP: // ページアップ
ScrollData.nPos -=ScrollData.nPage;
break;
case SB_THUMBPOSITION:// ドラッグ後の位置
case SB_THUMBTRACK: // ドラッグ中の位置
ScrollData.nPos = ScrollData.nTrackPos;
break;
}
if(ScrollData.nPos<0)
{
ScrollData.nPos=0;
}
else if(ScrollData.nPos > ScrollData.nMax)
{
ScrollData.nPos = ScrollData.nMax;
}
//スクロールバーの位置を修正
SetScrollInfo(SB_VERT,&ScrollData);
CScrollView::OnVScroll(nSBCode, ScrollData.nPos, pScrollBar);
}
> 本ソースは、CString配列sizeに1行ずつ値を格納しています。
私が思うに、SetScrollSizes()はこの直後あたりが良いだろうと思います
前後がわからないので何とも言えませんが
> そうなんですよね。しかし「他に適切なところがあるのか?」と言われると困ってしま
> い、結局OnDrowに入っている現状です。
メモリDCを使って、OnDraw()ではBitBlt()のみを行うという方法がありますが
私が指摘したのは
表示開始位置と最大表示行数がわかれば
その範囲の行だけを描画してみてはどうか?ということです
つまり画面が20行しか表示できなければ、半端な位置から開始していたとしても
最大で21行描画すれば、残りは描画する必要がないという意味でした
表示画面の一番上の行番号と最大表示行数がわかれば実装できると思います
> WM_VSCROLLの実装は、以前試してみたのですが、スクロールバーが表示されなくなって
> しまいました。
これがまずいのでは?
> SetScrollInfo(SB_VERT,&ScrollData);
コードから推察するとこんな感じのことがしたいのかな?
ScrollData.fMask = SIF_POS;
SetScrollInfo(SB_VERT,&ScrollData);
(これは未確認)
ただしSetScrollInfo()呼ぶと、ビュー画面の方の再描画がうまくできないかもしれない
ので、そこは要注意
> size.cyやsize.cxが16bitで表せない値
> (32768以上)になった時に、おかしな動きをします。(スクロールバーが最終行を指して
> いるのに最終行が表示されなかったり、ループでログを書きこんでいるのですが、最終
> 行に正確に書き込みを行わなかったり)
予想してたのと違う現象のことのようなので、私では答えられないかも
ku様いろいろアドバイス有難う御座います。
現状報告をします。
OnDraw内で行っていたログ表示をやめました。
代わりに以下のソースを追加しました。
メッセージを受け取り、WM_PAINT時に描画を行うようにしました。
まだ、うまくいかない箇所が多々ありますが、少しずつ前進しているかも?
という感じです。
問題点
1.ログが表示された時には、スクロールバーが表示されず、ユーザがウインドウサイズを変更した時
に初めて表示される。
2.スクロールバーの移動しても画面の表示がスクロールされない。
先程、ここまで進みku様にはいろいろとアドバイスを頂いているので出来るだけ早く
報告しようと思い、まだ未完成の部分も多いです。
LRESULT CxxxxView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
int i, len, dy;
HDC hdc;
PAINTSTRUCT ps;
SCROLLINFO si; //スクロールバーの状態
static int dispno, pos;
RECT rc;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL; //全ての情報を取得
GetScrollInfo(SB_VERT,&si);//縦スクロールバーの情報取得
switch (message) {
case WM_SIZE:
GetClientRect(&rc);
dispno = rc.bottom / m_cyChar;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = m_MessageArray.GetSize();
si.nPage = dispno;
si.nPos = pos;
FlatSB_SetScrollInfo(this->m_hWnd, SB_VERT, &si, TRUE);
break;
case WM_VSCROLL:
switch (LOWORD(wParam)) {
case SB_LINEUP:
dy = -1;
break;
case SB_LINEDOWN:
dy = 1;
break;
case SB_PAGEUP:
dy = -1 * si.nPage;
break;
case SB_PAGEDOWN:
dy = si.nPage;
break;
case SB_THUMBTRACK:
dy = HIWORD(wParam) - si.nPos;
break;
default:
dy = 0;
break;
}
dy = max(-1 * si.nPos, min(dy, si.nMax - si.nPos));
if (dy != 0) {
si.nPos += dy;
pos = si.nPos;
FlatSB_SetScrollInfo(this->m_hWnd, SB_VERT, &si, TRUE);
ScrollWindowEx(0, -dy * m_cyChar, NULL, NULL, NULL, NULL, 0);
InvalidateRect(NULL, TRUE);
UpdateWindow();
}
break;
case WM_PAINT:
hdc = ::BeginPaint(this->m_hWnd, &ps);
for (i = 0 ;i <= dispno; i++) {
if (m_MessageArray.GetSize() > 0)
{
if (i + si.nPos < m_MessageArray.GetSize()) {
len = m_MessageArray.GetAt(i)->m_String.GetLength();
TextOut(hdc, 0, m_cyChar * i, m_MessageArray.GetAt(i)->m_String,
len);
}
}
}
EndPaint(&ps);
break;
default:
return CScrollView::WindowProc(message, wParam, lParam);
}
return 0;
}
ここ終わったらちゃんとマルチポストの後始末はしときなよ。
すいませんでした