OnDrawにTextOutやDrawTextで書いた文字列をスクロールさせたい

[戻る]
わか [E-Mail] [HomePage] 2005/02/14(月) 16:13:07
今年の2月からWin2000上でVC++6.0を勉強してます。
初歩的な質問ですみません。本やネットでいろいろ調べまくった
のですが、過去ログなどもうまくHITしなかったので。
(キーワードに何を入れたらいいかもわからないありさまで・・・)

以下に概要を記述します。
(実際はフォントの変更やら、色を変えたりいろいろやってますが。)

<やってみたこと>
1.新規作成でMFC AppWizard(exe)でプロジェクト作成
2.アプリの種類はSDIを選択
3.基本クラスをCViewからCScrollViewにして作成
4.onDraw関数にpDC->SetMapMode(MM_ISOTROPIC)を記述
5.onDraw関数にif(pDC->IsPrinting())でプリンタかどうか判断し、
  SetWindow〜やSetViewport〜などでウインドウ、ビューポートの
  調整
6.onDraw関数にpDC->DrawTextやpDC->TextOutを使ってテキスト表示
  (ここは適当にループとかさせていろいろ書いてやります)
7.ViewクラスのOnInitialUpdate関数のところでsizeTotal.cx と
  sizeTotal.cyの値を10000にする(適当な値です。)

<やりたいこと・わからないこと>
1.要はスクロールバーでテキストを適切にスクロールさせたいんです。
  TextOutにデバイスコンテキストを渡すと文字を書けるのはいいが、
  何に対して書いてるのかわかりません。あとでそれをスクロール
  させるときに、高さや幅ってどのように計算するのでしょうか?計
  算できたとして、sizeTotal.cxなどの値をどこで変えてやれば良い
  のでしょうか?(試しにonDrawで適当な値に変えてみたら、あたり
  まえですが画面がちらつきます。)

2.メニューバーのプレビューを押すと、プレビュー表示ができます。
  上記、5.の調整で一見うまく見えています。(何もしないと、紙に
  対してすごく小さい文字が書かれてプレビューされていました。)
  ここで、プレビュー画面をクリックすると、拡大表示されますよね。
  ところが、用紙が拡大されるだけでなく、その上に書かれた文字まで
  もがさらに拡大表示されたようになります。縮小表示ボタンを押して
  も、もう文字の大きさは戻りません。

VC++のサイトを巡っていると、必ず「onDrawにTextOutで書いてみよう!」
みたいに書かれているのですが、その先どうしていいかがわかりませんで
した。onDrawにTextOutで書いたら、次はどうやってスクロールさせるの
でしょうか?どうやってプレビュー(印刷)するのでしょうか?
(ここでの前提としては、SDIで基本クラスをCScrollViewにした場合です。)

未熟者ですが、どうぞよろしくお願いいたします。

REE 2005/02/14(月) 17:08:18
スクロールに関しては、SetScrollSizes を調べてみてください。

PATIO 2005/02/14(月) 18:18:07
兎にも角にもCScrollViewの説明をHELPで読む事が必要だと思います。
クラス概要をよく読めば、REEさんの指摘されている部分も書いてあります。

印刷に関しては、CScrollViewを使っているからといって勝手に改ページしたりは
してくれないと思います。
なにも実装しないとOnDrawの実装が印刷時にもそのまま呼ばれてしまうので
それでは印刷時の改ページの制御等は全く行われません。
はっきり言ってしまうと改ページをするような印刷がしたいのであれば、
自分できちんと実装しないと無理です。

HELPに「印刷 : 複数ページの文書の印刷」というトピックがありますので
それをじっくり呼んでドキュメント−ビュー・アーキテクチャを使用したときの
印刷処理の流れをきちんと理解してそれぞれ必要な所に必要な実装をしましょう。

ウインドウ周りに関してはMFCがかなり自動化してくれていますが、
印刷に関してはほとんど自前で実装しないといけないと考えましょう。

PATIO 2005/02/14(月) 18:30:20
補足。

CScrollViewは便利なクラスですが、
基本的にウインドウ上でスクロールを実現するには、
スクロールしているように見えるように座標を計算をして描画する必要があります。
スクロール状況に合わせてビューポートを変えると言う手もありますけれど、
基本は座標を計算して描画するという手段だと思います。
この方法だと描画する必要があるのはどのデータなのかを把握した上で
そのデータがウインドウ上ではどの位置になるのかを計算して描画する事になると
思います。

