CDialogを継承したMy1Dialogを継承したMy2Dialogを作成する方法

[戻る]
ぺこ 2007/10/15(月) 14:34:29
MFCなのですが、自分で作成したダイアログの継承方法がわからず
やり方を求め2日間徘徊しているのですが見つからず質問することにしました。
1.class A : public CDialog
2.class B : public CDialog
3.class C : public CDialog
上記ダイアログ3つをウィザードで作成します。
それぞれのクラスではボタンやエディットといった各種コントロールを実装させ機能を
実現しています。
このうち3のCクラスというのは、A、Bで行う共通の処理を実装しています。
このあと、1、2の継承元であるCDialogをCに変更し、
1.class A : public C
2.class B : public C
A、BのクラスからCのクラスに実装している機能を利用したいのです。
が、どのようにすればよいのか手順がわからず困っています。
メッセージマップなどを修正することはわかっていますが、どのようにどう修正すべき
なのかわかりません。
継承する必要性がないのであれば、その方法などもご教授頂けると助かります。

とおりすがり 2007/10/15(月) 15:14:30
状況がよくわからないのでスケルトンの部分だけ。
cppファイルの

//コンストラクタを変更
CDlg_A::CDlg_A(CWnd* pParent /*=NULL*/)
    : CDlg_C(CDlg_A::IDD, pParent)

//必要に応じて
void CDlg_A::DoDataExchange(CDataExchange* pDX)
{
    CDlg_C::DoDataExchange(pDX);
}

//メッセージマップを変更
BEGIN_MESSAGE_MAP(CDlg_A, CDlg_C)


リソースIDが違ったりすると予期せぬ動きをするので
オススメはしません。

PATIO 2007/10/15(月) 16:28:47
共通の処理と言うのがどういう内容なのかによって
多少変わってくるようにも思えますけれど。

コントロールに絡まない物であるなら
通常のダイアログリソースありきの継承ではなくて
単純にCDialogから派生したクラスを作って
そこに共通処理を入れておいて
他のクラスはそのクラスから派生するようにしたらよろしいかと。
VS2005ではやってませんけれど、VC++6.0ではMFCのクラスから
派生させたクラスから更に派生させるとクラスウィザードで
ハンドラの追加がうまく出来なくなったと記憶しています。
その場合、手動でハンドラの追加をする事になると思います。
まあ、既にウィザードでハンドラを追加したファイルを参考に
すれば、そこまで難しくないとは思いますけれど。

コントロールに絡むような処理で同じと言う話になると
これを派生等でコードの集約をしようとした場合にかえってややこしくなる
ケースもあると思います。元々、MFCのフレームワークがそういう用途を
前提に作られていないような気がするので素直にそれぞれ実装した方が
かえってわかりやすいかもしれません。

ぺこ 2007/10/15(月) 18:14:20
>>とおりすがりさん
似たようなコードをどこかで見て、
class Dlg_A : public CDialog
class Dlg_B : public Dlg_A
として、作ってみたのですが、Dlg_Bを表示したとき、確かにDlg_Aに実装した
ボタンなどが表示され、正常に動作しましたが、肝心のDlg_Bに実装したボタン
などが表示されませんでした。OnInitialDialog

>>PATIOさん
↓それぞれがダイアログ(D1,D2,D3)だとして
D1   D2   D3
+-+  +-+  +-+
| |  |A|  |A|
+A+  +-+  +-+
| |  |B|  |C|
+-+  +-+  +-+

D1ではボタンやエディットボックスなどを配置して、一通りの機能Aを実装しています。
ボタンを押した処理なども通常のダイアログと同じようにD1内で行っています。
そして、新しくダイアログD2,D3が追加され、機能B,Cを実装しました。
このB,Cも、ボタンやエディットボックスなどを配置しています。
ただこのD2,D3にはD1と同じ機能Aを実装したいのです。
ですので、D1をうまいこと継承させ(ダイアログのまま継承しなくてもよいのです
が)、
D2,D3が出来れば・・・と考えています。このD2、D3では、Aを表示する位置も決まって
いますし、Aの機能に手を加えることはありません。

