こんにちわ。
VC++6.0ダイアログベースで、
ドロップダウンリスト型コンボボックスに
エクスプローラからファイルをドラッグ&ドロップして
そのファイル名をコンボボックスのリストボックスの項目に
追加し選択したいのですがどうすればいいですか?
【WM_DROPFILES】というメッセージが関係あると思うのですが
コンボボックスではないのでしょうか?
ダイアログでドラッグ&ドロップはできそうなのですが・・・
教えて下さい。
1.リソースのコンボボックスのプロパティの ドラックドロップを許可 にチェックを入
れる。
2.CComboBoxのサブクラス(説明しやすいようにCDropComboBoxとする)を新規作成し、
WM_DROPFILES ハンドラを作成する。
下記のURLはCEditの場合です。参考にはなると思います。
http://homepage2.nifty.com/DSS/VCPP/MFC/CEdit/CEditFileDrop.htm
3.ダイアログクラスにCDropComboBox型のコントロール変数を追加する。
って感じでどうでしょうか?
Blueさんありがとうございます。
CDropComboBoxというクラスを基本クラスCComboboxで作成し、WM_DROPFILESの関数を作
成しました。そしてダイアログクラス CSelLogFile で型CDropComboBoxでコンボボック
スのコントロール変数を作成すると、以下のエラーが発生しました。
派生クラスなど作成したことないのでどうすればいいわかりません。色々やってみたん
ですが・・・
\sellogfile.h(22) : error C2146: 構文エラー : ';' が、識別子 'm_DropCmb1_Ctrl' の
前に必要です。
\sellogfile.h(22) : error C2501: 'CDropComboBox' : 識別名を宣言するのに、型が指
定されていません。
\sellogfile.h(22) : error C2501: 'm_DropCmb1_Ctrl' : 識別名を宣言するのに、型が
指定されていません。
ClassWizardでコントロール変数を追加下ということでよいのですよね?
そうすると、注意を促すメッセージボックスが出ると思うのですけど。
ダイアログクラスのヘッダに #include DropComboBox.h の一文を追加してください。
ヘッダのほうにインクルードするのですね。
できました。できました。
ありがとうございます。
あとはファイル名を取得できれば解決です。
やってみます。
Blueさんもうひとつ質問いいですか?
ファイル名はBlueさんの例のおかげで取得できたのですが
新規作成したCDropComboBoxクラス内関数OnDropFiles()内で
ダイアログクラス CSelLogFile のメンバ変数(さきほど作成した_DropCmb1_Ctrl)
を用いてCComboBox::InsertStringなどの関数を使いたいのですがどうやるのでしょう
か?
さきほどから色々やってるのですがうまくいかなくて・・・
自分自身(this)ではなくて?
例えば
void CDropComboBox::OnDropFiles(HDROP hDropInfo)
{
// TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォル
トの処理を呼び出してください
UINT files;
TCHAR path[ MAX_PATH ];
// ドロップされたファイル数を取得
files = ::DragQueryFile( hDropInfo, UINT_MAX, NULL, 0);
for ( UINT i = 0; i < files; i++ )
{
::DragQueryFile( hDropInfo, i, path, sizeof( path ) / sizeof(
TCHAR ) );
this->AddString( path ); // 末尾に追加
if ( !i ) this->SetWindowText( path );
}
CComboBox::OnDropFiles(hDropInfo);
}
えっ?このCDropComboBoxクラス内では
すでにコンボボックスのコントロール扱いになるのですか?
私がやりたかったのは通常コンボボックスを操作する時
コントロール変数を作成し(例えばm_DropCmb1_Ctrl)、
m_DropCmb1_Ctrl.AddString;
と記述すると思いますが、
CDropComboBoxクラス内でこのようにアクセスするとえらーになります。
今閃いたのですが、この時は元のダイアログクラスのヘッダをインクルードし、
CSelLogFile dlg;
dlg.m_DropCmb1_Ctrl.AddString;
と記述すればできると思うんですが・・
これってBlueさんのthisのやり方と同じことですよね?
> えっ?このCDropComboBoxクラス内では
> すでにコンボボックスのコントロール扱いになるのですか?
そうです。
ただ、どのコンボボックスかというのは、実体をつくらないとわかりませんけど。
> これってBlueさんのthisのやり方と同じことですよね?
全然違いますよ。
そのやりかただと、そのダイアログのコンボボックスだけにしか対応できません。
例えば、ダイアログに 3つ CDropComboBox を配置したらどうなりますか?
>ただ、どのコンボボックスかというのは、実体をつくらないとわかりませんけど。
なるほど。ようするにCDropComboBoxでコントロール変数を宣言している全てのコンボボ
ックスに対してアクセス可能というわけですね。
しかし実体とやらを作るのはどうやるのでしょうか?
>例えば、ダイアログに 3つ CDropComboBox を配置したらどうなりますか?
CSelLogFile dlg;
dlg.m_DropCmb1_Ctrl.AddString;
dlg.m_DropCmb2_Ctrl.AddString;
dlg.m_DropCmb3_Ctrl.AddString;
ではだめですかね?
ちなみに私がやりたいことの詳細は、
--------------------------------------------------------------
CSelLogFileにはコンボボックス1つとボタン1つしかありません。
ボタン押下でコモンダイアログを表示しファイルを選択します。
また、ドラッグ&ドロップもできるようにします。
コンボボックスの項目数は最大5つです。
項目が5つある時ファイルが選択されたら最古文字列を削除し、
項目の先頭に今選択されたファイルを追加したいのです。
---------------------------------------------------------------
このような仕様の場合、どういうやり方が最適だと思いますか?
> ではだめですかね?
うーむ。。。
わかりにくいかもしれませんがMFC関係なしのサンプルです。
mainをダイアログ, barをCDropComboBox, TestメソッドはDropイベントとして考えてみ
てください。
#pragma warning( disable : 4786 )
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Foo
{
private:
vector< string > m_list; // 文字列リスト
public:
void AddString( const string& s ) // 追加
{
m_list.push_back( s );
}
void ShowList() const // 表示
{
cout << ----------------- << endl;
for ( vector< string >::const_iterator it = m_list.begin(); it !=
m_list.end(); ++it )
{
cout << *it << endl;
}
cout << ----------------- << endl;
}
};
class Bar : public Foo
{
public:
void Test() // 任意のメソッド
{
AddString( TEST );
AddString( test );
}
};
int main()
{
Bar b1, b2, b3;
b1.AddString( abc );
b2.AddString( abc );
b3.AddString( abc );
b1.AddString( efg );
b2.AddString( efg );
b3.AddString( efg );
b1.Test();
b3.Test();
cout << b1 << endl;
b1.ShowList();
cout << b2 << endl;
b2.ShowList();
cout << b3 << endl;
b3.ShowList();
return 0;
}
> このような仕様の場合、どういうやり方が最適だと思いますか?
の場合、AddStringメソッドのようなCComboBoxの持っているメソッドではなく、
自作のメソッドで文字列追加をするようにすれば良いでしょう。
(そのメソッドからはCCombBox::AddStringを呼ぶが。)
> コンボボックスの項目数は最大5つです。
というのも、CDropComboBoxのメンバにするとか。
>の場合、AddStringメソッドのようなCComboBoxの持っているメソッドではなく、
自作のメソッドで文字列追加をするようにすれば良いでしょう。
(そのメソッドからはCCombBox::AddStringを呼ぶが。)
ダイアログクラスでやっている処理と同じ処理をドラッグ&ドロップ時も行いたいので
この処理をメンバ関数にして両方でコールするというのはどうでしょうか?
以下はボタン押下時の処理です。
このファイルパス取得以後の処理をサブルーチン化し引数にファイル名を。
void CSelLogFile::OnButton1Commonfile()
{
// TODO: この位置にコントロール通知ハンドラ用のコードを追加してくださ
い
CString str;
int ret, i, num;
CFileDialog dlg( TRUE, NULL, NULL, OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT | OFN_FILEMUSTEXIST, szFilter, NULL );
dlg.m_ofn.lpstrTitle = ログファイルの選択;
// キャンセルボタンが押された時
if( dlg.DoModal() != IDOK ) return;
// 選択されたファイルのフルパスを取得する
str = dlg.GetPathName();
// コンボボックスのリストボックスに選択されたファイルがあるか検索する
ret = m_DropCmb1_Ctrl.SelectString(-1,str);
// 見つからなかったとき
if( CB_ERR == ret ){
// コンボボックスの項目数を取得する
num = m_DropCmb1_Ctrl.GetCount();
// 最大コンボボックスの項目数以上のとき
if(num >= MAX_COMBO_LIST_NUM ){
// 項目削除
for( i=(MAX_COMBO_LIST_NUM-1); i<num; i++ ){
m_DropCmb1_Ctrl.DeleteString(i);
}
}
// リストボックスの先頭に追加し、選択する
m_DropCmb1_Ctrl.InsertString(0,str);
m_DropCmb1_Ctrl.SetCurSel(0);
}
else{
// 見つかったファイルを項目から削除し、先頭に追加し、選択する
m_DropCmb1_Ctrl.DeleteString(ret);
m_DropCmb1_Ctrl.InsertString(0, str);
m_DropCmb1_Ctrl.SetCurSel(0);
}
}
>えっ?このCDropComboBoxクラス内では
>すでにコンボボックスのコントロール扱いになるのですか?
というか、このクラスはコンボボックスのコントロールそのものです。
このクラスは、CComboBoxを派生していますので、その機能をそのまま持っています。
m_DropCmb1_Ctrlの型もCDropComboBoxになっているはずですので確認してみてください。
>CSelLogFile dlg;
>dlg.m_DropCmb1_Ctrl.AddString;
ちなみにこれは問題外です。
なぜかというと、これでは、新しくダイアログを作り、その中のコンボボックス
(当然ドロップされたコンボボックスとは別のもの)に追加することになってしまいます。
> 自作のメソッドで文字列追加をするようにすれば良いでしょう。
> (そのメソッドからはCCombBox::AddStringを呼ぶが。)
AddStringをオーバーライドするとこんな感じ。
// DropComboBox.h : ヘッダー ファイル
・・・
class CDropComboBox : public CComboBox
{
・・・
public:
int m_Max; // 最大ストック数
int AddString( LPCTSTR lpszString );
};
---------------------------------------------------------
// DropComboBox.cpp : インプリメンテーション ファイル
・・・
int CDropComboBox::AddString( LPCTSTR lpszString )
{
// 重複
if ( this->FindString( -1, lpszString ) != CB_ERR )
{
return CB_ERR;
}
// ストック数
if ( this->m_Max )
{
if ( this->m_Max == this->GetCount() )
{
this->DeleteString( 0 );
}
}
return ( ( CComboBox* )this )->AddString( lpszString );
}
---------------------------------------------------------
// SelLogFile.cpp : インプリメンテーション ファイル
・・・
BOOL CSelLogFile::OnInitDialog()
{
・・・
// TODO: 特別な初期化を行う時はこの場所に追加してください。
this->m_Test.m_Max = 5; // 最大数設定
return TRUE; // TRUE を返すとコントロールに設定したフォーカスは失われませ
ん。
}
void CSelLogFile::OnButton1Commonfile()
{
// このなかでは、数や重複のことは気にしないで、AddStringで追加していく。
}
>なぜかというと、これでは、新しくダイアログを作り、その中のコンボボックス
(当然ドロップされたコンボボックスとは別のもの)に追加することになってしまいま
す。
え?そんなことはないと思うんですが・・・
CSelLogFile dlg;
dlg.m_DropCmb1_Ctrl.AddString
こうすると、CSelLogFileのメンバ変数を参照できるはずですけど。
dlgはCSelLogFile クラスのthisポインタと同じ働きになると認識しています。
実際、このやり方で他クラスのメンバ変数に値を代入したりリードしたりしたことがあ
るのですが!?