例えば、画面内に四角形を描画したとして、
スクロールバーのイベントを拾って現在表示中の位置を計算してメンバー変数に設定。
クライアント領域の再描画をInvalidateRect等で促し、
OnDrawで表示位置から四角形のウインドウ上の位置を計算して再描画する。
こうすれば、見かけ上スクロールしているように見えるわけです。
古臭い手段ですが、基本はこれだと思います。
見た目はかっこいい事をやっている処理もプログラム内では意外と泥臭いな処理で
実現している事が多いので便利なクラスがあれば使うけれど、
なければ泥臭い処理を自分でやらなければならないと考えていた方がいいです。
自分が特に処理を書かなくてもうまく動いてくれると言うことははっきり言って希少だと
思います。

わか [E-Mail] [HomePage] 2005/02/14(月) 18:52:36
さっそくのレスありがとうございます。

>REEさん
void xxxxView::OnInitialUpdate()に
SetScrollSizes(MM_TEXT, sizeTotal);という記述が
もともと書いてありますよね。このsizeTotalのcxとcyに
渡してやる数値は計算で求めなくてよいのでしょうか?
適当な値(ここでは10000)をセットしたらとりあえず
びろ〜んとスクロールします。でもスクロールバーを戻
そうとすると、残像がのこったり、戻らなかったり、な
んか表示がおかしい。

HELPにある、OnUpdateのところで「ドキュメントメン
バ関数を呼び出すことにより、ビューに関連付けられた
ドキュメントからサイズ情報を取得する」というのが
決め手になりそうな気もしますが、ん〜・・・、そう言
われてもどうコーディングしたらいいのかわからないと
いうのが現状なんです。

>PATIOさん
はい、書いてあります。書いてあるんで読むには読むん
ですけど・・・。VCを始めたばかりで、もちろんHELPも
読んではいるのですが、なかなか意味がわからなくて・・・。

ホントにド素人ですいません。
引き続きいろいろやってみます。

わか [E-Mail] [HomePage] 2005/02/14(月) 19:00:54
>PATIOさんの補足

すいません、レス書き込み中に補足を書いていただいた
ようで、入れ違いとなりました。
補足ありがとうございます。

おっしゃること、理屈ではぼんやりわかっているんです
が、コードを書き慣れていないと、どう記述(表現?)
していいものやら。。。
よろしければ、サンプルなどご紹介していただけないで
しょうか?

PATIO 2005/02/15(火) 10:10:44
一度に解決しようとしないで一つ一つ順を追って解決しましょう。
なぜそうなるのかも理解できていないと意味がありませんし。
SetScrollSizeで設定すべき内容の単位はなんでしょう。
多分、わかさんがわかっているのは行数なんじゃないかと思いますが、
行数から設定するべき単位に直すには何が要るでしょう?
それを得るにはどうすればいいでしょう。

こんな風に順を追って問題を解決しないと多分雲を掴むような話になると思います。

そもそも、SetScrollSizeが何故必要なのかは理解できていますか?
それが理解できていないと先に進まないような気もしますけれど。

わか [E-Mail] [HomePage] 2005/02/15(火) 10:32:40
いまMSDNのチュートリアルにあるScribbleというレッスンの
ステップ4:ビュー機能の強化というのを必至で解析しながら
見よう見真似でやっています。
xxxxDOC.CPPというファイルが自動的に作られていたんですね。
ここにビューに表示しているドキュメントの処理を書いていく
のかな?自分で大きさを決めて初期化して・・・。
なんとなくわかってきました。TextOutで文字を書いたときに
この初期値(サイズ)を超えたら再設定してやればいいかな?

PATIOさんレスありがとうございます。

>SetScrollSizeで設定すべき内容の単位はなんでしょう。

ピクセル・・・かな?

>わかさんがわかっているのは行数なんじゃないかと思いますが

暫定的にこんな風に書いているので、行数もわかりますが、
これって座標(ピクセル値?)も把握できてるといってよい
のかな?高さはこれでいいとして、テキストの幅を返す関数
があったような・・・。

long NowTop;    // 現在の上位置
NowTop = 100;
for(int i=0;i<=50;i++){
  pDC->TextOut(LEFTMGN,NowTop+i*15,"あいうえお");
}

も少しがんばってみます。。。

PATIO 2005/02/15(火) 15:32:56
MFCにはドキュメント−ビュー・アーキテクチャと言うのがあって
この仕組みを使う場合、ドキュメントクラスとビュークラスの役割は
大雑把に次のように分かれると思います。
(間違いがあれば、気が付いた方が指摘してください。)

ドキュメントクラス:
 表示対象になるデータを保持し、ファイルからの読み込みやファイルへの書き出し等も
このクラスが行う。ファイルフォーマットを把握して読み込んだデータを作業しやすく
展開したり、メモリ上でデータを管理してビューの要請にしたがって必要なデータを
取り出して引き渡すのもこのクラスの仕事。
 通常、画面からの入力データもこの中に引き取られて管理されます。
