MFC タブコントロールのOwner Draw Fixedについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
かなたん
記事: 50
登録日時: 12年前
連絡を取る:

MFC タブコントロールのOwner Draw Fixedについて

#1

投稿記事 by かなたん » 11年前

Windows7 Visual Studio 2008で、MFCのSDIでダイアログを作成しています。
MFC編 - タブコントロールの基本1を参考にタブコントロールを使っています。
表示は問題なくでき、タブコントロールのプロパティーの表示部分をいじってどうなるのか試してみました。
Owner Draw Fixedをtureにしたときのデザインがいいと思い、そのまま実行させてみました。
[album]698[/album]
すると、そのダイアログを表示させようとするとDebug Assertion Faildが出てしまいました。
[album]700[/album]
ダイアログのほうを見てみると、中身は表示されていますがタブが表示されていませんでした。
[album]699[/album]
そもそもOwner Draw Fixedの説明にある「親ウィンドウがタブを表示するようにします」の意味はわかっておらず、ただデザインが気に入っただけです。
ネットで調べてみようと思いましたが、読んでもよくわかりませんでした。
falseのままにすれば問題はないのですが、Owner Draw Fixedはtrueにすべきではないのでしょうか?
どのようにすればあのようなデザインが使えるようになるでしょうか?
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#2

投稿記事 by softya(ソフト屋) » 11年前

画像が小さすぎて見えませんので、お願いします。

Owner Draw FixedはtrueはOSやMFCに任せずに自分で親ウィンドウが描画を担当するという意味です。
デザインが気に入られたみたいですが、あれは何もしていない状態ですね。
ただ、Owner Draw FixedはtrueにしただけだとDebug Assertion Faildしないので原因は別にあるのでは?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

かなたん
記事: 50
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#3

投稿記事 by かなたん » 11年前

softya(ソフト屋) さんが書きました:画像が小さすぎて見えませんので、お願いします。
確かに画像小さいですね・・・すいません。
画像はフォーラムのアルバムにアップロードしたものです。
「File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winctrl2.cpp
Line: 1219」
などと書かれていました。
softya(ソフト屋) さんが書きました:Owner Draw FixedはtrueはOSやMFCに任せずに自分で親ウィンドウが描画を担当するという意味です。
デザインが気に入られたみたいですが、あれは何もしていない状態ですね。
そうだったんですか。
VBAのユーザフォームにあるタブみたいでいいと思いました。
softya(ソフト屋) さんが書きました:ただ、Owner Draw FixedはtrueにしただけだとDebug Assertion Faildしないので原因は別にあるのでは?
無事タブコントロールを表示できることを確認した後、あれこれプロパティーの表示部分をいじったりしましたが、どれも変えては戻しの繰り返しです。
なので、今回はうまくいっていた状態からOwner Draw Fixedをtrueにしただけです。
本当はあったけど隠れていたバグがOwner Draw Fixedをtrueにしたのをきっかけに出てきたのでしょうか?
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#4

投稿記事 by softya(ソフト屋) » 11年前

かなたん さんが書きました:画像が小さすぎて見えませんので、お願いします。

確かに画像小さいですね・・・すいません。
画像はフォーラムのアルバムにアップロードしたものです。
「File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winctrl2.cpp
Line: 1219」
などと書かれていました。
その行にはどういう命令が有るんでしょうか?
かなたん さんが書きました:
softya(ソフト屋) さんが書きました:ただ、Owner Draw FixedはtrueにしただけだとDebug Assertion Faildしないので原因は別にあるのでは?
無事タブコントロールを表示できることを確認した後、あれこれプロパティーの表示部分をいじったりしましたが、どれも変えては戻しの繰り返しです。
なので、今回はうまくいっていた状態からOwner Draw Fixedをtrueにしただけです。
本当はあったけど隠れていたバグがOwner Draw Fixedをtrueにしたのをきっかけに出てきたのでしょうか?
これだけの情報だとなんとも言えないんですが、試しに新規でダイアログを作ってタブコントロールを置いてOwner Draw Fixedをtrueとして見ましたが問題は発生しませんでした。

【補足】
トラブルが合った場合、問題を切り分けるために私はよく新規でプロジェクトを作って、その機能だけを実験します。
その上でトラブルを潰して本来のプロジェクトに反映させれば他のバグが原因で悩まされる心配がないからです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

かなたん
記事: 50
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#5

投稿記事 by かなたん » 11年前

softya(ソフト屋) さんが書きました:その行にはどういう命令が有るんでしょうか?

コード:

