FC2ブログ

VS2017-はじめの1/10歩(14):ボタンに画像を貼付ける

   プログラミング [2019/09/07]
VS2017を使うんだけど慣れてないからVS6でまずは作ってました。
Win32アプリとして実装した内容の備忘録です。

これまでも含めた内容は、

→ナレーション再生と音声認識を使う
  →そのため音声データの作り方(多言語対応)
  →ナレーション(.WAV)再生のサンプルコード
  →音声認識のサンプルコード
→VS6で作ったC/C++コードをVS2017に持って行って.cppレベルで共通にする方法
→ダイアログベースのプログラム
  →背景に画像(.BMP)を使う
  →ラベルを透かす
  →ボタンの色を変える
  →Windowsタブレット上でピンチイン/アウト:拡大縮小
  →タブレット画面の回転への対応
  →カスタムなチェックボックス作成
  →ボタンに画像を貼付ける←今回

となってます。

今回、要はアイコンをボタンにする話です。
至ってありふれた内容で、申し訳ないです。
さるの個人的備忘録が主目的なので、勘弁してやってください。m(__)m


ベースは、ボタンコントロールを使います。
まずは、ボタンを作成する際のスタイルに「オーナー描画」=BS_OWNERDRAW を指定します。
リソースエディタ上では、見た目フツーのキャプション入りのボタンコントロールです。

◆リソースの準備
まずは、アイコンを用意します。
VS2017のリソースエディタは、使えるんだか使えないだか・・・。
できなくて、追加で何かをインストールしたような気がします。(曖昧ですいません。)
現状のさるの環境では、リソースビューから追加/編集できるようになってました。
外部エディタ(ペイント)を呼び出すこともできました。

一方、VS6だとビットマップの編集が16色までしかできなくて、綺麗なアイコンを作るのは至難の業。
なので、外部のエディタ(GIMP)を使って256色のビットマップを作りました。

見た目四角じゃないボタンを作ろうとすると、最低2つ。
何かの機能のON/OFF状態を示すには、さらに1つのビットマップが必要です。

例えば、或る機能が有効(ON)なときと無効(OFF)のときの2種類の表示(ビットマップ)があって、
さらにそのときのアイコン(画像)を四角以外に見せるための・・・なんていうのかなー
謂わば「型」のビットマップを準備します。下右側のような感じ。
20190907_2.jpg  20190907_3.jpg  20190907_1.jpg

まずは、「型」(上の右側)で透ける(真白)の部分を決めて、ON/OFF用(左と真ん中)で「描く」部分(真黒以外)を指定してます。(たぶん)
それを、重ねて描くんです。

ビットマップを準備したら、リソースエディタ上でビットマップを定義します。
(詳細な操作方法は省略しちゃいます。すいません。)
.rc上だと以下のような行が追加されている状態になります。
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

IDB_BMPSNDOFF BITMAP MOVEABLE PURE "mic256off.bmp"
IDB_BMPSNDON BITMAP MOVEABLE PURE "mic256on.bmp"
IDB_BMPCIRCALPH BITMAP MOVEABLE PURE "circ_alph256.bmp"


※綺麗に見せるためには元のビットマップが結構重要なんだけど、
 初めての方は試行錯誤してみてください。

◆ウィンドウプロシジャ内の処理
//----------------------------------------------
case WM_INITDIALOG:
{


//アイコンボタンのプロシジャの変更
SwapBitmapButtonProc(hDlg, IDC_BTSOUND);

return TRUE;
}



//******* オーナードローボタン描画
case WM_DRAWITEM:
{
INT cid = LOWORD(wParam);
LPDRAWITEMSTRUCT podraw = (LPDRAWITEMSTRUCT) lParam;
switch (cid) {


//アイコンボタン
case IDC_BTSOUND: //g_fHearing
DrawBmpToButton(GetDlgItem(hDlg, IDC_BTSOUND), podraw->hDC, IDB_BMPSNDON, g_fHearing);
break;


}
return TRUE;
} //end WM_DRAWITEM



//****** ボタンクリック等
case WM_COMMAND:
switch (LOWORD(wParam)) {



//**** 音声入力ON/OFF
case IDC_BTSOUND:
if (!g_fHearing) {
g_fHearing = TRUE;


}
else {
g_fHearing = FALSE;


}
InvalidateRect(GetDlgItem(hDlg, LOWORD(wParam)), NULL, FALSE);
return TRUE;



} //end switch WM_COMMAND-lParam
break;
//----------------------------------------------