ファイルフォーマットとドキュメントクラスは対になる存在と考えていいと思います。
まあ、クラス設計によって複数のフォーマットを一つのドキュメントクラスで扱うのも
ありですし、その辺は臨機応変でいいと思いますけれど。

ビュークラス:
 OSやアプリからの要請にしたがって画面上に情報を表示します。
自分がどの情報を表示すべきかとか、どのあたりに表示すべきかなどの情報はこのクラス
で管理します。また、ユーザーのオペレーションに従って画面のコントロールを行うのも
このクラスの仕事です。
ユーザーから受取ったデータはドキュメントクラスに引き渡し管理させます。
また、表示に必要な情報はドキュメントクラスから取り出して画面に描画します。
表示対象の情報そのものは基本的にドキュメントクラスから受け取りますが、
ファイルに出力するまでもない表示用の情報等はビュークラスで管理してもいいと思います。

例えば、ドキュメントクラスが画像で情報を保持しているのであれば、
内部的にはピクセル数で画像のサイズを保持しているでしょうから
サイズはそのまま取り出せます。

もし、ドキュメントクラスが単なるテキストデータを保持しているとしたら、
ドキュメントクラスがそのテキストデータを表示するのに必要なサイズをピクセル値で
計算して引き渡すと言うのはどうかなという気もします。
テキストを保持しているのであれば、行数と一行の最大文字数を返せば、
そこからビュークラスで計算できると思います。
画面周りの情報はビュークラスが保持しているわけなので計算が必要なら
ビュークラスでやるのが筋かなと私は思います。
固定ピッチフォントなら一文字の幅と高さから計算可能ですよね。
TextOutで描画しているのであれば、改行処理は自分でやっているはずです。
一回の改行幅がわかっていれば、行数から表示に必要なピクセル値が算出できるはずです。
行数が増えてくると単純に計算するだけではうまく行かなくなりますけれど、
それについては取り合えず今の問題が片付いてから考えてもいいかなと思います。

シャノン 2005/02/15(火) 15:42:12
> MFCにはドキュメント−ビュー・アーキテクチャと言うのがあって
> この仕組みを使う場合、ドキュメントクラスとビュークラスの役割は
> 大雑把に次のように分かれると思います。
> (間違いがあれば、気が付いた方が指摘してください。)

「なんでそんな面倒くさいことするんだよ」って思われる前に補足。

> もし、ドキュメントクラスが単なるテキストデータを保持しているとしたら、
> ドキュメントクラスがそのテキストデータを表示するのに必要なサイズをピクセル値> 計算して引き渡すと言うのはどうかなという気もします。
> テキストを保持しているのであれば、行数と一行の最大文字数を返せば、
> そこからビュークラスで計算できると思います。
> 画面周りの情報はビュークラスが保持しているわけなので計算が必要なら
> ビュークラスでやるのが筋かなと私は思います。

このように、データと表現を分離しておけば、表示部分が GUI ではなくコンソールアプ
リに変更になっても、データ部分(ドキュメントクラス)は変更せずに済みます。
Excel を思い浮かべればわかりやすいと思いますが、Excel は表形式のデータを表形式
で表示することも、折れ線グラフで表示することも、棒グラフで表示することもできま
す。
データと表現が分離されているので、新しい表現方法(グラフ)を追加したいときで
も、データ構造を変更する必要がありません。
逆に、データの内部構造を変更しても、表示部分を変更せずに済ませることもできま
す。

こういうのが、オブジェクト指向の一つのメリットです。

わか [E-Mail] [HomePage] 2005/02/15(火) 16:18:58
>PATIOさん
レスありがとうございます。
なるほど、クラス(オブジェクト指向)の考えそのものですね。

さて、進捗ですが、残念ながらまだうまくいきません。
上で記したOnDrow関数内のfor文のあとに・・・

pDC->TextOut(LEFTMGN,NowTop+i*15,"かきくけこ");

// ドキュメントのサイズを変更(暫定的に横方向は固定)
GetDocument()->ResizeDocument(800,NowTop+i*15);

// スクロールサイズ設定
SetScrollSizes(MM_TEXT, GetDocument()->GetDocSize());

と入れてみましたが、どこかおかしいでしょうか?
(実際うまくいってないのでおかしいのでしょうけど)
仮にNowTop+i*15の値が、でかくなるように適当にかえてみると、
スクロール幅は確かに大きくなります。しかし、一番下までスク
ロールさせても最後に書いた「かきくけこ」が出てきません。
(しかも残像が残る)

もうひとつ不思議な現象が・・・
SetScrollSizesの部分はOnUpdateのところにも記述しました。
そこで、Docの更新を知らせる→OnUpdateを呼ぶために
OnDrawの最後に
pDoc->UpdateAllViews(this);    
を追加したのですが、どうもOnUpdateが呼ばれないようです。
(OnUpdateにMessegeBoxを入れて確認しました。)