void CTabCtrl::DrawItem(LPDRAWITEMSTRUCT)
{
	ASSERT(FALSE);  // must override for self draw tab controls
}
のASSERT部分です。
ということは、タブコントロールの描画部分が原因なのでしょうか?
softya(ソフト屋) さんが書きました:これだけの情報だとなんとも言えないんですが、試しに新規でダイアログを作ってタブコントロールを置いてOwner Draw Fixedをtrueとして見ましたが問題は発生しませんでした。

【補足】
トラブルが合った場合、問題を切り分けるために私はよく新規でプロジェクトを作って、その機能だけを実験します。
その上でトラブルを潰して本来のプロジェクトに反映させれば他のバグが原因で悩まされる心配がないからです。
マインスイーパー.rcにあるダイアログ部分とメニューの一部・Resorce.hにある#defineの一部を新規プロジェクトにコピペしてみました。
クラスウィザードでコピーしてきたダイアログのクラスを作り、中身は元のプロジェクトからコピペしました。
そして、新規プロジェクトのview.cppにメニューのメッセージハンドラを作り実行させてみると、なぜかダイアログが出ませんでした。

コード:

void CダイアログテストView::OnShowRecord()
{
	// TODO: ここにコマンド ハンドラ コードを追加します。
	CShowRecord ShowRecord_dlg;
	INT_PTR ShowRecord_Show;
	ShowRecord_Show = ShowRecord_dlg.DoModal();
	if(ShowRecord_Show==IDOK){
	}
}
なにかコピペなどに問題があったのでしょうか?
リソースビューでダイアログなどを開いてみると元の通り表示されますし、Owner Draw Fixedなどの設定も元のままですが・・・
Owner Draw Fixedをfalseにしても変わりませんでした。

ShowRecord.cpp

コード:

// ShowRecord.cpp : 実装ファイル
//

#include "stdafx.h"
#include "ダイアログテスト.h"
#include "ShowRecord.h"


// ShowRecord ダイアログ

IMPLEMENT_DYNAMIC(CShowRecord, CDialog)

CShowRecord::CShowRecord(CWnd* pParent /*=NULL*/)
	: CDialog(CShowRecord::IDD, pParent)
{

}

CShowRecord::~CShowRecord()
{
}

void CShowRecord::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_Record, record_tab);
}


BEGIN_MESSAGE_MAP(CShowRecord, CDialog)
	ON_NOTIFY(TCN_SELCHANGE, IDC_Record, &CShowRecord::OnTcnSelchangeRecord)
END_MESSAGE_MAP()


// ShowRecord メッセージ ハンドラ

BOOL CShowRecord::OnInitDialog()
{
	CDialog::OnInitDialog();

	// TODO:  ここに初期化を追加してください
	CRect    rect;
	record.Create(CRecord::IDD, this);
	record.GetClientRect(&rect);
	rect.OffsetRect(1, 21);
	record.MoveWindow(&rect, FALSE);
	record_tab.InsertItem(0, _T("初級"));
	record_tab.InsertItem(1, _T("中級"));
	record_tab.InsertItem(2, _T("上級"));
	OnTcnSelchangeRecord(0, 0);
	record.ShowWindow(SW_SHOW);

	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

void CShowRecord::OnTcnSelchangeRecord(NMHDR *pNMHDR, LRESULT *pResult)
{
	// TODO: ここにコントロール通知ハンドラ コードを追加します。
    
	int select = record_tab.GetCurSel();
	//*pResult = 0; //こっちだと「動作を停止しました」といわれるので、
	if (pResult) *pResult = 0; //参考にしているサイトのようにこのようにするとエラーは出なかった。
}
Record.cpp

コード:

// First.cpp : 実装ファイル
//

#include "stdafx.h"
#include "ダイアログテスト.h"
#include "Record.h"


// First ダイアログ

IMPLEMENT_DYNAMIC(CRecord, CDialog)

CRecord::CRecord(CWnd* pParent /*=NULL*/)
	: CDialog(CRecord::IDD, pParent)
{

}

CRecord::~CRecord()
{
}

void CRecord::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CRecord, CDialog)
	ON_BN_CLICKED(IDC_Reset, &CRecord::OnBnClickedReset)
	ON_NOTIFY(TCN_SELCHANGE, IDC_Record, &CRecord::OnTcnSelchangeRecord)
END_MESSAGE_MAP()


// First メッセージ ハンドラ

void CRecord::OnBnClickedReset()
{
	// TODO: ここにコントロール通知ハンドラ コードを追加します。
}

void CRecord::OnTcnSelchangeRecord(NMHDR *pNMHDR, LRESULT *pResult)
{
	// TODO: ここにコントロール通知ハンドラ コードを追加します。
	*pResult = 0;
}
なぜかタブコントロールの中身用のクラスにもタブ変更の通知ハンドラを作っていた(今コピペしてみて気が付きました)ので、いらないと思いとりあえずコメントアウトして試してみました。
それでも元のプロジェクトではDebug Assertion Faildですし、新しいプロジェクトではダイアログが出ませんでした。
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#6

