DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ListViewで列順の変更を保持する方法

環境/言語:[WIN2000 or WINXP/VB.NET200]
分類:[.NET]

WIN2000 or WINXP
VB.NET2003
こうといいます
VB.NET2003で開発しています。

さて表題の方法ですが
ListViewを使用して一覧表を表示する画面を開発しているのですが
ユーザから
「実行時にマウスのドラック&ドロップで変更した列順を
 保持して次回のAP起動時に保持している列順で表示したい」
との要望があり
色々調査&検討しているのですが
実行時に変更した列順を抽出する方法が見つからなく
皆さまから情報をいただきたく書きこませていただきました
以下例を示します。

@初期表示
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
ユーザID | ユーザ名 | 年齢 | 所属 | 備考       |
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
と表示されているのを
実行時にマウスのドラックで

A列を移動
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
ユーザID|ユーザ名| 備考       | 所属 | 年齢    |
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
のように年齢を後ろにもっていき、備考を前に持ってきたとします。
この状態でAPを終了させ再度起動すると
Aの状態で表示されるようにしたいのです。
ちなみに列幅の変更はコードで取得することができたので幅情報は保持することが
できました。

※お客様がこの機能を望む理由
 実は、列は10カラムほどあり横スクロールしないと全列を見ることはできません
 そこでお客様が、好きなように重要な列を左に持ってきてスクロールしなくても
 重要な情報が見れるようになればとても使い勝手があがる
 と言う理由からです。

長々と書いてしまいましたが
列順を何とか抽出して(ファイル等に保持)再起動時に列順を設定することは可能なのでしょうか
ファイルエクスプローラやタスクマネージャの「プロセス」タグを見る限り列順を覚えておけると思うのですが
(ListViewを使用しているのか分かりませんが)


どうか、皆様からの情報を待っております。
宜しくお願いいたします。
2003/12/18(Thu) 22:29:08 編集(投稿者)

もうしわけありません、発言者のこうです
書いてる途中で送信してしまいました
以前pin's Laboratoryへ投稿していた内容ですが
情報がえられなかったのでこちらへ投稿いたしました。
マルチポストになるのなら
管理人さん−>削除してもかまいません。
環境も途中で送信してしまいました
Windows2000 or WindowsXP/VB.NET2003/.NET Frameworkのバージョン1.1の環境です
どうかよろしくお願いいたします。
こんばんは、こうさん。ピラルクです。

ヘッダはSysHeader32という別Windowのようで、
そいつに問い合わせる感じになります。
Sourceのコメントにあるとおり未完成なので、(^^;;;
完成させてやってください。

Public Class Form1
  Inherits System.Windows.Forms.Form

  Const LVM_GETHEADER As Integer = &H1000 + 31 'LVM_FIRST + 31
  Const HDM_HITTEST As Integer = &H1200 + 6    'HDM_FIRST + 6

  Structure HD_HITTESTINFO
    Dim pt As Point
    Dim flags As Integer
    Dim iItem As Integer
  End Structure

  Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
    ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
    ByVal wParam As IntPtr, ByRef lParam As HD_HITTESTINFO) As IntPtr

'" Windows フォーム デザイナで生成されたコード "
  '↑↑↑ ListView と Button を1つずつ貼る

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    'このSub内は単なるテスト環境構築用
    With ListView1
      .AllowColumnReorder = True
      .View = View.Details

      .Columns.Add("ユーザID", 80, HorizontalAlignment.Left)
      .Columns.Add("ユーザ名", 120, HorizontalAlignment.Left)
      .Columns.Add("年齢", 60, HorizontalAlignment.Left)
      .Columns.Add("所属", 70, HorizontalAlignment.Left)
      .Columns.Add("備考", 200, HorizontalAlignment.Left)

      .Items.Add(New ListViewItem(New String() {"123", "scott", "25", "DEPT1", "OraUser"}))
      .Items.Add(New ListViewItem(New String() {"246", "sa", "28", "DEPT2", "SQLUser"}))
    End With
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim i As Integer
    Dim p As Point
    Dim udtHTI As HD_HITTESTINFO
    Dim hSysHeader As IntPtr = SendMessage(ListView1.Handle, LVM_GETHEADER, IntPtr.Zero, Nothing)

    udtHTI.pt.X = 10 'なぜか10位でないと上手くいかない。なぜ0ではダメなのか?

    For i = 0 To ListView1.Columns.Count - 1
      SendMessage(hSysHeader, HDM_HITTEST, IntPtr.Zero, udtHTI)
      udtHTI.pt.X += ListView1.Columns(udtHTI.iItem).Width
      Console.Write(ListView1.Columns(udtHTI.iItem).Text & ", ")
    Next
    Console.WriteLine()

    '列幅を狭めているとudtHTI.iItemは-1を返す。対策が必要だ。。。
  End Sub
End Class
ピラルクさん
回答ありがとうございます。