印刷/プレビューはちょっと難しそうなので、まずこれだけで
も解決したいと思います。

わか [E-Mail] [HomePage] 2005/02/15(火) 16:22:00
>シャノンさん
すいません、また入れ違いだったみたいで。
書いている最中に投稿ありがとうございます。。。
そうですね。その考え方そのものは理解できます。

シャノン 2005/02/15(火) 16:23:13
> そうですね。その考え方そのものは理解できます。

余計なお世話でしたね。すいませんでした。

PATIO 2005/02/15(火) 16:56:16
基本的にSetScrollSizeはOnDrawの中で呼ぶ物ではないと思います。
データの追加や削除が起こった時点でそれが起こった事をCScrollViewに知らせるために
使用するものです。
早い話、SetScrollSizeに設定された値が垂直スクロールを一番下げた状態と水平スク
ロールを一番右に持っていった状態のときのスクロールバー上の数値になります。
例えば、ドキュメントクラスにCStringArrayを使った文字列の配列を作成して
これに対して表示したい文字列を入れておくとします。
このタイミングでSetScrollSizeを呼び出して今の状態を設定してやります。
何らかのアクションで文字列を増やしたり、減らしたりするのであれば、
そのタイミングでSetScrollSizeを呼び出してサイズを更新する必要があります。
OnDraw(描画部分)ではデータを増やしたり、減らしたりしないで純粋に描画だけの
処理を行うようにしましょう。こういった処理はきちんと分離してあげないと
うまく行きません。
OnDrawは描画を行う為のメンバー関数であってデータ更新を行う為の場所ではないのです。

PATIO 2005/02/15(火) 17:01:44
追伸。
CScrollViewもそうですが、CViewクラスが持っているメンバー関数は
それぞれ役割分担がきちんとしています。
実際にはOSが送出するウインドウメッセージに対応してメンバー関数が設定されています。
(ただし、全てのメンバー関数がウインドウメッセージに対応しているわけではありません)
なので、各関数の役割分担というのをきちんと把握して使用しないと
予定外の動きをしたりする事になるので自分がコード書いている関数が何をすべき関数な
のかを
把握するようにしましょう。
基本的にはその関数の役目でないコードはその関数内に書くべきではありません。

わか [E-Mail] [HomePage] 2005/02/15(火) 17:07:47
>シャノンさん
いえいえ、まだまだ未熟者なので参考になります。

>PATIOさん
つまりそれってOnUpdateのところに記述しておいて、OnDraw
のところにはUpdateAllViews(this)を記述して、OnUpdate
イベントを起こすというのは、考え方としては間違いではない
と思っていいのでしょうか?といってもそのOnUpdateが呼ばれ
ないんですが・・・。

dairygoods 2005/02/15(火) 17:15:22
順番としては、こうなると思います。
1. pDoc->UpdateAllViews(this); を呼ぶ(保持データが変化した事を通知)
2. 全ビューの OnUpdate() が呼ばれる
3. OnUpdate() で、Invalidate() 等を呼び出し、
   画面の更新を指示する。(表示内容が変化したことを通知)
3. OnDraw() が呼ばれる

なので、OnDraw()の中で1や3が起こりうる操作をするのは、
あまり良くないでしょう。

PATIO 2005/02/15(火) 17:33:00
いえ、そもそもOnDrawというのはユーザーがデータを追加したときに呼ばれるのではなくて
画面を更新したいときに呼ばれるものなのでそこでUpdateAllViewsが呼ばれるはずはない
のです。
もし能動的に増やしたり、減らしたりしたいのであれば、
メニューでもこさえてそこを選択されたら増やすとか、
マウスの右クリックで増やすとか、そういうオペレーションを入れるべきです。
ようは、データの増やしたり、減らしたりする操作をOnDrawの中から起動しようというのが
間違いだと思います。

REE 2005/02/15(火) 17:37:36
UpdateAllViewsは、ドキュメントが変更された時に、他のビューに表示を更新するように
通知するためのものです。

そのためドキュメントに対応するビューのうち、引数に指定していないビューのOnUpdate
が呼ばれます。
従って、UpdateAllViews(this); では、自身のOnUpdateは呼ばれません。
自身を含めたOnUpdateを呼びたいのであれば、引数にNULLを指定してください。

わか [E-Mail] [HomePage] 2005/02/15(火) 18:23:46
皆さんレスありがとうございます。
dairygoodsさん、PATIOさんがおっしゃるようにOnDrow内に書く
のは良くないというのは、私もなんとなくそんな気はします。

となると・・・

1.OnDrawでドキュメントにテキストを書いた→とりあえずOK
2.たくさん書いたからドキュメントの高さを増やしたい→どこで?
3.高さを増やしたからスクロール幅も増やしたい→どこで?

