2010年6月19日土曜日

ListViewのヘッダ幅を固定してみる(C++/CLI & win32)

なんかやっぱり流行りというかAndroid関連での検索が引っかかってくる今日この頃。
そんな中 windowsformアプリ関連(C++/CLI)のことをちょこっと。しかも今更な内容。
正直Windowsアプリはwin32APIでゴリゴリに書く人で、.Netってな~に?って状態ですので、間違ってても責任取りません。
ってかむしろこういう方法あるよ!、みたいな素敵な指摘が欲しいです。

内容としては ListView header幅固定 を C++/CLIにwin32APIを混ぜて実現したもの
ちなみにヘッダの境界線にマウスが来た際にマウスカーソルが変更されるのも阻止。
まとめると

  • ListViewのヘッダ幅がマウスで弄れないこと
  • ヘッダの境界線上でマウスカーソルが変わらないこと
なお、会社で確認した動作確認はXP SP3
開発はVS2008 standerd sp1
家での確認は 7 pro のVC++ 2008 Express Edition

スゲー簡単な方法
// stdafx.hに追加
#include "windows.h"
#include "commctrl.h"

// Form1.hの適当なところに追加
// 略
EnableWindow( ListView_GetHeader( reinterpret_cast(this->listView1->Handle.ToInt32()) ), FALSE);
// 略

ぶっちゃけ、 Google先生に「ListView ヘッダ固定」で聞いてTOPに返ってくる内容ですが…。
上記内容をフォームのコンストラクタの中にでも突っ込んでおけばOK。
その辺から拾ってきたのを足しただけですので、ありなのかどうかすら分かりませんが、一応目的は果たせます。

上記の注意点

windows form アプリ的にはあまりwin32APIを使用されたくなさそうです。
環境にもよりますが、多分上記はまずリンカエラーとなるでしょう。
これもググれば答えが返ってきますが、一応方法を。

vs2008 standerd sp1 の場合は、うろ覚えですが、
・プロジェクト → プロパティ → C/C++ → 全般 → 追加のインクルードなんたら → 親からなんたら にチェック
で通るようになるかと。
何でも2005位からformアプリ作成時の初期設定では、win32用のライブラリが軒並み外されているとかで、自分で設定が必要らしいです。
もちろん個別に必要なライブラリをリンクしてもいいんですけど。

VC++ 2008 Express Edition
上記と同じ方法で行けると思いきや、駄目でした。
そこで取った方法は次の方法。

  • Win32コンソールアプリを作成
  • プロジェクト → プロパティ → リンカ → コマンドライン からlibファイル名をコピー
  • Formアプリの同じ場所にコピーした内容を貼付け
超強引ですがこれで行けますwww

ちなみにコピーした内容はこれ

kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

不要なものも居るけどその辺は無視。

ちょっと面倒な方法

お作法的にいいのかどうかは全く分かりませんが、上記にたどり着いて気がついたのは
「余裕でwin32APIガンガン使えるじゃん!」ってことです。

ウィンドウハンドル取得できれば結構やりたい放題ですよね。
さて、上記のようにHeaderを無効化することで目的を果たすと、楽は楽なのですがこんな要望が来たらアウト。
・ヘッダ部分のクリックはしたい!
更に、私が確認した環境では上記方法ではヘッダ部分をクリックすると、何故か一行目のアイテムがクリックされたと認識することも発覚。
というわけで、ListViewもヘッダ部分もサブクラス化してしまへ。

//****************
// Form1.hとか
//****************

HWND hList = reinterpret_cast(this->listView1->Handle.ToInt32());
HWND hHeader = ListView_GetHeader( hList);

OrgList = (WNDPROC)GetWindowLongPtr( hList, GWLP_WNDPROC);
SetWindowLongPtr( hList, GWLP_WNDPROC, (LONG_PTR)ListViewSubWindowProc);

OrgListHeader = (WNDPROC)GetWindowLongPtr( hHeader, GWLP_WNDPROC);
SetWindowLongPtr( hHeader, GWLP_WNDPROC, (LONG_PTR)ListViewHeaderSubWindowProc);


//****************
// hoge.cpp/h 色々略
//****************

WNDPROC OrgList, OrgListHeader;


LRESULT CALLBACK    ListViewHeaderSubWindowProc( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
 switch(msg){
 case WM_SETCURSOR:
  return TRUE;
 }
 return (CallWindowProc(OrgListHeader, hWnd, msg, wp, lp));
}

LRESULT CALLBACK    ListViewSubWindowProc( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
 NMHEADER* pnmhdr;
 switch(msg){
 case WM_NOTIFY:
  pnmhdr = (NMHEADER*)lp;

  switch(pnmhdr->hdr.code){
  case HDN_ENDTRACK:
  case HDN_BEGINTRACK:
  case HDN_TRACK:
  case HDN_DIVIDERDBLCLICKW:
   return TRUE;
  }
  break;
 }
 return (CallWindowProc(OrgList, hWnd, msg, wp, lp));
}

上記のソースもその辺探せば幾らでも出てきます。

  • Header部分でカーソルを変更させない
  • カラム幅をいじらせない
それぞれを自前で実装したというか、本来の実装へ渡さないだけですが。

ちなみにヘッダをクリックできるとか出来ないとかは、ListView::HeaderStyleで変更出来ますので、 まあ、わざわざwin32APIを使用するまででもないでしょう。

なお、恐らくこの段階ではまたもエラーになると思います。
コンパイル時に「 /clr:pure または /clr:safe と共にコンパイルされた関数に対する呼び出し規約 '__stdcall ' が無効です」というエラーがでると思います。
その指摘に従って
・プロジェクト → プロパティ → 共通言語ランタイム サポート
を /clr:pure から /clr へ変更することで、コンパイルが通るようになります。

また、Vistaから「HDS_NOSIZING」なるスタイルが追加されたようなのですが、 もしかするとこれらの実装(サブクラス化)をしなくても、このスタイル設定一発で終わる可能性も大ですね。
ま、めんどくて確認していませんし、そもそも私の場合XPで動作することが前提ですから。
誰か結果を教えてください。

ってか、こんな実装ありなんだろうか…
ぶっちゃけ何処がC++/CLIなんだか、普通にwin32じゃん…
C++/CLI 使いの方教えてください。
むしろ一番目に書くべきな気もしますが、貼り付けたコンソールのウィンドウハンドルって結構簡単に取れるよ!ってことが言いたいだけです。

0 件のコメント:

コメントを投稿