引き続きシリーズの外。今回は完全に外。@IT の ネイティブアプリとCLRアプリの違い スレッドより。
スレ主さん曰く、Becky! のリストビューは第一カラムが幅固定であると。.NET のプロパティにはそう設定できるプロパティがないと。まあそういうことらしいです。しかし考えてみれば生 API 使うのならもともとプロパティだのなんて存在しませんし、MFC の CListView クラスなどにもそんなのありません。あるのはただ WM_NOTIFY と いくつかのメッセージのみ。別にこの辺がアンマネージドというわけでは、まあありますが、幸いメモリのことは一切勘案することなく、unsafe にもしないで、Marshal クラスも使わず書ける部分です。
WndProc のオーバーライドだって普通に存在しているんだから使わなければ損損。と言う比較的現実主義な私としては早速実装してみるのでした。
///<summary> ///カラム幅の変更を制限できる ListView です。 ///</summary> public class ColumnWidthFixableListView : ListView { ///<summary> ///サイズの変更を禁止するカラムのインデックスの配列です。 ///指定したインデックスのカラム右側の仕切りがドラッグできなくなり、 ///またダブルクリックによる自動調整も不可能になります。 ///存在しないインデックスが含まれていても問題ありません。 ///</summary> public int[] FixedColumns { get { return this.fixedColumns; } set { this.fixedColumns = (value == null) ? new int[0] : value; } } private int[] fixedColumns = new int[0]; private struct NotifyMessageHeader { public IntPtr Window; public int Id; public int NotifyCode; public int ItemIndex; public MouseButton MouseButton; public IntPtr ItemInfo; // 単に警告を黙らせるためだけなので無くて良いコンストラクタ public NotifyMessageHeader(int dummy) { this.Window = this.ItemInfo = IntPtr.Zero; this.Id = this.NotifyCode = this.ItemIndex = 0; this.MouseButton = MouseButton.Left; } } private enum MouseButton { Left = 0, Right = 1, Middle = 2, } protected virtual bool ProcessNotifyMessage(ref Message m) { object lParam = m.GetLParam(typeof(NotifyMessageHeader)); NotifyMessageHeader header = (NotifyMessageHeader)lParam; const int BeginTrackW = -326; //HDN_BEGINTRACKW const int BeginTrackA = -306; //HDN_BEGINTRACKA const int DividerDoubleClickW = -325; //HDCN_DIVIDERDBLCLICKW const int DividerDoubleClickA = -305; //HDCN_DIVIDERDBLCLICKA switch (header.NotifyCode) { //A が来るはずなのに W が来たりなどあるようなのでいっそ全部 case BeginTrackW : case DividerDoubleClickW : case BeginTrackA : case DividerDoubleClickA : if (this.fixedColumns == null) break; if (Array.IndexOf(this.fixedColumns, header.ItemIndex) != -1) { m.Result = new IntPtr(1); return true; } break; } return false; } protected override void WndProc(ref Message m) { if (m.Msg == 0x4E) { //WM_NOTIFY if (ProcessNotifyMessage(ref m)) return; } base.WndProc(ref m); } }