OnDrawにTextOutやDrawTextで書いた文字列をスクロールさせたい – プログラミング – Home

通知
すべてクリア

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

固定ページ 1 / 3

わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

今年の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
 REE
(@REE)
ゲスト
結合: 24年前
投稿: 240
 

スクロールに関しては、SetScrollSizes を調べてみてください。


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

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

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

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

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


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

補足。

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

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


返信引用
わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

さっそくのレスありがとうございます。

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

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

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

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


返信引用
わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

>PATIOさんの補足

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

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


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

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

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

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


返信引用
わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

いま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
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

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

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

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

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

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


返信引用
aetos
(@aetos)
Noble Member
結合: 6年前
投稿: 1480
 

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

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

> もし、ドキュメントクラスが単なるテキストデータを保持しているとしたら、
> ドキュメントクラスがそのテキストデータを表示するのに必要なサイズをピクセル値

> 計算して引き渡すと言うのはどうかなという気もします。
> テキストを保持しているのであれば、行数と一行の最大文字数を返せば、
> そこからビュークラスで計算できると思います。
> 画面周りの情報はビュークラスが保持しているわけなので計算が必要なら
> ビュークラスでやるのが筋かなと私は思います。

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

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


返信引用
わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

>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を入れて確認しました。)

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


返信引用
わか
 わか
(@わか)
ゲスト
結合: 20年前
投稿: 17
Topic starter  

>シャノンさん
すいません、また入れ違いだったみたいで。
書いている最中に投稿ありがとうございます。。。
そうですね。その考え方そのものは理解できます。


返信引用
aetos
(@aetos)
Noble Member
結合: 6年前
投稿: 1480
 

> そうですね。その考え方そのものは理解できます。

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


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

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


返信引用
PATIO
(@patio)
Famed Member
結合: 4年前
投稿: 2660
 

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


返信引用
固定ページ 1 / 3

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました