新トップページへ | Tip

WindowsAPIを使ってウインドウなどを表示する際のメモ

LastUpdate : 06/07/02

 コントロールの使い方の、個人的なメモです。
しかも、サンプルのコードは、私が書いたソフトの内部で使用されているものからの、ただのコピペです。ですので、余計なコードも入ってますのでご注意くださいw
一度覚えたけど、どーだったかなぁ・・・、なんとなくしか覚えて無いなぁ〜っていうときに、見ると、役に立ちます。
内容については、正しいのかどうなのか、保証できません。
MSDN、もうちょっと便利にならんかなぁ・・・。

ページ内リンク(もくじだよん):
  1. リストビュー(コモンコントロール)の使い方
  2. エディットボックスの使い方
  3. コンボボックスの使い方
  4. スクロールバーの使い方
  5. ウインドウ+ダイアログがある場合(複数のウインドウ・ダイアログのメッセージ処理)
  6. リストボックスの使い方
  7. ファイルを開くダイアログ(コモンコントロール)
  8. RGB色の取得(コモンコントロール)
  9. ドラッグ&ドロップされたファイル名前の取得


●リストビューの使い方

※レポート形式の場合のみについて書かれています。

ヘッダにcommctrl.hを使い、comctl32.libとリンクさせる必要が有り。
また、コードの初めの方に
InitCommonControls();
を呼ぶ(コモンコントロールの初期化のため)。


☆ リストビューで項目をクリックしたら、その行をすべてを選択させたい場合

HWND hList=GetDlgItem(hwnd,IDC_LIST_LAYER);
DWORD style=ListView_GetExtendedListViewStyle(hList);
style=style | LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT;
ListView_SetExtendedListViewStyle(hList,style);
と、する。


☆ 選択されているアイテムを調べたい場合
int order=ListView_GetNextItem(hList,-1,LVNI_SELECTED);
とやると、選択されているアイテムの順番(上から数えて何番目にあるか)が帰ってくる。
第二引き数に3とか5とか数字をやると、その番号の順番から探索を始める。
また、LVNI_SELECTED以外にも、flagとして指定できる値がある。
MSDNで、「LVM_GETNEXTITEM Message」を参照のこと(キーワード検索すればでてきます)。
エラーだと-1の値が返ってくる。


☆ COLUMNにデータの設定の仕方
LV_COLUMN Col;
memset(&Col,0,sizeof(LV_COLUMN));
Col.mask=LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
Col.fmt=LVCFMT_LEFT;
Col.cx=50;
Col.iSubItem=0;
Col.pszText="表示";
ListView_InsertColumn(hList,0,&Col);

Col.cx=100;
Col.iSubItem=1;
Col.pszText="レイヤーNo.";
ListView_InsertColumn(hList,1,&Col);
とすればよい。



☆ アイテムを追加したい場合
LV_ITEM Itm;
memset(&Itm,0,sizeof(LV_ITEM));
Itm.mask=LVIF_TEXT;
Itm.iItem=0;            //何番目の行か
Itm.iSubItem=0;         //何番目の列か
Itm.pszText="1";
ListView_InsertItem(hList,&Itm);

Itm.pszText="サンプル";
Itm.iSubItem=1;
ListView_SetItem(hList,&Itm);



☆ アイテムの削除
 ListView_DeleteAllItems(HWND hList)
ですべてのアイテムを削除。
 ListView_DeleteColumn(HWND hList,int order)
でorder番目のCOLUMNを削除
 ListView_DeleteItem(HWND hList,int order)
でorder番目のアイテムを削除。


☆ コールバックでNOTIFYを使うと、リストビューのおのおののイベントに対し、検出
LVN_ITEMCHANGED以外にもいくつか、ある。
LVN_ITEMCHANGEDは、何かしらの変更があれば、常に呼ばれます。

//-------------------------------------------------------------
        case WM_NOTIFY:
                {
                        LayerSelectorDialogCallBack_WM_NOTIFY(lp);
                        break;
                }
//-------------------------------------------------------------
static int LayerSelectorDialogCallBack_WM_NOTIFY(LPARAM lp)
{
        HWND hList=GetDlgItem(g_hDlgLayer,IDC_LIST_LAYER);
        LPNMHDR hdr=(LPNMHDR)lp;

        //リストビュー用
        if( hdr->hwndFrom == hList )
        {
                if( hdr->code == LVN_ITEMCHANGED )
                {
                        //リストビューに変更があった場合の処理・・・
                }
        }
        return 0;
}




☆ リストビューの中のアイテムの数を取得
ListView_GetItemCount(hList)

☆ チェックボックス付きのリストビューで、チェックボックスがチェックされているか調べる。
ListView_GetCheckState(hListLayer,c)
で、戻ってきた値がゼロ以外ならば、チェックされています。ゼロならば、チェックされていません。

☆ チェックボックス付きリストビューで、チェックボックスをチェックする。
ListView_SetCheckState(hList,0,TRUE);   //チェックされた状態にする。
で、第二引き数が場所。


☆ リストビューのアイテムを選択し、フォーカスをセット。
ListView_SetItemState(hList,0,LVIS_SELECTED | LVIS_FOCUSED,LVIS_SELECTED | LVIS_FOCUSED);//一番最初の項目を選択。
第二引き数が場所。
なんだか理屈はよくわからないが、こうすればなるw





●エディットボックスの使い方

☆ 文字列の取得、設定
SetWindowText(HWND hwnd,LPSTR str)
GetWindowText(HWND hwnd,LPSTR str,int str_len);
で、基本的に、値のセット・取得は可能。取得する際、長さがあらかじめ知りたい場合は
GetWindowTextLength(HWND hwnd)
を使う。


☆ 数字だけしか入力できないようにする

SetWindowLong( hEdit , GWL_STYLE , ES_NUMBER | GetWindowLong(hEdit,GWL_STYLE) )
でよかったはず。


☆ 読み取り専用にする
SendMEssage(hEdit,ES_READONLY,0,0);
でよかった気がしないようでもない。


☆ WM_NOTIFY
switch( msg )
{
        //...なんかのコード(switch( msg )以下
        
        case WM_COMMAND:
        {
                switch( LOWORD(wp) )
                {
                        case IDR_EDIT1: //エディットボックスのID
                        {
                                if( HIWORD(wp) == EN_UPDATE )
                                {
                                        //エディットボックス(識別IDがIDR_EDIT1)に何か変更があった
                                        //...
                                }
                                break;
                        }
                }
                
                break;
        }
        
        //...なんかのコード
}
とかやるとよい。EN_UPDATE以外にも色々ある。
また、EDITコントロールは、保持するテキストを無限には保証していない。
大量のテキストは処理できない・・・らしいw(そういう場面にまだ出くわしたことが無い)。





●コンボボックスの使い方

☆ 現在の選択位置を取得

int cursel=SendMessage(hCombo,CB_GETCURSEL,0,0);
で、エラーだとCB_ERRが帰ってきます。


☆ 内容をすべて消去
SendMessage(hCombo,CB_RESETCONTENT,0,0)
とする。


☆ アイテムを追加
SendMessage(hPalleteDialogImageCombo,CB_INSERTSTRING,(WPARAM)i,(LPARAM)str);
もしくは、
SendMessage(hPalleteDialogImageCombo,CB_ADDSTRING,(WPARAM)0,(LPARAM)str);

iは、挿入する位置、strは文字列。
ADDSTRINGは、そのまま、データが積まれて追加される。位置を指定したい場合は、INSERTSTRING
を使う。

☆ コンボボックスの内容が変更されたときを知る方法
コールバック関数の中で
//-------------------------------------------------------------
case WM_COMMAND:
{
        switch( LOWORD(wp) )
        {
                //コンボボックスへのメッセージ
        case IDC_COMBO_IMAGE:
                {
                        if( HIWORD(wp) == CBN_SELCHANGE )
                        {
                                //選択項目に変更があった場合の処理
                                
                        }
                        break;
                }
        }
        break;
}
とする。





●スクロールバーの使い方

☆ スクロールバーの制御
コールバック関数の中で
int CALLBACK PalleteImageDialogCallBack(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
        switch( msg )
        {
                //-------------------------------------------------------------
        case WM_VSCROLL:
                {
                        SCROLLINFO Si;
                        memset(&Si,0,sizeof(PSCROLLBARINFO));
                        Si.cbSize=sizeof(SCROLLINFO);
                        Si.fMask=SIF_POS | SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
                        GetScrollInfo(g_hDlgPalleteImage,SB_VERT,&Si);
                        int page_size=Si.nPage; //1ページ分のサイズを取得。
                        int pos=0;
        

                        //スクロールバーの位置を設定。
                        switch( LOWORD(wp) )
                        {
                        case SB_LINEUP:
                                {
                                        pos-=1;
                                        break;
                                }

                        case SB_LINEDOWN:
                                {
                                        pos+=1;
                                        break;
                                }

                        case SB_PAGEUP:
                                {
                                        pos-=page_size;
                                        break;
                                }
                
                        case SB_PAGEDOWN:
                                {
                                        pos+=page_size;
                                        break;
                                }

                        case SB_THUMBTRACK:
                                {
                                        pos=HIWORD(wp) - Si.nPos;
                                        break;
                                }

                        default:
                                {
                                        //処理を中止する。
                                        return 0;
                                }
                        }


                        Si.nPos+=pos;
                        if( Si.nPos <= 0 )Si.nPos=0;
                        if( Si.nPos >= Si.nMax )Si.nPos=Si.nMax;
                        SetScrollInfo(g_hDlgPalleteImage,SB_VERT,&Si,TRUE);
        
                        break;
                }

                //-------------------------------------------------------------
        case WM_HSCROLL:
                {
                        //WM_VSCROLLの内容と同じ。ただしSetScrollInfoは
                        SetScrollInfo(g_hDlgPalleteImage,SB_VERT,&Si,TRUE);
                        //とすること。
                        break;
                }
                //以下省略
として、スクロールバーに対するイベントを検出。
コントロールとして作成し、たくさんスクロールバーがある場合
if( (HWND)lp == hScrollBar1 ){  //目的のスクロールバーだった場合の処理    }
とLPARAMの値がHWNDだそうだ。これで識別できるかも。
やったこと無いのでどうなるかわかりませんが^^;




☆ 縦横のスクロールバーの状態の取得
SCROLLINFO InfoH,InfoV;
memset(&InfoV,0,sizeof(SCROLLINFO));
memset(&InfoH,0,sizeof(SCROLLINFO));
InfoV.cbSize=sizeof(SCROLLINFO);
InfoV.fMask=SIF_ALL | SIF_DISABLENOSCROLL;
InfoH=InfoV;
GetScrollInfo(g_hWndEditor,SB_HORZ,&InfoH);
GetScrollInfo(g_hWndEditor,SB_VERT,&InfoV);

ただし、ウインドウに付属するようにして作られたスクロールバーの場合のみ。
コントロールとして作られたのならば、fMaskには、SB_CTLも追加すること。
SIF_DISABLENOSCROLLをなくせば、スクロールバーが不要な状態になると、自動的に消えます。




☆ スクロールバーの状態の設定(サンプルとして水平方向のスクロールバー
SCROLLINFO Info;
Info.cbSize=sizeof(SCROLLINFO);
Info.fMask=SIF_POS | SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
Info.nMin=0;
Info.nMax=width;
Info.nPage=Rc.right;
Info.nPos=0;                                    //現在表示している位置
SetScrollInfo(g_hDlgPalleteImage,SB_HORZ,&Info,TRUE);

このInfo.nMaxとInfo.nPageの関係であるが、たとえば、イメージならば、nMaxに、表示したい
イメージの横幅、そして、nPageに、現在の表示エリアの横幅を設定しておけばよい。
GetScrollInfoで取得したnPosには、0〜(nMax - nPage-1)の値がはいる・・・だった気がした。
詳細はヘルプでしらべてくだちぃ(ぉ





●ウインドウ+ダイアログがある場合(複数のウインドウ・ダイアログのメッセージ処理)

普通に、CreateDialogすればよい。手を加えなければならないのは、メッセージループのところである。
☆ メッセージループ
        //メッセージループへ
        MSG msg;
        while( GetMessage(&msg, NULL, 0, 0) ) 
        {
                if( IsDialogMessage(g_hDlgPallete,&msg) == 0 && IsDialogMessage(g_hDlgLayer,&msg) == 0 )
                {
                        TranslateMessage( &msg );
                        DispatchMessage( &msg );
                }
        }
        return (int)msg.wParam;
と、IsDialogMessage関数で、自分のダイアログへのメッセージかを調べる(自分へのメッセージではなかったら、ゼロを返す)。
そして、自分のではなかったら、DispatchMessage関数とかを呼ぶ。
という処理の流れ。


☆ アクセラレーターを使う場合。
        HACCEL hAccel=LoadAccelerators(hInst,MAKEINTRESOURCE(IDR_ACCEL1);       //IDR_ACCEL1はresource.hで定義されてるとする
        //メッセージループへ
        MSG msg;
        while( GetMessage(&msg, NULL, 0, 0) ) 
        {
                if( IsDialogMessage(g_hDlgPallete,&msg) == 0 && TranslateAccelerator(g_hDlgPallete,hAccel,&msg) == 0 )
                {
                        TranslateMessage( &msg );
                        DispatchMessage( &msg );
                }
        }
        return (int)msg.wParam;
とするといいらしい。昔書いたコードなので、忘れた(ぉ





●リストボックスの使い方

☆ アイテムの挿入・追加
SendMessage(hList,LB_ADDSTRING,(WPARAM)0,(LPARAM)str)
SendMessage(hList,LB_INSERTSTRING,(WPARAM)order,(LPARAM)str)
とする。
orderは、挿入する位置、strは挿入する文字列。


☆ アイテムの削除
SendMessage(hList,LB_DELETESTRING,(WPARAM)order,(LAPARAM)0)
で、order番目(ゼロがら数えて)のアイテムを削除
SendMessage(hList,LB_RESETCONTENT,(WPARAM)0,(LAPARAM)0)
で、全部削除。


☆ アイテムの数を取得
int count=SendMessage(hList,LB_GETCOUNT,(WPARAM)0,(LAPARAM)0)
で取得する。count == LB_ERRだと、エラーです。


☆ 選択カーソル位置を取得
int count=SendMessage(hList,LB_GETCURSEL,(WPARAM)0,(LAPARAM)0)
で取得する。count == LB_ERRだと、エラーです。


☆ 選択カーソル位置を設定

SendMessage(hList,LB_SETCURSEL,(WPARAM)order,(LAPARAM)0)
order番目に、カーソルをセットします。


☆ アイテムの文字列を取得。
char str[256];
memset(str,0,256);
SendMessage(hList,LB_GETTEXT,(WPARAM)order,(LAPARAM)str)
とかします。文字列の長さが不安ならば、
int len=SendMessage(hList,LB_GETTEXTLEN,(WPARAM)order,(LAPARAM)str)
とし、文字列の長さを取得してから処理するものよいかもしれません。


☆ NOTIFY
コールバック関数の中で
//-------------------------------------------------------------
case WM_COMMAND:
{
        switch( LOWORD(wp) )
        {
                //エディットボックスへのメッセージ
        case IDC_EDIT1:
                {
                        if( HIWORD(wp) == LBN_SELCHANGE )
                        {
                                //選択項目に変更があった場合の処理
                                //..
                        }
                        break;
                }
        }
        break;
}





ファイルを開く(コモンコントロール)

ヘッダにcommctrl.hを使い、comctl32.libとリンクさせる必要が有る。
        char str[256];
        memset(str,0,256);
        OPENFILENAME Ofn;
        memset(&Ofn,0,sizeof(OPENFILENAME));
        Ofn.lStructSize = sizeof(OPENFILENAME);
        Ofn.Flags = OFN_FILEMUSTEXIST;
        Ofn.lpstrFilter = "TargaFile(*tga)\0*.tga\0\0";
        Ofn.nFilterIndex = 0;
        Ofn.hwndOwner = g_hDlgPallete;
        Ofn.lpstrFile = str;
        Ofn.nMaxFile = 256;
        if( GetOpenFileName(&Ofn) == 0 )return NULL;    //キャンセルされた。

コードの中ではstr[256]としているが、str[MAX_PATH]とかやるほうが良さそうな雰囲気である。
Ofn.lpstrFilterであるが、書き方はこういう感じ。
ファイルが選択されて「開く」ボタンが押されると戻り値はゼロ以外の値が帰ってくる。
そして、strにそのパス(フルパス)が入っている。

表示する名前(1)\0*.bmp\0表示する名前(2)\0*.jpg表示する名前(3)\0*.png\0\0

最後は\0\0で終わる。
 文字列+\0+検索用のコマンド\0
で一つのセット。増やしたい場合は\0以降、繋げる。





RGB色を取得。(コモンコントロール)

ヘッダにcommctrl.hを使い、comctl32.libとリンクさせる必要が有る。
コード内のcust_colorは、RGBの値を示す部分以外はゼロで埋める必要がある。

        HWND hEditColor=GetDlgItem(hDlg,IDC_EDIT_SETTING_LINE_COLOR);
        COLORREF cust_color[16];
        memset(cust_color,0,sizeof(COLORREF) * 16);

        CHOOSECOLOR Cc;
        memset(&Cc,0,sizeof(CHOOSECOLOR));
        Cc.lStructSize=sizeof(CHOOSECOLOR);
        Cc.Flags=CC_FULLOPEN;
        Cc.hwndOwner=g_hWndEditor;
        Cc.rgbResult=0;
        Cc.lpCustColors=cust_color;
        if( ChooseColor(&Cc) == 0 ) return -1;  //キャンセルボタンが押された。

        //エディットボックスに色の値をセット。
        char str[256];
        
        memset(str,0,256);      
        sprintf(str,"R: %d  G: %d  B: %d",GetRValue(Cc.rgbResult),GetGValue(Cc.rgbResult),GetBValue(Cc.rgbResult));
        SetWindowText(hEditColor,str);
とかやるとよい。





ドラッグ&ドロップされたファイル名前の取得

ダイアログなら、ドラッグ&ドロップを許可するように設定すること。
ウインドウならば、DragAcceptFiles関数なんかでドラッグ&ドロップを許可させること(DragAcceptFiles(hwnd,TRUE)とかやる)。

コールバック関数内でファイルがドロップされるとWM_DROPFILESで知らせてくれるので、こーする。

int CALLBACK FilerDialogCallback(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
        switch( msg )
        {
                //-------------------------------------------------------------
        case WM_DROPFILES:
                {
                        FilerDialogDroppedFile(hwnd,wp);
                        break;
                }
・・・・・・(以下うだうだ。

で、このメッセージが来たときの処理方法は以下のとおり。
DragQueryFileの第二引数に-1を指定すると、ドロップしたファイルの数を取得できる。
この第二引数は、ドロップされたファイルの何番目のパスを取得したいのか、を指定するのに使う。
処理の最後にはDragFinishで内部で確保したらしいメモリを解放させる。

int FilerDialogDroppedFile(HWND hwnd,WPARAM wp)
{
        //ドロップされたファイルの数を取得。
        unsigned int count = DragQueryFile((HDROP)wp,-1,0,0);
        
        //ファイルのパスを得る。
        unsigned int len;
        char path[ MAX_PATH ];
        unsigned int i;
        for( i=0;;i++ )
        {
                if( i >= count )break;
                memset(path,0,MAX_PATH);
                len = DragQueryFile((HDROP)wp,i,path,MAX_PATH-1);
                path[ len ] = '\0';
                //converterクラスにファイルパスを追加。
                if( FilerDialogDroppedFile_add_path(hwnd,path) )
                {
                        DragFinish((HDROP)wp);
                        return -1;
                }
        }
        DragFinish((HDROP)wp);
        return 0;
}