会社に行かないと.NET環境がないので
月曜日に確認しようと思います。
その後結果報告をさせていただきます。
ソースを掲載していただき本当にありがとうございます。
では、月曜日にがんばってみます。
2003/12/24(Wed) 00:45:09 編集(投稿者)

横槍失礼します。初カキコになります、深山と申します。
私も以前こうさんと同様のことをしたいと考え、挫折したことがありました(^_^;)
ピラルクさんのお陰で取っ掛かりが掴めましたので、お礼を兼ねて私なりの調査報告をさせて頂きたいと思います。

■HDM_HITTEST
このメッセージで取得できるのはudtHTI.ptで渡した座標に位置する列情報のようです。
座標が列の境界線と重なってしまうと左の列の値が返ってきてしまうようで、その所為で

> なぜか10位でないと上手くいかない。

となっていたようです。
列幅を狭めていると上手くいかないのもその辺りの兼ね合いになるようでした。
また、対象列が画面内に表示されていない時もudtHTI.iItemは-1を返すようです。
(HITTESTとしては該当なしということでしょうか。)

■HDM_GETITEM
どうにも上手くいかない‥‥(ーー;)
そこでSysHeader32関連のメッセージを調べたところ、HDM_GETITEMというそのものずばりのものを見つけることができました。
以下、これを使用したサンプルです。
(ピラルクさんのソースをベースにさせて頂き、変更のない箇所は省略しています。)


Const LVM_GETHEADER As Integer = &H1000 + 31 'LVM_FIRST + 31
Const HDM_GETITEM As Integer = &H1200 + 3 'HDM_FIRST + 3
Const HDI_ORDER As Integer = &H80

Structure HD_ITEM
Dim mask As Integer
Dim cxy As Integer
Dim pszText As String
Dim hbm As Integer
Dim cchTextMax As Integer
Dim fmt As Integer
Dim lParam As Integer
Dim iImage As Integer
Dim iOrder As Integer
End Structure

Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
ByVal hwnd As IntPtr, ByVal wMsg As Integer, _
ByVal wParam As Int32, ByRef lParam As HD_ITEM) As IntPtr

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
Dim p As Point
Dim udtHDI As HD_ITEM
Dim hSysHeader As IntPtr = SendMessage(ListView1.Handle, LVM_GETHEADER, Nothing, Nothing)

udtHDI.mask = HDI_ORDER
For i = 0 To ListView1.Columns.Count - 1
SendMessage(hSysHeader, HDM_GETITEM, i, udtHDI)
Console.Write("{0}({1}), ", ListView1.Columns(i).Text, udtHDI.iOrder)
Next
Console.WriteLine()
End Sub


私の環境(WinXP/VB.NET2000)では問題なく動作したようですけども、どうでしょうか?

今回のことがあるまで、SysHeader32のことは全く知りませんでした。
こうさん、ピラルクさんのお陰で勉強になりました。ありがとうございます。
(ところでこういうカキコは失礼にあたったりするのでしょうか? だとしたら申し訳ありませんm(__)m)


追記:
うわっ、お名前を間違えてしまってる!?(滝汗)
本当に申し訳ありません。修正致しましたm(__)m
こんばんは、深山さん。ピラルクです。

おぉ、Structure HD_ITEM で udtHDI.mask = HDI_ORDER
で SendMessage(hSysHeader, HDM_GETITEM, i, udtHDI)
でしたか。Pointもなくなりこれは順番確認には確実ですね。

HD_HITTESTINFOは、マウスイベントとの連動で用いる感じですね。
私は最初、10ずらしていましたが、後で0から1ピクセルずつ増や
してスキャンしてみたら、8ピクセルのズレが確認できました。2
ピクセル程度ならボーダか何かかなと解釈したかもしれないけど、
8ピクセルというのは解せない。

> 座標が列の境界線と重なってしまうと左の列の値が返ってきてしまうようで、

境界線上にマウスポインタがあると、8ピクセル程度右にズレても
マウスポインタが ⇔ のままだから、きっとこの8ピクセルという
ことなんでしょうね。完成ありがとうございました。v(^^)

> ところでこういうカキコは失礼にあたったりするのでしょうか?

誰に対しての...?
という感じですが、誰であっても失礼なわけありません。

ピラルクとピラクルを間違うのは、通常、幾分失礼かもしれませんが(笑)
(↑気にしてません。念のため)
発言者のこうです
ピラルクさん、深山さん
本当にありがとうございました
今日、HDM_GETITEMを使用し確認しました。
私がもとめているものが完成です。
本当になんとお礼をいってよいのやら
長年VBをやってきていましたが SendMessage を使用したのは初めてでした。
どうも敷居が高そうで...
SendMessageを使用すると色々なコントロールの制御ができそうですね。
今度は、回答する側になれるよう、これからもっと勉強していきます
ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板