ざっくり説明すると、
ダイアログの初期化の時点で、アイコンボタンのウィンドウプロシジャを別のプロシジャに置き換えます。
置き換えられたプロシジャでは、WM_ERASEBKGND(背景描画)のときだけ「何もしない」処理に置き換えて、
その他のメッセージは、元の置換え前のプロシジャに処理させます。
コントロールの再描画が必要なときは、WM_DRAWITEMメッセージが来るので、そこで実際にビットマップを描画します。
「型」とON/OFF用のどっちか。
g_fHearingは、機能のON/OFFを示すグローバル変数です。(本来、クラス化するべきだとは思います。はい。)

描画等処理の中身は・・・


◆アイコンボタンのオーナードロー処理など
//----------------------------------------------
//name :SwapBitmapButtonProc
//function :ビットマップボタンのプロシジャの置換え
//parameter :hDlg -親ダイアログのハンドル
// iCid -コントロールID
//global :[u]g_DefaultButtonProc
//return :TRUE-成功(処理済み)、FALSE-失敗(未処理)

VOID SwapBitmapButtonProc(
HWND hDlg,
INT iCid)
{
HWND hBtn;

hBtn = GetDlgItem(hDlg, iCid);
if (!g_DefaultButtonProc) {
g_DefaultButtonProc = (WNDPROC)GetWindowLong(hBtn, GWL_WNDPROC);
}
SetWindowLong(hBtn, GWL_WNDPROC, (LONG)BitmapButtonProc);
return;
}

//----------------------------------------------
//name :BitmapButtonProc
//function :ビットマップボタンの背景塗りつぶし禁止用プロシジャ
// ※描画は従来通り、WM_DRAWITEメッセージの延長で行う。
//parameter :hDlg -ダイアログハンドル
// message -メッセージ番号
// wParam, lParam -メッセージパラメタ
//global :[r]g_DefaultButtonProc
//return :TRUE-成功(処理済み)、FALSE-失敗(未処理)

LRESULT CALLBACK BitmapButtonProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
switch(message) {
case WM_ERASEBKGND: // 背景描画を禁止する
return TRUE;
}
return CallWindowProc(g_DefaultButtonProc, hWnd, message, wParam, lParam );
}

//----------------------------------------------
//name :DrawBmpToButton
//function :ボタンに画像を描画する
//parameter :hCtrl -コントロールのウィンドウハンドル
// hDc -コントロールのデバイスコンテキスト
// iBmpId -ビットマップID
// fOnOff -使用する画像ファイルの区別
//global :[r]g_hInst
//return :なし

VOID DrawBmpToButton(
HWND hCtrl,
HDC hDc,
UINT uiBmpId,
BOOL fOnOff)
{
RECT rect;
HBITMAP hbmp;
HBITMAP hbmp_alpha;
BITMAP bm;
HDC hmdc;

GetWindowRect(hCtrl, &rect);
hbmp_alpha = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BMPCIRCALPH));
if (fOnOff)
hbmp = LoadBitmap(g_hInst, MAKEINTRESOURCE(uiBmpId));
else
hbmp = LoadBitmap(g_hInst, MAKEINTRESOURCE(uiBmpId+1));

GetObject(hbmp, sizeof(BITMAP) , &bm);

hmdc = CreateCompatibleDC(hDc);
SelectObject(hmdc, hbmp_alpha);
StretchBlt(hDc, 0, 0, rect.right-rect.left, rect.bottom-rect.top,
hmdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND);
SelectObject(hmdc, hbmp);
//BitBlt(hDc, 0, 0, rect.right-rect.left, rect.bottom-rect.top, hmdc, 0, 0, SRCCOPY);
//TransparentBlt(hDc, 0, 0, rect.right-rect.left, rect.bottom-rect.top,
// hmdc, 0, 0, bm.bmWidth, bm.bmHeight, RGB(0,0,0));
StretchBlt(hDc, 0, 0, rect.right-rect.left, rect.bottom-rect.top,
hmdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCPAINT); //SRCCOPY→SRCPAINT
DeleteDC(hmdc);
DeleteObject(hbmp);
DeleteObject(hbmp_alpha);

return;
}
//----------------------------------------------


BitBlt()は、貼付け先のボタンサイズが今回可変なので使えませんでした。
TransparentBlt()を使わなかったのは、何かうまくいかなかったのだと思います。詳細忘却。
StretchBlt()は、単純なドットの複製/間引きでビットマップを拡縮するので、余り綺麗な絵になりません。
綺麗にしたい場合は、アンチエリアス処理を自作してやるか、その機能を持ったライブラリをどこぞからもってくる必要があります。

その他の細かい処理内容の説明は省きます。

興味のある方は、温かい気持ちでよしなに解釈してやってください。
描画系の話であれば、「猫でもわかるプログラミング」とか参考にされると詳しく載ってます。
さるもほぼ、バイブルとして崇めてます。

次回・・・スワイプ・スクロールについてのサンプルになります。

では、この辺で。
m(__)m

スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-
本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



PVアクセスランキング にほんブログ村 にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR