
作成中のアプリもだいぶ必要機能を網羅出来て来て
よーし、もうちょっとだ!ってとこに来て
コンボボックスのドロップダウンが「ほとんど表示されてない」事に気付きました。
なんでやのんということで調べてみると
どうやらCreateWindowExとかリソースとかで指定する「高さ」が
コンボボックスでCBS_DROPDOWNLISTスタイル込みの場合
ドロップダウンを表示させたときのドロップダウンリストの高さを含むように
予約領域先に作っておかなければいけない、とのこと
えー、クリックすると表示になる割に 「内部的に別々で自動的に高さ計算」 とかになってるんじゃないのー?
ということで
またしてもMicrosoftちゃんにしてやられたって気分ですが
しょうがないのでちゃっちゃとクラス化してみました。
(ていうか、なんで他のコントロールはほとんど独自クラスにしてあるのに、コンボボックスはクラス化してなかったんだろうと、小一時間(ry))
なお、現状ではMFCは使えません(w | orz)
あ、たぶん
TcsLiteral → const TCHAR* const
szt → size_t
以外のtypedefやdefineは見れば元が何なのか分かる(か、分かんなくても問題ないはず)
と思うので省略しまふ(お)
ヘッダ
#pragma once
class myComboBox {
HWND wnd;
szt num;
public:
myComboBox( HWND hw, int x, int y, int cx );
~myComboBox();
BOOL Enable( BOOL b ) const { return EnableWindow( wnd, b ); }
Void Select( Dword i ) const { SendMessage( wnd, CB_SETCURSEL, (WPARAM)i, 0 ); }
Int GetIndex() const { return (Int)SendMessage( wnd, CB_GETCURSEL, 0, 0 ); }
Void Setup() const;
Void AddString( TcsLiteral s ){
SendMessage( wnd, CB_ADDSTRING , 0 , (LPARAM)s );
++num;
}
Void AddStrings( TcsLiteral* const s, const szt num_ ){
for ( szt i = 0; i < num_; ++i ) SendMessage( wnd, CB_ADDSTRING , 0 , (LPARAM)s[i] );
num += num_;
}
};
ソース
#include "StdAfx.h"
#include "myComboBox.h"
#include "FontLibrary.h"
myComboBox::myComboBox( HWND hw, int x, int y, int cx ) : num(0) {
wnd = CreateWindowEx(0, _T("COMBOBOX"),null ,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST ,
x, y , cx, 0, hw, null, HINST::me, null);
}
myComboBox::~myComboBox(){
DestroyWindow( wnd );
}
Void myComboBox::Setup() const {
RECT rc;
GetClientRect( wnd, &rc );
int cy = (int)SendMessage( wnd, CB_GETITEMHEIGHT, -1, 0 );
cy += (int)SendMessage( wnd, CB_GETITEMHEIGHT, 0, 0 ) * num;
cy += GetSystemMetrics(SM_CYEDGE) * 2;
cy += GetSystemMetrics(SM_CYEDGE) * 2;
SetWindowPos( wnd, null, 0, 0, rc.right, cy, SWP_NOMOVE|SWP_NOZORDER );
SendMessage( wnd, CB_SETCURSEL, 0 , 0 );
SendMessage( wnd, WM_SETFONT, (WPARAM)FontLibrary::Default, True );
}
使い方の例はこんな感じです。
wnd = CreateWindowEx( ~
・
・
・
combo_measden = new myComboBox( wnd, 50, 200, 60 );
TcsLiteral c[] = { _T("1"), _T("2"), _T("4"), _T("8"), _T("16"), _T("32") };
combo_measden->AddStrings( c, sizeof c / sizeof(c[0]) );
combo_measden->Setup();
解放時
/* Windows的には「HWNDの、子ウインドウ」のみについて言えば親ウインドウのDestroyのときに同時に自動解放してくれるようですが、どっちみちcombo_measdenポインタはdeleteかけないといけないので */
delete combo_measden;
DestryWindow( wnd );
で、今回何が知りたいのかというと
このSetup関数の中の
int cy = (int)SendMessage( wnd, CB_GETITEMHEIGHT, -1, 0 );
cy += (int)SendMessage( wnd, CB_GETITEMHEIGHT, 0, 0 ) * num;
cy += GetSystemMetrics(SM_CYEDGE) * 2;
cy += GetSystemMetrics(SM_CYEDGE) * 2;
SetWindowPos( wnd, null, 0, 0, rc.right, cy, SWP_NOMOVE|SWP_NOZORDER );
この調整部分です。
こんでXP以降のWindowsは全部OKですかね?
XPはクラシックでもXP用のVisualスタイルでもおkっぽいですが。
その他の突っ込みどころもあれば募集致します。
No.4ベストアンサー
- 回答日時:
> LOGFONTのlfHeightが「XP以降のどれについても」そのまんまコンボボックスの高さとして使えるかどうか
> っていうのは、MSDN確実な問題って言う表記はありましたでしょうか?
ないでしょう。
画面の解像度(ディスプレイの画素数だけでなく、DPI値を含む)にも影響されるでしょうし。
リソースエディタを使う場合も含めて、最初に(見た目など適当な数値で)高さを決めておけば、行が中途半端に表示されるとか空白の行がいくつも表示されるようなことはないようにWindowsが調整してくれるので、環境によって表示される行数が多少違っても問題ないのであれば、任せちゃったほうが楽だというだけです。
厳密にどの環境でも同じ行数を表示したいということなら、提示されたコードでいいんじゃないでしょうか。
いや~
細かく何度もありがとうございます♪
全く同じ見解のようで、安心しました。
ちょっと貸してもらって、試すことが出来ましたよ
結果、やった範囲の全てのテーマ(デザイン)などで、XPでもVistaでも7でも
下のコードで全部思い通りの行数表示になることが分かりました。
もちろん
もし仮にずれる環境があったとしてもWS_VSCROLLを指定しておけば
計算した高さ > 指定されるべきぴったりの高さ
であれば、表示数が多くなるだけか
限界まで表示されてれば
自動的に縮小されて表示される仕様により、問題なし。
逆に
計算した高さ < 指定されるべき高さ
であれば、意図してないとこでスクロールバーが表示されるか
表示される数が少なくなるけども
スクロールはできるのでドロップダウンリスト上での選択の可否ということであれば
問題なし。
(WS_VSCROLL指定しとくと最小以下でも1個は表示されるっぽい(?))
ということで
また
CB_GETITEMHEIGHT
は、やはりいかにも専用のメッセージっぽく思うので
このコードで行こうと思います。
(少しだけ変えたので、現時点での、調整後の最終コードを載せておきます)
////////myComboBox.h////////
#pragma once
class myComboBox {
HWND wnd;
szt num;
public:
myComboBox( HWND hw, int x, int y, int cx,
DWORD style_ = WS_CHILD|WS_VSCROLL|CBS_DROPDOWNLIST );
~myComboBox();
BOOL Enable( BOOL b ) const { return EnableWindow( wnd, b ); }
Void Select( Dword i ) const { SendMessage( wnd, CB_SETCURSEL, (WPARAM)i, 0 ); }
Int GetIndex() const { return (Int)SendMessage( wnd, CB_GETCURSEL, 0, 0 ); }
Void Setup( Dword index_to_select = 0, szt max_num_to_show = 0 ) const;
Void AddString( TcsLiteral s ){
SendMessage( wnd, CB_ADDSTRING , 0 , (LPARAM)s );
++num;
}
Void AddStrings( TcsLiteral* const s, const szt num_ ){
for ( szt i = 0; i < num_; ++i ) SendMessage( wnd, CB_ADDSTRING , 0 , (LPARAM)s[i] );
num += num_;
}
};
////////myComboBox.cpp////////
#include "StdAfx.h"
#include "myComboBox.h"
#include "FontLibrary.h"
myComboBox::myComboBox( HWND hw, int x, int y, int cx, DWORD style ) : num(0) {
wnd = CreateWindowEx( 0, _T("COMBOBOX"), null,
style, x, y , cx, 0, hw, null, HINST::me, null);
}
myComboBox::~myComboBox(){ DestroyWindow( wnd ); }
Void myComboBox::Setup( Dword i, const szt maxnum_ ) const {
SendMessage( wnd, WM_SETFONT, (WPARAM)FontLibrary::Default, False );
RECT rc;
GetWindowRect( wnd, &rc );
const szt n_ = ( maxnum_ && maxnum_ < num ) ? maxnum_: num;
int cy = (int)SendMessage( wnd, CB_GETITEMHEIGHT, -1, 0 );
cy += (int)SendMessage( wnd, CB_GETITEMHEIGHT, 0, 0 ) * n_;
cy += GetSystemMetrics(SM_CYEDGE) * 4;
SetWindowPos( wnd, null, 0, 0, rc.right-rc.left, cy, SWP_NOMOVE|SWP_NOZORDER|SWP_SHOWWINDOW );
Select( i );
}
(もしまたなんか変な個所が見つかったら直すと思いますが)
どうもありがとうございました♪
No.3
- 回答日時:
調整される方向は、縮小だけです。
勝手にサイズを大きくしたりはしません。10.5行分の高さを指定して作成すると、10行分の大きさで表示される。選択肢が5つしかなければ、5行分に縮小されるということです。
フォントの高さは、LOGFONT情報を取得すればわかります。
HFONT hFont = FontLibrary::Default;
LOGFONT lf;
ZeroMemory(&lf, sizeof(lf));
GetObject(hFont, sizeof(lf), &lf);
とすると、lf.lfHeightにフォントの高さが取得できます。
どうもどうも
いずれも書いた後で文章見直してみて
同様のことを思いました。
>調整される方向は、縮小だけです。勝手にサイズを大きくしたりはしません。
10.5行分の高さを指定して作成すると、10行分の大きさで表示される。選択肢が5つしかなければ、5行分に縮小されるということです。
はい、なので限界まで表示する分には質問文の方法で問題なかった→限界まで表示する分には余分に確保しておけばいいとなるはず
ただし、スクロールバーを表示させたいとなると話は変わってくる、というわけです。
>フォントの高さは、LOGFONT情報を取得すればわかります。
はい、そうなんですよね。
実際アプリケーションのユーザー設定をファイルに保存したり、取り出したりといったこともやってるので、その時LOGFONTの情報も取り扱っているので
使い方はOKですが
そのことと
LOGFONTのlfHeightが「XP以降のどれについても」そのまんまコンボボックスの高さとして使えるかどうか
っていうのは、MSDN確実な問題って言う表記はありましたでしょうか?
もし一つでも確かめてみないと分かんない環境があれば不確実ってことになりますし
もしコンボボックスの内容を後で変更したい場合って言うのが生じれば
結局Setup関数が必要になるのには変わりないってのがあるんですよね。
No.2
- 回答日時:
なんか、そもそも論になっちゃいそうですが、コンボボックスの高さは、スタイルにCBS_NOINTEGRALHEIGHTを付けなければ、
・選択肢が中途半端に表示されないように行の境界に調整される
・項目数が少ない場合は、すべての項目が表示可能な最小の高さに調整される
となってませんでしたか?
なので、例えば選択肢が10を超えたらスクロールするようにしたければ、最初からドロップダウン部分が10行+αになるようにコントロールを作成すればよかったと思いますけど。+αも適当に0.5行分とかで問題ないと。
この回答への補足
>最初からドロップダウン部分が10行+αになるようにコントロールを作成すればよかったと
その場合、リソースでなく、CreateWindowExでやるとしたら
最初から高さを計算して、ってことでしょうか?
そうなるとちょーっとムズイんですよね
FontLibrary::Defaultの内容は
GetStockObject(DEFAULT_GUI_FONT);
なんですが
SendMessage( wnd, WM_SETFONT, (WPARAM)FontLibrary::Default, False );
こいつがあると下の「直したバージョン」のコードの計算手順じゃないと単純化出来ないという問題があったんです。
(質問文の段階の時はたまたま 変更前>変更後だったためOKだったてことかな)
そこさえなんとかできれば簡易化できるかもしれませんが
そうなると余計「環境依存の恐れ」が増えてしまうと思ってしまいます。
>CBS_NOINTEGRALHEIGHT
はい、そうですね。つけてませんw
付けて試してみましたが
>・選択肢が中途半端に表示されないように行の境界に調整される
こっちに関しては変わったようだけども
>・項目数が少ない場合は、すべての項目が表示可能な最小の高さに調整される
これは、私の環境では少なくとも変わった感じはありません。
SendMessageでWM_SETFONTやるかどうかとも関係ないっぽいです。
やはり
少ない高さに設定して「なんにも手を加えてなかったら」「線しか表示されてなかった」ので質問文のとおりの状況、って事です。
環境で違うんでしょうかね?
まあ、下のコードで万事OKということなら何ら問題はないのですが。
No.1
- 回答日時:
コンポボックスのドロップダウン部分の高さは、ダイアログの配置なんかと一緒でデザインの一部だと思いますよ。
元のダイアログの大きさを超えてドロップダウンするとみっとない時もあるから、全項目が一度に表示できなくてもスクロールバー表示で対応したいときもあるし。項目数が固定の時は、全項目が一目でわかるように、多少はみ出してもOKの時もあるし。そもそも、全項目が画面に入りきらない場合もあるし。
あと、コンボボックスの横幅って、ウィンドウ領域とクライアント領域で同じでした?
個人的には、同じだったとしてもGetWindowRectを使うな。
ご回答ありがとうございます♪
>コンポボックスのドロップダウン部分の高さは、ダイアログの配置なんかと一緒でデザインの一部だと思いますよ。
なるほど。
Expressだとリソースエディタが使えないので.rcファイルに数値を打ち込むか
最終的にはほとんどはAPI呼び出しに書き変えるという手順で作っていたので
そういう意識がありませんでしたが
そっちからいってれば迷いがなかったかもしれませんね。
>元のダイアログの大きさを超えてドロップダウンするとみっとない時もあるから、全項目が一度に表示できなくてもスクロールバー表示で対応したいときもあるし。項目数が固定の時は、全項目が一目でわかるように、多少はみ出してもOKの時もあるし。そもそも、全項目が画面に入りきらない場合もあるし。
なるほど!
「え?コンボボックスでのドロップダウンリストでの縦のスクロールバーってどうやって表示できるんだろう」と思って色々いじってたら
ウインドウスタイルに
WS_VSCROLL
を加えつつ
myComboBox::myComboBox( HWND hw, int x, int y, int cx ) : num(0) {
wnd = CreateWindowEx(0, _T("COMBOBOX"),null ,
WS_CHILD | WS_VSCROLL | WS_VISIBLE | CBS_DROPDOWNLIST ,
x, y , cx, 0, hw, null, HINST::me, null);
}
高さを「全部は収まらない」ようにする
と出来るって事ですね?(一応確認)
※あるいはCBS_DISABLENOSCROLLが追加してあれば全部収まる高さがあっても無効状態で表示になりますが
一昨日までの段階ではWS_VSCROLLを付けてない状態でしかも高さが1つ分も表示出来ない指定だったので「線」しか出てなかったので「なんじゃこりゃ」な状態でしたが
>あと、コンボボックスの横幅って、ウィンドウ領域とクライアント領域で同じでした?
>個人的には、同じだったとしてもGetWindowRectを使うな。
それ私も最初思ったのですが
どこだったかな…この部分は、英語のページで書いてあったのをWindowsAPI用にそのまんまなおしたって言う感じでした。
で、私の環境では少なくともYESですね。
ただ、↓こうやっても同じになりますし、感覚的にはやっぱGetWindowRectの方が良いと思いますので
現時点ではこう直しておくことにします。
宣言は
Void Setup( Dword index_to_select = 0, szt max_num_to_show = 0 ) const;
定義は
Void myComboBox::Setup( Dword i, const szt maxnum_ ) const {
SendMessage( wnd, WM_SETFONT, (WPARAM)FontLibrary::Default, False );
RECT rc;
GetWindowRect( wnd, &rc );
const szt n_ = ( maxnum_ ) ? maxnum_: num;
int cy = (int)SendMessage( wnd, CB_GETITEMHEIGHT, -1, 0 );
cy += (int)SendMessage( wnd, CB_GETITEMHEIGHT, 0, 0 ) * n_;
cy += GetSystemMetrics(SM_CYEDGE) * 4;
SetWindowPos( wnd, null, 0, 0, rc.right-rc.left, cy, SWP_NOMOVE|SWP_NOZORDER );
Select( i );
}
combo_measden->Setup(2,4); //選択インデックスと最大表示数を指定できる。
こいでどうでしょう?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# 質問です 下記のコードを分かりやすく解説お願いします 初心者です #include ‹stdio.h 3 2022/05/26 22:03
- C言語・C++・C# C言語でif文が予想と違う動きをする件について7 4 2023/03/20 00:26
- C言語・C++・C# C pointer? or... 2 2022/03/29 00:47
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# Cのdoubleの浮動小数点表示について 3 2023/04/17 13:14
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
「指定されたキャストは有効で...
-
C言語での引数の省略方法
-
複数桁10進数の*桁目だけを抽出...
-
#define _CRT_SECURE_NO_WARNIN...
-
ラップ関数とはどんなものですか?
-
(int *)の意味
-
if と配列の組み合わせ
-
C++でRPGを作成する際のステー...
-
C言語についてです。
-
C言語での奇数の和
-
C 言語の Gauss Jordan 法について
-
商と剰余を同時に求める(C言語)
-
C言語で行列の積を計算できるよ...
-
未解決の外部シンボル _printf...
-
行列の列の絶対値の総和の最大...
-
memsetについて
-
【Visual Studio】プロジェクト...
-
C言語で分からないところがあり...
-
「{ } で囲むだけ」は正しい?
-
整数データの配列から同じ値の...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
「指定されたキャストは有効で...
-
C言語での引数の省略方法
-
#define _CRT_SECURE_NO_WARNIN...
-
複数桁10進数の*桁目だけを抽出...
-
ラップ関数とはどんなものですか?
-
C言語 エラーの原因がわからな...
-
(int *)の意味
-
【C++】関数ポインタの使い方
-
if と配列の組み合わせ
-
構造体の勉強中です 合計点の高...
-
windows-findstrの正規表現を使...
-
C言語で分からないところがあり...
-
int型の変数値をバイト列として...
-
PowerShellがうまくいかない
-
C言語での奇数の和
-
「{ } で囲むだけ」は正しい?
-
std::set<int> で、ある値が何...
-
実数の整数部,小数部の取得
-
エラー 添字が付けられた値が、...
-
int16_t の _t は何?
おすすめ情報