投稿記事 by usao » 11年前

>ASSERT(FALSE);
本当にあなたのコードにこれが書かれているのであれば,
Debug実行した際には,ここに到達した時点で
Debug Assertion Failed と言われるのは当たり前です.そうしろと書いたのだから.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#7

投稿記事 by softya(ソフト屋) » 11年前

>ASSERT(FALSE);

これはウィザードが自動生成したコードである可能性が高いですね。自動生成時のコメントが有るはずです。
オーナードローを選択したのだから自分で描画コードを書くはずだから、書いてないのは忘れているミスって解釈なのでしょう。

【補足】
// must override for self draw tab controls
ってウィザードのコメントこれだけ?
まぁ、mustなので書け!って事だけど。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#8

投稿記事 by softya(ソフト屋) » 11年前

あっ、クラスのコピペですがリソース問題とか色々ややこしいので詳しくないうちは止めたほうが良いです。
※ これだけの情報だと何とも言えないので。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#9

投稿記事 by ISLe » 11年前

CTabCtrl::DrawItemというのはMFCのソースコードなのでは?
クラスウィザードで新規にクラスを作るとそのクラスにオーバーライドされたメンバ関数が自動生成されるのではないかと。

かなたん
記事: 50
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#10

投稿記事 by かなたん » 11年前

usao さんが書きました:>ASSERT(FALSE);
本当にあなたのコードにこれが書かれているのであれば,
Debug実行した際には,ここに到達した時点で
Debug Assertion Failed と言われるのは当たり前です.そうしろと書いたのだから.
softya(ソフト屋) さんが書きました:>ASSERT(FALSE);

これはウィザードが自動生成したコードである可能性が高いですね。自動生成時のコメントが有るはずです。
オーナードローを選択したのだから自分で描画コードを書くはずだから、書いてないのは忘れているミスって解釈なのでしょう。

【補足】
// must override for self draw tab controls
ってウィザードのコメントこれだけ?
まぁ、mustなので書け!って事だけど。
Assertionに書かれていたファイルは私が作ったものではなく、ファイルパスもf:\から始まっていることからどこにあるのかさえわかっていませんでした。
ですが、デバッグ中に出たときに中止でなく再試行をしてみると、winctrl2.cppというファイルが開かれ矢印があの位置にありました。
なのでそこのことなんだろうなと思いそのまま引用しました。
自分で描画コードを書く―今回はあのデザインのままにしたいからクラスをオーバーライドして描画用関数の中身は何も書かないようにするのだと思い、とりあえずCShowRecordクラスにCTabCtrlクラスも継承させるようにしてみました。

コード:

class CShowRecord : public CDialog , public CTabCtrl
すると、以下のようなエラーが出てきてしまいました。

コード:

1>省略\showrecord.cpp(31) : error C2594: 'static_cast' : 'void (__thiscall CShowRecord::* )(NMHDR *,LRESULT *)' から 'void (__thiscall CCmdTarget::* )(NMHDR *,LRESULT *)' への変換はあいまいです。
1>省略\showrecord.cpp(43) : error C2594: '引数' : 'CShowRecord *const ' から 'CWnd *' への変換はあいまいです。
ISLe さんが書きました:CTabCtrl::DrawItemというのはMFCのソースコードで、クラスウィザードで新規にクラスを作るとオーバーライドしたコードが自動生成されるのでは?
改めてクラスを作れば自動でオーバーライドされるのでしょうか?
そもそもC++やMFCでクラスのオーバーライドをしたことがなく、どのようにすればいいのかよくわかっていません。
softya(ソフト屋) さんが書きました:あっ、クラスのコピペですがリソース問題とか色々ややこしいので詳しくないうちは止めたほうが良いです。
※ これだけの情報だと何とも言えないので。
中身を同じにさせるためにコピペでいいかななんて思っていました。
それによって何か問題が起こることは考えていませんでした。
その可能性を知らなかったので。
名前さえ合わせればエラーにならないからいいかななんて思っていましたが、そういう問題ではないようですね。
せめて全部ではなく関数の中身などの必要最小限にすることにします。
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#11

投稿記事 by softya(ソフト屋) » 11年前