dairygoodsさんの言われるような、「順番」というのは、みなさん
どこで覚えてくるのでしょう。経験ですか?
(正直、まだデバッグ方法が良くわかっていないせいもあるが)

試しにREEさんのおっしゃるとおり、NULLを指定したら、確かに
OnUpdateイベントが起きました。
でも、これやっちゃだめなんですよねぇ。。。

REE 2005/02/15(火) 20:16:41
>1.OnDrawでドキュメントにテキストを書いた→とりあえずOK

OnDrawは、ドキュメントの内容を表示するのが役割ですので、
ここで、ドキュメントの変更はしないで下さい。

>2.たくさん書いたからドキュメントの高さを増やしたい→どこで?
>3.高さを増やしたからスクロール幅も増やしたい→どこで?

ドキュメントの高さやスクロール幅を変えるのは、ドキュメントの変更をしたときです。

>試しにREEさんのおっしゃるとおり、NULLを指定したら、確かに
>OnUpdateイベントが起きました。
>でも、これやっちゃだめなんですよねぇ。。。

はい、やらないほうが良いです。

わか [E-Mail] [HomePage] 2005/02/16(水) 08:58:54
>REEさん

ますますわからなくなってきてしまいました。
何か私勘違いしてますか?

>OnDrawは、ドキュメントの内容を表示するのが役割ですので、
>ここで、ドキュメントの変更はしないで下さい。

いろんなサイト、参考書をみると全てOnDrawにTextOutを書いて
います。これってホントはやっちゃいけないんですか?
「内容を表示」するというのはどういう意味なのでしょう?
TextOutは「ドキュメントの変更」を指しているのですか?

>ドキュメントの高さやスクロール幅を変えるのは、ドキュメントの変更をしたときです。

私、ドキュメントの変更の意味がわかっていないみたいです。

tetrapod 2005/02/16(水) 09:44:55
正直 Document-View が何ぞや、あたりからはじめてもらうほうがよさげな気がします。
Scribble とか(Doc-View 入門的には良いけど他の部分が古杉ですが)

Document っつーのは単なるデータです。
HTML をご存知なら例えば &lt;iostream&gt; という文字列を考えましょう。
View っつーのはそれを表示する手段。
先ほどの Document に対して Text-View なるものを用意すると &lt;iostream&gt; と表示。
同じ Document を Html-View なるもので表示すると <iostream> と表示。
# 表示するように view を作る、ってこと。

TextOut は画面にデータを表示する関数です。
表示する際にどのような形式を用いるかを View は決めます。
OnDraw は「描画しなさい」って通知なので View は既存の Doc を自分の形式で表示します。
OnDraw の度に Doc の内容が変わるあるいは変えるっつーのは設計がヘン。

Doc の変更ってのは先の文字列が #include &lt;iostream&gt; に変わったとか。
すると表示されるべき幅が変わるはずです。
ScrollSize を変更する必要は、そんなときだけ生じるはず。

επιστημη 2005/02/16(水) 09:59:23
>>試しにREEさんのおっしゃるとおり、NULLを指定したら、確かに
>>OnUpdateイベントが起きました。
>>でも、これやっちゃだめなんですよねぇ。。。
>
> はい、やらないほうが良いです。

え? なんで? 他のViewまでre-drawしちゃうから?

るーた 2005/02/16(水) 10:02:30
「ドキュメントの変更」を描画領域の変更と置き換えて考えてみてください。

たいちう 2005/02/16(水) 10:24:44
> え? なんで? 他のViewまでre-drawしちゃうから?

OnDraw でやってはダメなんだという話でしょう。
NULL だろうと this だろうと。

# 掲示板での説明では限界があると思いますので、参考書を紹介します。
# 回答者とわかさんの努力によって今回の問題が解決した後でも役に立つと思います。 
# 『Inside Visual C++ version5―Microsoft Visual C++ Ver.5標準教科書』

http://www.amazon.co.jp/exec/obidos/ASIN/4756121748/qid=1108516441/ref=sr_8_xs_a
p_i1_xgl/249-2911650-4016320

REE 2005/02/16(水) 10:40:56
>「ドキュメントの変更」を描画領域の変更と置き換えて考えてみてください。

いやこれは却って誤解を招きそうです。

描画領域の変更は、むしろビューの変更です。
描画内容の変更ならいいですが・・


>> はい、やらないほうが良いです。
>え? なんで? 他のViewまでre-drawしちゃうから?

これに関しては、たいちうさんのフォロー通りの意味です。

わか [E-Mail] [HomePage] 2005/02/16(水) 11:26:36
みなさん長い間お付き合いいただき、ありがとうございます。
私のためを思って、答えを自分で導き出せるように遠まわしに回答して
いただいたりしていただいたようですが、あまり時間もなくなってきて
しまったため、今回のところはもう降参です。

