none
ListView のアイテムをドラッグ&ドロップ操作によるリストの並べ替えをしたい RRS feed

  • 質問

  • ListView 内でデータの並び替えを任意の場所にドラッグ&ドロップする感じで並び替えをしたいと考えています。
    イメージとしては、IEのお気に入りの位置をづらすような感じです。

    現在考えている方法は、初めにListViewで選択しいるアイテム情報を取得する。

            Dim lv As ListView = DirectCast(sender, ListView)
            'ポイントしているListViewItemを取得
            Dim lvi As ListViewItem = lv.GetItemAt(e.X, e.Y)

    MouseMoveでマウス左ボタンを押したまま任意の場所に移動させることができなく、
    この辺り宜しければご教示いただけたら幸いです。

    2019年7月17日 7:29

すべての返信

  • RappyKakuwane さま よろしく。

    Form ですよね。 でしたら、ListBox1 から ListBox2 の例が以下にあります。

    https://dobon.net/vb/dotnet/control/draganddrop.html

    翻訳なので読みずらいが、こちらの方が良いかも知れません。

    https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.dodragdrop?view=netframework-4.8

    2019年7月17日 8:33
  • 返信ありがとうございます。
    色々、試行錯誤して並べ替えできるようにはなりました。が・・・
    イメージと異なっています。

    ◆ソース
        '選択したListViewItem
        Dim lvi As ListViewItem = Nothing
        '選択したListViewItemのインデックス
        Dim listSelectNo As Integer = 0


        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ListView1.Clear()
            ListView1.View = View.Details   'リストビューを詳細表示に設定
            ListView1.Columns.Add("ファイル名", 800, HorizontalAlignment.Left) '列ヘッダー作成

            Dim lv0 As ListViewItem = New ListViewItem("aka")
            Dim lv1 As ListViewItem = New ListViewItem("blue")
            Dim lv2 As ListViewItem = New ListViewItem("green")

            'ListViewItemオブジェクトの作成
            Dim lvi As New ListViewItem("アイテム")

            'アイテムを追加
            ListView1.Items.Add(lvi)
            ListView1.Items.Add(lv0)
            ListView1.Items.Add(lv1)
            ListView1.Items.Add(lv2)

        End Sub

        Private Sub ListView1_MouseDown(ByVal sender As Object, _
                                    ByVal e As MouseEventArgs) Handles ListView1.MouseDown

            Dim lv As ListView = DirectCast(sender, ListView)
            '選択しているListViewItemを取得
            lvi = lv.GetItemAt(e.X, e.Y)
            '選択しているListViewItemのインデックスを取得
            listSelectNo = lvi.Index

        End Sub

        Private Sub ListView1_MouseUp(ByVal sender As Object, _
                                ByVal e As MouseEventArgs) Handles ListView1.MouseUp

            Dim lvp As ListView = DirectCast(sender, ListView)
            '【移動先】の選択しているListViewItemを取得
            Dim plvi As ListViewItem = lvp.GetItemAt(e.X, e.Y)
            '【移動先】の選択しているListViewItemのインデックスを取得
            Dim lnsertNo As Integer = plvi.Index

            '移動元のデータを取得
            Dim tmpData As ListViewItem = lvi

            '移動元のデータを削除
            ListView1.Items.Remove(lvi)

            '移動先にデータを追加
            ListView1.Items.Insert(lnsertNo, tmpData)


        End Sub

    -------------------------------------
    ListViewのアイテムを持ちあげるような形でなく、マウスの移動した場所に挿入する形になってます。
    IEとかだと選択したアイテムを持ち上げて移動する動作?がないのと

    Dim lnsertNo As Integer = lvpi.Index

    上記部分が、ListViewのアイテムからずれるとえらーになるので、、
    なにがまずいのか試行錯誤してます Orz

    サンプルありがとうございます。


    2019年7月17日 14:59
  • 「ドラッグ中に半透明のイメージを表示する方法」
    https://so-zou.jp/software/tech/programming/c-sharp/control/list-view/draggable-listview.htm

    残念ながら書かれているコードは C# ですが、何をどうすれば良いかは理解できるかと思います。

    2019年7月17日 15:57
  • IEとかだと選択したアイテムを持ち上げて移動する動作?がないのと

    ドラッグ中にアイテムを半透明表示させるには、KOZ6.0 さんが紹介されているように ImageList のドラッグ系API の併用が必要です。

    (Opacity を設定した別フォームなどで代用することもできますが、そっちの方が面倒そう)


    上記部分が、ListViewのアイテムからずれるとえらーになるので、、

    ListView の GetItem メソッドは、該当座標にアイテムが無い場合に Nothing を返しますので、If 等での追加判断が必要です。

    また、今回の目的では GetItem メソッドの代わりに、より詳細な情報を得られる .HitTest(e.Location) メソッドを利用することもできます。


        Private Sub ListView1_MouseUp(ByVal sender As Object, _
                                ByVal e As MouseEventArgs) Handles ListView1.MouseUp

            Dim lvp As ListView = DirectCast(sender, ListView)
            '【移動先】の選択しているListViewItemを取得
            Dim plvi As ListViewItem = lvp.GetItemAt(e.X, e.Y)
            '【移動先】の選択しているListViewItemのインデックスを取得
            Dim lnsertNo As Integer = plvi.Index

            '移動元のデータを取得
            Dim tmpData As ListViewItem = lvi

            '移動元のデータを削除
            ListView1.Items.Remove(lvi)

            '移動先にデータを追加
            ListView1.Items.Insert(lnsertNo, tmpData)


        End Sub

    引数 sender (および、それをキャストした変数 lvp) と ListView1 は、同一のインスタンスです。lvp と ListView1 の両方を同時に使うことは、動作的には問題ありませんが、コードとしての対称性に欠けますので、いずれかに統一しましょう。

    2019年7月18日 0:33
  • サンプルを書いてみました。こんな感じで如何でしょう。

    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ListView1.Clear()
            ListView1.View = View.Details
            ListView1.AllowDrop = True
            ListView1.FullRowSelect = True
            ListView1.MultiSelect = True
            ListView1.ListViewItemSorter = New ListViewItemCustomSorter()
            ListView1.InsertionMark.Color = Color.Magenta
            ListView1.Columns.Add("名前", 200, HorizontalAlignment.Left)
            ListView1.Columns.Add("サイズ", 70, HorizontalAlignment.Right)
            Dim folderPath As String = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
            For Each fi In New System.IO.DirectoryInfo(folderPath).EnumerateFileSystemInfos()
                Dim f = TryCast(fi, System.IO.FileInfo)
                ListView1.Items.Add(fi.Name).SubItems.Add(If(f Is Nothing, "(フォルダ)", f.Length.ToString("N0")))
            Next
        End Sub
    
        Private Sub ListView1_ItemDrag(sender As Object, e As ItemDragEventArgs) Handles ListView1.ItemDrag
            ListView1.DoDragDrop(ListView1.SelectedItems.Cast(Of ListViewItem)().ToArray(), DragDropEffects.Move Or DragDropEffects.Scroll)
        End Sub
    
        Private Sub ListView1_DragEnter(sender As Object, e As DragEventArgs) Handles ListView1.DragEnter
            If e.Data.GetDataPresent(GetType(ListViewItem())) Then
                e.Effect = e.AllowedEffect
            Else
                e.Effect = DragDropEffects.None
            End If
        End Sub
    
        Private Sub ListView1_DragOver(sender As Object, e As DragEventArgs) Handles ListView1.DragOver
            If Not e.Data.GetDataPresent(GetType(ListViewItem())) Then
                Return
            End If
            Dim targetPoint As Point = ListView1.PointToClient(New Point(e.X, e.Y))
            Dim targetIndex As Integer = ListView1.InsertionMark.NearestIndex(targetPoint)
            If targetIndex >= 0 Then
                Dim itemRect As Rectangle = ListView1.GetItemRect(targetIndex)
                ListView1.InsertionMark.AppearsAfterItem = targetPoint.Y > itemRect.Top + itemRect.Height \ 2
                If e.AllowedEffect.HasFlag(DragDropEffects.Scroll) Then
                    ListView1.EnsureVisible(targetIndex)
                End If
            End If
            ListView1.InsertionMark.Index = targetIndex
        End Sub
    
        Private Sub ListView1_DragLeave(sender As Object, e As EventArgs) Handles ListView1.DragLeave
            ListView1.InsertionMark.Index = -1
        End Sub
    
        Private Sub ListView1_DragDrop(sender As Object, e As DragEventArgs) Handles ListView1.DragDrop
            Dim targetIndex As Integer = ListView1.InsertionMark.Index
            If targetIndex = -1 Then
                Return
            ElseIf ListView1.InsertionMark.AppearsAfterItem Then
                targetIndex += 1
            End If
            Dim draggedItems() = DirectCast(e.Data.GetData(GetType(ListViewItem())), ListViewItem())
            Array.Reverse(draggedItems)
            ListView1.SelectedItems.Clear()
            For Each item As ListViewItem In draggedItems
                Dim insertItem As ListViewItem = DirectCast(item.Clone(), ListViewItem)
                ListView1.Items.Insert(targetIndex, insertItem)
                insertItem.Selected = True
                ListView1.Items.Remove(item)
            Next
        End Sub
    
        Private Class ListViewItemCustomSorter
            Implements IComparer
            Private Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
                Return DirectCast(x, ListViewItem).Index - DirectCast(y, ListViewItem).Index
            End Function
        End Class
    End Class
    2019年7月18日 4:58
  • 魔界の仮面弁士さま ありがとうございます。

    返信が遅くなり申し訳ありません。前回の返信のC#のサンプルとにらめっこしてました。

    サンプルありがとうございます。
    感激しています。
    まだソースは全部読めてないのですが、返信したくコメントいたしました。

    今、一個一個解読しています。
    2019年7月18日 19:10
  • RappyKakuwaneさん、こんにちは。フォーラムオペレーターのHarukaです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    ご質問いただいた件ですが、その後いかがでしょうか。
    魔界の仮面弁士さんから寄せられた投稿はお役に立ちましたか。

    参考になった回答には [回答としてマーク] をお願い致します。

    設定いただくことで、
    他のユーザーもお役に立つ回答を見つけやすくなります。

    お手数ですが、ご協力の程どうかよろしくお願いいたします。


    MSDN/ TechNet Community Support Haruka

    ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、
    ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2019年7月22日 7:37
    モデレータ