なるほどMFC側の基礎クラスのDrawItem()でアサートしていたんですね。
詳しくないと分からないと思いますが、MFCは基本的に継承してクラスを利用しています。
なので、いつの間にかオーバーライドを使っている所が多数あります。
Assertionに書かれていたファイルは私が作ったものではなく、ファイルパスもf:\から始まっていることからどこにあるのかさえわかっていませんでした。
ですが、デバッグ中に出たときに中止でなく再試行をしてみると、winctrl2.cppというファイルが開かれ矢印があの位置にありました。
なのでそこのことなんだろうなと思いそのまま引用しました。
自分で描画コードを書く―今回はあのデザインのままにしたいからクラスをオーバーライドして描画用関数の中身は何も書かないようにするのだと思い、とりあえずCShowRecordクラスにCTabCtrlクラスも継承させるようにしてみました。
それと適当に継承しても上手く行かなって当たり前です。
なんだろう、C++の勉強も必要ですが、各クラスの継承関係も把握しないと多重継承なんてしてはいけません。
「MFC 階層図」
http://msdn.microsoft.com/ja-jp/library ... x#feedback
階層的に違う物は「混ぜるな危険!」です。
詳しくない人がやるべきことでは無いですね。
管理しているコントロールリソースも違うので、混ぜられる筈がないのです。

とりあえず、変な事はせず(高度なオーナードローは使わない)普通にタブコントロールを実装してみてください。
それが出来たらステップアップとして将来的に挑戦スべきだと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

かなたん
記事: 50
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#12

投稿記事 by かなたん » 11年前

softya(ソフト屋) さんが書きました:なるほどMFC側の基礎クラスのDrawItem()でアサートしていたんですね。
詳しくないと分からないと思いますが、MFCは基本的に継承してクラスを利用しています。
なので、いつの間にかオーバーライドを使っている所が多数あります。
たとえばMFCで自動で作られるC~ViewクラスがCViewクラスを継承して作られているなどというのは知っていました。
softya(ソフト屋) さんが書きました:それと適当に継承しても上手く行かなって当たり前です。
なんだろう、C++の勉強も必要ですが、各クラスの継承関係も把握しないと多重継承なんてしてはいけません。
「MFC 階層図」
http://msdn.microsoft.com/ja-jp/library ... x#feedback
階層的に違う物は「混ぜるな危険!」です。
詳しくない人がやるべきことでは無いですね。
管理しているコントロールリソースも違うので、混ぜられる筈がないのです。
C++やMFCで自分でクラスの継承をしたことがないのですが、とりあえずCTabCtrlをオーバーライドしなくてはならないらしいと思って適当にやってしまいました。
とりあえず、CDialogクラスもCTabCtrlクラスもCWndクラスの派生クラスですよね?
「Visual C++6.0[MFC6.0]対応MFCライブラリ標準リファレンス」という本を持っていて、そこでCTabCtrlクラスのメンバであるDrawItemを見て、引数の説明にあるDRAWITEMSTRUCT構造体についても見てみたのですが、その構造体の説明を読んでもあまりよくわかりませんでした。
softya(ソフト屋) さんが書きました:とりあえず、変な事はせず(高度なオーナードローは使わない)普通にタブコントロールを実装してみてください。
それが出来たらステップアップとして将来的に挑戦スべきだと思います。
デフォルト設定のままでタブコントロールを使用することはできてます。
画像
ただ、デフォルトのタブコントロールの右側に白い線が出ているのが気になっていました。
あと、選択されたタブが白くなるのも気に入っていません。
そこでプロパティーの表示をあれこれいじったりしてみると、あのOwner Draw Fixedのデザインがいいと思ったのです。
でも、今の私の知識ではこのままのほうがよさそうですかね。
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#13

投稿記事 by softya(ソフト屋) » 11年前

Vista以降の描画スタイルとかも関係ありそうですね。
ここ直すのはかなり知識を必要とするので、知識を増やしてからのほうがよさそうです。
CTalCtrlは使ったことがないのでサンプル出せなくて申し訳ないです。
※ いつもは私は設定メニューなどでプロパティシートのタブを使っているのですが仕組みが違いすぎて例にならないのです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

かなたん
記事: 50
登録日時: 12年前
連絡を取る:

Re: MFC タブコントロールのOwner Draw Fixedについて

#14

投稿記事 by かなたん » 11年前

softya(ソフト屋) さんが書きました:Vista以降の描画スタイルとかも関係ありそうですね。
ここ直すのはかなり知識を必要とするので、知識を増やしてからのほうがよさそうです。
CTalCtrlは使ったことがないのでサンプル出せなくて申し訳ないです。
※ いつもは私は設定メニューなどでプロパティシートのタブを使っているのですが仕組みが違いすぎて例にならないのです。
プロパティーシートとタブコントロールが全くの別物であるのはどこかのサイトで見ました。
今回いろいろと質問してみて私にはわからないことだらけだったので、今回はOwner Draw Fixedについてはあきらめることにします。
いつか継承のこととかしっかり勉強してから改めて試すことにします。
ありがとうございました。
わからないことも、ブログに書いているうちにひらめくこともある。
本当に行き詰ったら、考え直すのも1つの手かな。

閉鎖

“C言語何でも質問掲示板” へ戻る