2014年4月18日金曜日

wpf : ListViewのある列の幅を自動でいっぱいに広げその他の列幅は固定

ちょっと作りたいものがあって、その部品としてある列の幅を自動でいっぱいに広げ、その他の列幅は固定するListViewを作りました。 その作り方をメモ。 サンプルコードを実行するとこんなウィンドウが表示されます。

ユーザーがウィンドウサイズを変更すると「メモ」の列は自動で調整されます。 その他の列は固定幅です。 列の入れ替えも禁止にしました。


作った環境はこちら。

  • Windows7 64bit home
  • Visual Studio 2012 Express for Desktop
  • .NET Framework 4.5

参考サイトはこちらです。


まずはサンプルコードのガワとなる部分を載せましょう。 これを書き換えて目的のコードを作ります。 MainWindow.xamlはこれ。

MainWindow.xaml.csはこれです。

Item.csはこれです。


まずは列幅の固定と入れ替え禁止の簡単なやり方から。 GridViewColumnHeaderを明示的に書いて無効します。 最初に挙げたコードでは列名をGridViewColumn要素のHeader属性で指定していました。 これをこういう風に書き換えます。

GridViewColumnHeader要素にForeground属性を指定しているのは、無効にすると文字の色がグレーになってしまうため。 それは文字の色を黒で上書きして対処しています。 Widthを省略すると列幅を自動調整するところで誤動作します。 明記しておいてください。

列ヘッダにメニューやクリックイベントなどを何も実装しない場合はこれでokです。 ですがメニューなどが必要な場合は無効にすると無反応になってしまいます。 別のやり方をする必要があります。


そういう場合は、このやり方にしましょう。 GridViewColumnHeaderを明示的に書くところは同じ。 ただし無効にはしません。 通常はマウス操作で列のサイズ変更と列の入れ替えができてしまいます。 ここでは列の入れ替えを防ぐためにMouseDownイベントのハンドラを付けます。

MouseDownイベントのハンドラの処理は、

これだけです。 これで列の入れ替え処理にイベントが到達する事はありません。

列のサイズ変更も止めなければなりません。 GridViewColumnHeaderは子にThumbを持っています。 それを無効にしましょう。 参考サイトはxamlで書いて長くなっています。 ListView全体を書き換えるときはそちらのやり方の方がよさそうですが、大変です。 この例ではその処理はListViewのLoadedイベントでやります。

GridViewColumnHeaderを明記しないとheaderの中身は文字列になったり別のコントロールになったりしてうまくいかないので注意。 GridViewColumnHeaderの子コントロールの階層はMSDNのページで「Default WPF Themes」を落としてきて確認。 デフォルトのテンプレートはClassic.xamlに書いてあります。

これで列幅の固定は完了。


最後にListViewのサイズが変更されたとき、それにあわせて「メモ」の列を調整する処理を書きます。 ListViewにSizeChangedイベントのハンドラを追加。

参考サイトにはいくつかやり方が書いてあるけど、やりたいこととかける時間の兼ね合いでこれを選びました。 引き算で幅を決めるだけです。 参考サイトで幅が取得できないとか書いてるのはGridViewColumnHeaderを明記してないからですね。 この例の通りに書けば大丈夫です。

ただListViewの幅から各列の幅を引いただけだとちょっと「メモ」の列幅が大きくなってしまうので余分にLIST_VIEW_COLUMN_MARGINを引いてます。 これはListViewの枠幅(BorderThickness)とか、行選択の青い四角形を描くための余白とかのようですね。 手作業で適当な値を探しました。

ListViewのBorderThicknessを変えると当然LIST_VIEW_COLUMN_MARGINの値も変わります。 たいした手間では無いのでそのつど探しましょう。 ちょうどいい値だと横のスクロールバーが表示されません。

wpfはディスプレイの設定とかで勝手に拡大縮小されるんですよね。 そういうのとか、別のなんかのはずみとかでLIST_VIEW_COLUMN_MARGINがちょうどいい値でも横のスクロールバーが出てしまうことがあるかもしれません。 そういうのが嫌ならListViewのScrollViewer.HorizontalScrollBarVisibilityプロパティをHiddenにして、横のスクロールバーが表示されないようにしておきます。

これで列の自動調整処理もできました。


最後に、サンプルコード全体も載せておきましょう。 一応、列ヘッダのクリックイベントとコンテキストメニューが動くのを確認するためのコード数行が追加されています。 まずはMainWindow.xamlから。

MainWindow.xaml.csです。


2014/04/21追記

ListViewの右側に出るスクロールバーの幅の調べ方を書くの忘れてました。 こんな感じです。

スクロールバーの表示の有無で調節する列の幅を変えたり、スクロールバーを表示しっぱなしにして常にその分の幅を引いたりしましょう。