ズバリ教えてください。

1.デフォルトで表示したいテキストを書く場所(TextOut)
2.1に対してスクロール幅を計算および、設定する場所(SetScrollSizes)
3.ウィンドウのサイズを変更したときに2を再計算、再設定する場所
4.ResizeParentToFit(TRUE);を書く場所

私の回答は
1.OnDraw
2.OnInitialUpdateとOnUpdate
3.2と同様
4.OnUpdate

ですが、これではうまくいきませんでした。
どういう結果になるかというと、
1.垂直スクロールバーの計算はうまくいっているように
  見えるが、ツマミを一番下までもってくると、最後の
  行が表示されていない。
2.スクロールすると残像が残る。
3.一番下までスクロールしたあと、ウィンドウの位置を
  変えると、一番上の行が表示される。(スクロールバー
  のツマミは一番下にきたまま)

どうぞ宜しくお願いいたします。

REE 2005/02/16(水) 12:04:15
>1.デフォルトで表示したいテキストを書く場所(TextOut)
>2.1に対してスクロール幅を計算および、設定する場所(SetScrollSizes)
>3.ウィンドウのサイズを変更したときに2を再計算、再設定する場所
>4.ResizeParentToFit(TRUE);を書く場所

>私の回答は
>1.OnDraw

これでOKです

>2.OnInitialUpdateとOnUpdate

これもOKでしょう

>3.2と同様

これは、一般的には不要ですが、
もし書くとしたら、WM_SIZEのハンドラであるOnSizeです。

>4.OnUpdate

これもOKですね


ちなみに、OnDrawで使っているNowTopって何のための変数ですか?
普通にスクロールすればいいのであれば、この様な変数は不要です。
高速化が必要な場合を除いてOnDrawでは現在のスクロール位置とは無関係に描画すればよ
いです。

dairygoods 2005/02/16(水) 12:32:41
> 私のためを思って、答えを自分で導き出せるように遠まわしに回答して
> いただいたりしていただいたようですが

というより、どこで躓いているか見えないので、
一般的な仕組みを理解してもらい、
間違いor勘違いを自力で見つけてもらうしかないのです。


> 2.1に対してスクロール幅を計算および、設定する場所(SetScrollSizes)
> 3.ウィンドウのサイズを変更したときに2を再計算、再設定する場所

この表現がちょっと気になります。

SetScrollSizes は、スクロール等考慮せず、
自分の描きたい絵が 何×何 ピクセルかを指定するだけです。
スクロールバーの設定は、MFCが勝手に計算してくれます。

画面サイズの変更によって、表示する内容が変化しないのであれば、
SetScrollSizes を呼び出す必要はありません。

そして、
OnDrawでは、たとえば、SetScrollSizes で 1000x1000 を指定したなら、
(0, 0) - (1000, 1000) の領域に好きなように描くだけです。

#印刷もする場合は、論理単位(mmなど)を使わないと
#画面と紙のサイズが合いませんが。


> 4.ResizeParentToFit(TRUE);を書く場所

ResizeParentToFit() は、ビュー自身のサイズにあわせて、
メインウィンドウのサイズを変更する機能です。
ウィンドウサイズは、大抵、ユーザーが自由に決めますし、
さらに、何か変更するたびにウィンドウサイズが変化しては、
使いづらいでしょう。

多分、呼び出し自体不要だと思います。
どうしても必要なら、OnInitialUpdateでしょう。

dairygoods 2005/02/16(水) 13:04:20
ちょっと補足します。

> 画面サイズの変更によって、表示する内容が変化しないのであれば、
> SetScrollSizes を呼び出す必要はありません。

は、ウィンドウのサイズを変更したとき(3)の話です。

PATIO 2005/02/16(水) 13:58:28
私が前に書いた自前で計算しないといけないと言う所が誤解を招いてるかも。
自前で計算しなくてはならないのはCScrolViewのような便利なクラスを使用しない場合です。
CScrollViewを使っているなら面倒な部分はCScrollViewが面倒を見てくれるので
毎回、一枚の紙に一行ずつ描画するイメージでいいはずです。
ウインドウ上の位置の調整はCSrollViewがしてくれます。