>単純にCDialogから派生したクラスを作って
>そこに共通処理を入れておいて
>他のクラスはそのクラスから派生するようにしたらよろしいかと。
やりたいことはそれです。

>単純にCDialogから派生したクラスを作って
class Dlg_A : public CDialog
これですよね?ここに共通処理を入れています。

>他のクラスはそのクラスから派生するようにしたらよろしいかと。
class Dlg_B : public Dlg_A
・・・あれ?これではないんですか?

で、これをやりたいのですが、その実現方法が・・・

maru 2007/10/15(月) 19:22:11
リソースIDを使用していないのであれば的外れですが。

> class Dlg_A : public CDialog
> class Dlg_B : public Dlg_A
> として、作ってみたのですが、Dlg_Bを表示したとき、確かにDlg_Aに実装した
> ボタンなどが表示され、正常に動作しましたが、肝心のDlg_Bに実装したボタン
> などが表示されませんでした。
となるのは、Dlg_Bのコンストラクタから呼び出されたDlg_Aのコンストラクタが
Dlg_AのリソースIDを使っているからではないですか?

CDlg_Bのコンストラクタを
CDlg_B::CDlg_B(CWnd* pParent /*=NULL*/)
    : CDlg_A(CDlg_B::IDD, pParent)
とすると、
CDlg_B::CDlg_B(UINT id, CWnd* pParent /*=NULL*/)
    : CDialog(id, pParent)
というコンストラクタが必要になります。
/* 動くかどうかは試していません */

maru 2007/10/15(月) 19:24:57
まちがえた!

> CDlg_B::CDlg_B(UINT id, CWnd* pParent /*=NULL*/)
>     : CDialog(id, pParent)
> というコンストラクタが必要になります。

CDlg_A::CDlg_A(UINT id, CWnd* pParent /*=NULL*/)
    : CDialog(id, pParent)

ぺこ 2007/10/15(月) 21:27:47
>>maruさん
リソースIDは使用しています。
ダイアログにはボタンを追加してあるのですが、
IDDを変更しただけだと、どちらかのボタンが表示され、
どちらかの動作を行うだけでした。

====呼出し元======================================
void TestDlg::OnBnClickedButton1()
{
  Dlg1 dlg;
  dlg.DoModal();
}

====継承元ダイアログ(.h)=========
class Dlg2 : public CDialog
{
  DECLARE_DYNAMIC(Dlg2)
public:
  Dlg2(UINT id, CWnd* pParent = NULL);
  virtual ~Dlg2();
  enum { IDD = IDD_DIALOG2 };
protected:
  DECLARE_MESSAGE_MAP()
};

====継承元ダイアログ(.cpp)=======
IMPLEMENT_DYNAMIC(Dlg2, CDialog)
Dlg2::Dlg2(UINT id, CWnd* pParent): CDialog(id, pParent)
{
}
BEGIN_MESSAGE_MAP(Dlg2, CDialog)
END_MESSAGE_MAP()

====派生ダイアログ(.h)==========
#include "Dlg2.h"
class Dlg1 : public Dlg2
{
  DECLARE_DYNAMIC(Dlg1)
public:
  Dlg1(CWnd* pParent = NULL);   // 標準コンストラクタ
  virtual ~Dlg1();
  enum { IDD = IDD_DIALOG1 };
protected:
  DECLARE_MESSAGE_MAP()
};

====派生ダイアログ(.cpp)==========
IMPLEMENT_DYNAMIC(Dlg1, Dlg2)
Dlg1::Dlg1(CWnd* pParent): Dlg2(Dlg1::IDD, pParent)
{
}
BEGIN_MESSAGE_MAP(Dlg1, Dlg2)
END_MESSAGE_MAP()