ドキュメント−ビュー・アーキテクチャを使う場合、
通常はドキュメントクラスに表示したい文字列を持たせます。
例えば、ドキュメントクラスのメンバー変数にCStringArrayの変数を作成して
ドキュメントクラスのOnNewDocumentで文字列配列を必要なデータで初期化すれば、
起動時に既にデータが設定された状態に出来ると思います。
通常、ドキュメントクラスはそのドキュメントクラスが扱うデータを取り出したり
設定したり、追加したりするメンバー関数を追加定義するのが普通です。
この場合なら、データの行数を取得する関数、インデックスを指定して特定の行のデータ
を取り出す関数、
データに指定したデータを追加する関数等がドキュメントクラスには必要になると思います。
ビュークラスのOnInitialUpdateでドキュメントクラスから文字列の行数をもらえば、
表示に必要なY方向の幅は計算できると思いますから、これをSetScrollSizeで設定します。
ビュークラスのOnDrawは、ドキュメントクラスから一行ずつデータをもらいながら
データの行数分だけTextOutします。
みそになるのは、ビュークラス自身は内部にデータが何行あるとか、どの行にどんなデー
タがあるとか知っている必要はないということです。
ビュークラスは、ドキュメントクラスに何行あるかをたずね、
一行ずつデータを取り出して、行数回TextOutを繰り返すと言う処理をするだけにします。

表示するデータを増やしたいのであれば、メニュー等を用意しておいて
それを操作したらデータが一行分増えると言う風にします。
例えば、メニューに「データ」という項目を設けて、これをクリックしたらデータを増や
すでもよいです。
クリックされたら、データをドキュメントクラスに追加して、あとはOnInitialUpdateで
やった事と同じ処理でSetScrollSizeを行います。

ドキュメント−ビュー・アーキテクチャを使用するということは、
表示系とデータ管理系を切り離して自由に組み合わせが出来るような構造にすると言うの
が目的になります。
この辺の考え方は今後、ドキュメント−ビュー・アーキテクチャを使うのであれば、基本
になる考え方なのできちんと抑えておいた方がいいです。

わか 2005/02/17(木) 09:40:23
ご回答ありがとうございます。
昨日の午後から体調を崩し今日は自宅でお休みです。(インフルエンザっぽい)

>REEさん
記述する場所はおおむねあってますよね。
では、なぜ残像が残ったり、スクロールバーのつまみを一番下に
移動しても一番下まで表示されないのか?また、ウィンドウの位置
を動かすと、表示が一番上に戻ってしまうのか?(つまみはそのまま)

>ちなみに、OnDrawで使っているNowTopって何のための変数ですか?
この変数は、次の行はどの位置(y方向)から書き始めたらよいか
保存する変数です。実はTextOutの前にDrawTextも使って、領域を
使った表みたいなものを表示しているためです。説明を省略してし
まってすいません。

>dairygoodsさん
>というより、どこで躓いているか見えないので
結果が上記のように意図していない結果になることに躓いています。

>自分の描きたい絵が 何×何 ピクセルかを指定するだけです。
自分の描きたい絵(文字)が何行分あるかを計算して指定している
つもりなんですが・・・・。計算値が間違っているかもしれないの
で、ためしに明らかに大きい値を指定しても最後の行が表示されて
いないのです。

>画面サイズの変更によって、表示する内容が変化しないのであれば、
>SetScrollSizes を呼び出す必要はありません。
そうなんですか。これは私の勘違いでした。

>OnDrawでは、たとえば、SetScrollSizes で 1000x1000 を指定したなら、
>(0, 0) - (1000, 1000) の領域に好きなように描くだけです。
なんか私の発想は順番が逆なんですかね。私のイメージでは、SetScrollSizes
で1000x1000を指定する前に、まず文字を描いてしまって、このサイズ
では表示しきれないから、あとで必要なサイズを計算して、改めて
SetScrollSizesでサイズを指定するイメージでした。
(印刷の話はちょっとおいておきます。)

REE 2005/02/17(木) 11:06:04
とりあえず、症状に影響しない部分を削除した上で、
関連すると思われる関数(特にOnDraw)のソースをそのまま載せてみては如何ですか?
その際には、削除したソースでも症状が再現することを確認しておいてください。

わか [E-Mail] [HomePage] 2005/02/18(金) 10:39:28
[[解決]]
REEさんありがとうございます。サンプルソースを作る過程で
削っているうちに、悪さ(残像およびスクロールが変になる)
しているコードがわかりました。でも、なぜなのかはわかりま
せん。このコードはネットから探し出してきたものです。
(とりあえず後回しにした、プレビューで文字が小さくなる
件で調べたときに見つけました。)
コメント「// プリンタかどうかチェック」のところから
「// フォント設定」の手前のところまでです。
ここを外すと、表示、スクロールとも、とりあえずうまくい
くような感じです。(しかしプレビューを表示してみると、
やはり文字が小さくなって、おかしな表示になります。)
プレビューについては、しばらく自力でまた調べます。
どうしてもわからないときは別件としてまた質問したいと思
います。そのときはよろしくお願いいたします。

ということで、いちおう解決・・・かな?
みなさん、長い間ありがとうございました。大変ご迷惑を
おかけいたしました。

void CSampleView::OnInitialUpdate()
{
    (省略)
    // コード修正ここから
//    sizeTotal.cx = sizeTotal.cy = 100;
    sizeTotal.cx = sizeTotal.cy = 1000;    // とりあえず
    // ここまで
    (省略)
}

void CSampleView::OnDraw(CDC* pDC)
{
    (省略)
    // TODO: この場所にネイティブ データ用の描画コードを追加します。

// コード追加ここから
    
    long NowTop;    // 現在の上位置
    long NowLeft;    // 現在の左位置

    // プリンタかどうかチェック。
    // 印刷なら色や設定を印刷用に切り替える。
    pDC->SetMapMode(MM_ISOTROPIC); // 1ピクセルが正方形になる
    pDC->SetWindowOrg(0, 0);
    pDC->SetViewportOrg(0, 0);
    pDC->SetWindowExt(500, 400);
    pDC->SetViewportExt(500, 400);
    
    if(pDC->IsPrinting()){
        int i,j;
        i = pDC->GetDeviceCaps(HORZRES); // これで紙の幅と高さが解る
        j = pDC->GetDeviceCaps(VERTRES);
        pDC->SetWindowOrg(0, 0);
        pDC->SetViewportOrg(0, 0);
        pDC->SetWindowExt(500, 400);
        pDC->SetViewportExt(i, j);
    }
    else{
        pDC->SetMapMode(MM_TEXT);
    }

    // フォント設定
    pDC->SetTextColor(RGB(0,0,0));    // テキストの色
    pDC->SetBkMode(TRANSPARENT);    // バックグラウンドモード設定
    CRect myAREA(10,10,100,100);    // 矩形領域確保(サイズ適当)

    // 罫線用ブラシの設定
    CBrush myBRUSH(RGB(0,0,0));        // 黒ブラシ
    pDC->SelectObject(&myBRUSH);    // 黒ブラシを選択

    // *****リテラル部*****
    CString ss = "XXXX:";        // 文字列をセット
    pDC->DrawText(ss,myAREA,DT_CALCRECT | DT_LEFT); // 最適な高さと幅を計算
    pDC->DrawText(ss,myAREA,DT_LEFT);    // 矩形領域文字列出力
    NowTop = myAREA.bottom;
    (中略)

    // *****表のヘッダ部*****
    ss = "各検査項目";            // 文字列を再セット
    // 構造体の値を変更する
    myAREA.left = 10;
    myAREA.top = NowTop + 5;        // 位置調整
    pDC->DrawText(ss,myAREA,DT_CALCRECT | DT_CENTER); // 最適な高さを計算
    myAREA.right = 200;        // 幅を調整
    pDC->DrawText(ss,myAREA,DT_CENTER);    // 矩形領域文字列出力
    pDC->FrameRect(myAREA,&myBRUSH);    // 矩形領域外周出力
    NowTop = myAREA.bottom;
    NowLeft = 200;
    (中略)

    // *****表のディテール部*****
    ss = "項目@\n項目A\n項目B";    // 文字列を再セット
    myAREA.left = 10;
    myAREA.top = NowTop-1;        // 位置調整
    pDC->DrawText(ss,myAREA,DT_CALCRECT | DT_CENTER); // 最適な高さを計算
    myAREA.right = 200;        // 幅を調整
    pDC->DrawText(ss,myAREA,DT_CENTER);    // 矩形領域文字列出力
    pDC->FrameRect(myAREA,&myBRUSH);    // 矩形領域外周出力
    NowTop = myAREA.bottom;
    NowLeft = myAREA.right;
    (中略)

    // ここは文字をたくさん書いて高さを稼ぐための仮処置
    for(int i=0;i<=20;i++){
        pDC->TextOut(10,NowTop+i*15,"あいうえお");
    }
    pDC->TextOut(10,NowTop+i*15,"かきくけこ");
    
// ここまで

}

PATIO 2005/02/18(金) 10:50:18
多分、DCの解像度を見てサイズ調整するコードがあったのではないかと思うのですが、
その所為で実際に更新しているサイズとCScrollViewが認識しているサイズにずれが出た
のかもしれませんね。

通常印刷を行うときは改ページ処理が必要になるのでOnDrawに印刷用のコードまで書くよ
りも印刷用のコードと画面用の描画コードは分けた方が無難です。
仮にやるとしても共通になりそうな部分だけ別関数にしてOnDrawとOnPrintの両方から呼
ぶようにするとかした方がいいと思いますよ。

dairygoods 2005/02/18(金) 11:48:45
CScrollViewは、プログラマがスクロール量を気にしなくても
描画できるように、SetViewportOrg() で原点を調整しています。

なので、OnDrawでSetViewportOrg(0,0)としてしまうと、
描画位置がずれます。

古いスレッドには追加発言できません。