そもそも、表示するコントロールやそのコントロールが実装している処理を
1つにまとめたいと思ったとき、通常どのようにまとめるのでしょう?
考え方が間違っているのでしょうか?

とおりすがり 2007/10/16(火) 09:23:39
自分ならオプションのフラグを追加して
そいつがどうかでコントロールの表示を消すとか、使用できなくするとかが
単純かつ確実にひとつにまとまると思ってますけど。(リソースを使う場合)
自分でCreateするなら如何様にでもすればOK

>ぺこ 2007/10/15(月) 18:14:20
の発言からするに、Aダイアログを子ダイアログにしてB,Cに貼り付けてしまうのも
ひとつの手かと思いました。

#質問の答えになってないかもしれませんが・・・

maru 2007/10/16(火) 10:52:32
> ダイアログにはボタンを追加してあるのですが、
> IDDを変更しただけだと、どちらかのボタンが表示され、
> どちらかの動作を行うだけでした。
リソースには継承の機能がないので、指定したIDのダイアログの内容が使用されます。
私が例示した方法は、Dlg_BのリソースにDlg_Aの内容も含めて記述することが前提です。
CDialogのコンストラクタにはリソースIDを一つしか渡せないので、そこは自明と考えて
時に書きませんでした。

ダイアログの処理(C++のクラスコード)を派生させてダイアログに任意の新規機能を
追加することは可能ですが、リソースを含めて差分プログラミングと成るような処理を
作成するのは困難です(前述したようにリソースには継承の機能がないため)。
CDialogの代わりに自分でリソースIDからダイアログボックスを作成する処理を自作
するなら可能でしょう。

私の場合、ダイアログの内容がほんのちょっと違っているだけなら
> とおりすがり 2007/10/16(火) 09:23:39
> 自分ならオプションのフラグを追加して
> そいつがどうかでコントロールの表示を消すとか、使用できなくするとかが
> 単純かつ確実にひとつにまとまると思ってますけど。(リソースを使う場合)
のようにして、特にクラスは分けるようなことはしていません。

後、Dlg_AのリソースとDlg_Bの追加分の関連が少ない場合、プロパティページを
使って複数のシートに分けて管理すると言うのもありではないかと考えます。

ぺこ 2007/10/16(火) 11:16:36
[[解決]]
子ダイアログでやりたいことが実現できました。
これまでコントロールを持たないクラスは何も気にせず継承ができましたので、
ダイアログも似たような感じかなと思い、継承すれば思っていることができるのでは?
という考えでした。ただ、コントロールが含まれているためメッセージマップなどを
どう扱うべきなのか見当がつかず困っていました。

1.メインになるダイアログを作成(MFCのダイアログベースで作成)
2.ダイアログA作成(ウィザードで何も変更せず)(このダイアログの中に)
3.ダイアログB作成(ウィザードで何も変更せず)(このダイアログを子として乗せ
る)
4.ダイアログBのプロパティをBorder:なし、Style:子に変更
5.ダイアログBにクラスDlg_Bを追加する。(Dlg_B.h、Dlg_B.cpp)
6.ダイアログBにボタンを1つ配置し、クリックイベントにMessageBox(TEXT("B"));
を追加。
7.ダイアログAにウィザードでprotectedにメンバ変数 mx_DlgB を追加。型はDlg_B。
8.ダイアログAのOnInitDialogで
  CRect rect;
  mx_DlgB.Create(Dlg_B::IDD, this);   // 子ダイアログを生成して
  mx_DlgB.GetClientRect(&rect);       // 子ダイアログのサイズ取って
  rect.OffsetRect(0, 0);                // 表示する位置を決めて
  mx_DlgB.MoveWindow(&rect, FALSE);    // そこへ移動して
  mx_DlgB.ShowWindow(SW_SHOW);        // 表示させる

メインダイアログにボタンが表示され、クリックで"B"が表示されました。
PATIOさん、とおりすがりさん、maruさんには感謝致します。
大切な時間を割いていただき、みなさま本当にありがとうございました。

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