跳到主要內容

 none
ListBox 我想讓item 在 Drag&Drop的時候出現一條線 讓使用者知道會把項目移動到哪 RRS feed

  • 問題

  • 小弟有找到TreeView版 但寫得太複雜看不懂

    也有找到C#的dll參考 但跟我程式的運作原理差太多

    我只需要畫線的部分就好 拖曳項目我已經寫好了

    原本也想自己寫 但不知道該寫在哪個事件中 要如何判斷高度和座標...

    只好上來發問  還請各路大神幫忙

    Item輸入格式:

    ListBox.items.add("00:00 title {vbback} Album")

    我想要的效果:  (我只需要畫線的部分就好 拖曳項目我已經寫好了)

    在此貼上我的部分:

    '自訂Item
     Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    
       Dim f1 As Font = e.Font
            Dim f2 As New Font("微軟正黑體", e.Font.Size - 2)
            Dim Ln As New PointF(e.Bounds.X, e.Bounds.Y) '第0行開始位置
            Dim L1 As New PointF(e.Bounds.X + 25, e.Bounds.Y) '第1行開始位置
            Dim L2 As New PointF(e.Bounds.X + 25 + 2, e.Bounds.Y + f1.GetHeight) '第2行開始位置
            Dim strLin() As String = Split(ListBox1.Items(e.Index), vbBack)
    
            e.DrawBackground()
      '如果被選取
                If (e.State And DrawItemState.Selected) Then
                    e.Graphics.DrawString((e.Index + 1).ToString("00") & ".", f2, Brushes.White, Ln)
                    e.Graphics.DrawString(strLin(0), f1, Brushes.White, L1)
                    e.Graphics.DrawString(strLin(1), f2, Brushes.White, L2)
                Else
                    e.Graphics.DrawString((e.Index + 1).ToString("00") & ".", f2, Brushes.Gray, Ln)
                    e.Graphics.DrawString(strLin(0), f1, Brushes.Black, L1)
                    e.Graphics.DrawString(strLin(1), f2, Brushes.Gray, L2)
                End If
        End Sub
    'Item寬度
       Private Sub listBox1_MeasureItem(ByVal sender As System.Object, ByVal e As MeasureItemEventArgs) Handles ListBox1.MeasureItem
            ListBox1.DrawMode = DrawMode.OwnerDrawVariable
            e.ItemHeight = 60
    
    
            Dim f(1) As Font
            f(0) = Me.Font '第1行字形 
            f(1) = New Font("微軟正黑體", f(0).Size - 2) '第2行字形 
    
            Dim strLin() As String = Split(ListBox1.Items(e.Index), vbBack)
    
            For i = 0 To 1
                '以每一行的寬度決定是否調整 HorizontalExtent 
                Dim TextSize As SizeF = e.Graphics.MeasureString(strLin(i), f(i))
    
                If ListBox1.HorizontalExtent < TextSize.Width + 25 Then
                    ListBox1.HorizontalExtent = TextSize.Width + 25
                End If
            Next
        End Sub
    '暫存Array
        Dim DataObj As New ArrayList()
     Private Sub ListBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseMove
            Try
                If ListBox1.Text <> "" And e.Button = MouseButtons.Right Then
                    DataObj.Clear()
                    DataObj.AddRange(ListBox1.SelectedIndices)
                    DataObj.Reverse()
                    Call DoDragDrop("data", DragDropEffects.Move)
                End If
            Catch ex As Exception
    
            End Try
    
        End Sub
    
        Private Sub ListBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop
       With ListBox1
                n02 = .IndexFromPoint(.PointToClient(New Point(e.X, e.Y)))
                '如果滑鼠目前位置沒有項目 就選擇最後一項
                If (n02 = ListBox.NoMatches) Then n02 = .Items.Count - 1
                If (n02 <> ListBox.NoMatches) Then
                    '由上往下拖
                    If n01 < n02 And n01 + DataObj.Count - 1 < n02 Then
                        'wmp
                        For i As Integer = DataObj.Count - 1 To 0 Step -1
    '因為我要用到wmplayer的部分 所以不能直接用C#的dll 
    '我那時候還不知道可以add class 要改的地方會很多 怕會出太多BUG 所以就寫成連動的
                            Call wmp.currentPlaylist.moveItem(n01, n02)
                        Next
                        'list
                        For i As Integer = 0 To DataObj.Count - 1
                            .Items.Insert(n02 + 1, .Items(Val(DataObj(i))))
                        Next
                        For i As Integer = 0 To DataObj.Count - 1
                            .SetSelected(n02 + 1 + i, True)
                        Next
                        For i As Integer = 0 To DataObj.Count - 1
                            .Items.RemoveAt(n01)
                        Next
                    End If
    
                    '由下往上拖
                    If n01 > n02 Then
                        For i As SByte = DataObj.Count - 1 To 0 Step -1
    
                            wmp.currentPlaylist.insertItem(n02, wmp.currentPlaylist.Item(ListBox1.SelectedIndices(i)))
                            .Items.Insert(n02, ListBox1.SelectedItems(i))
    
                            wmp.currentPlaylist.removeItem(wmp.currentPlaylist.Item(ListBox1.SelectedIndices(i)))
                            .Items.RemoveAt(ListBox1.SelectedIndices(i))
                        Next
                        For i As SByte = 0 To DataObj.Count - 1
                            .SetSelected(n02 + i, True)
                        Next
                    End If
    
                End If
            End With
        End Sub
    '這要讓右鍵點下去時會選取項目
        Private Sub ListBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
            If e.Button = Windows.Forms.MouseButtons.Right Then
                '  ListBox1.Items.Clear()
                Try
    
                    '如果放開的位置對了 就取消選取 並選擇新項目    '如果原本已經有選擇像目 而使用者直接按右鍵 選擇那個項目時 會取消選取之前的項目
                    If Not (ListBox1.SelectedIndices(0) <= ListBox1.IndexFromPoint(e.X, e.Y) And ListBox1.SelectedIndices(ListBox1.SelectedIndices.Count - 1) >= ListBox1.IndexFromPoint(e.X, e.Y)) Then
                        ListBox1.ClearSelected()
                    End If
    
                    ListBox1.SetSelected(ListBox1.IndexFromPoint(e.X, e.Y), True)
                Catch ex As Exception
    
                End Try
    
            End If
            n01 = ListBox1.SelectedIndex
        End Sub
    
     
        Private Sub ListBox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter
            e.Effect = DragDropEffects.All
        End Sub
    
    


    2019年10月5日 上午 02:52

解答

  • 有了 我用  ->vb net listbox drag drop Draw Insertion Line 找到了

    雖然是C#的範例 但比其他的範例簡單很多很多 我就看得懂了OTZ 不需要用到Windows API的樣子

    private void listBox1_DragOver(object sender, DragEventArgs e)
    {
        Point mLoc = listBox1.PointToClient(Cursor.Position);
        int idx = listBox1.IndexFromPoint(mLoc);
        if (idx  < 0) return;
        if (idx == prevItem) return;
    
        listBox1.Refresh();
        using (Graphics g = listBox1.CreateGraphics())
        {
            Rectangle rect = listBox1.GetItemRectangle(idx);
            g.DrawLine(Pens.Red, rect.Left, rect.Top, rect.Right, rect.Top);
        }
        prevItem = idx;
    }
    下面是我改過後的版本
      Dim prevItem As Integer = -1
        Dim rect As Rectangle
        Private Sub ListBox1_DragOver(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragOver
            e.Effect = DragDropEffects.Move
    
            Dim mLoc As Point = ListBox1.PointToClient(Cursor.Position)
            Dim idx As Integer = ListBox1.IndexFromPoint(mLoc)
    
            '如果目前的項目 跟之前的一樣 就不顯示線
            If (idx = prevItem) Then Return
            Dim g As Graphics = ListBox1.CreateGraphics()
            ListBox1.Refresh()
            Try
                rect = ListBox1.GetItemRectangle(idx)
                If idx < prevItem Then
    
                    g.DrawLine(Pens.Red, rect.Left, rect.Top - IIf(idx = -1, rect.Height, 0), rect.Right, rect.Top - IIf(idx = -1, rect.Height, 0))
                Else
                    g.DrawLine(Pens.Red, rect.Left, rect.Top + rect.Height, rect.Right, rect.Top + rect.Height)
    
                End If
            Catch ex As Exception
    
            End Try
            prevItem = idx
        End Sub




    2019年10月6日 上午 03:02

所有回覆

  • 2019年10月5日 上午 11:12
  • 我還不太會看C#...

    裡面好像有用到Windows API 看半天看不懂QQ

    不如您建議小弟用些什麼關鍵字搜尋吧 找了2天了 不知道關鍵字很難找TAT

    2019年10月5日 下午 05:32
  • VB.NET的討論較少, 找不太到, 所以才給您C#的文件...
    2019年10月5日 下午 10:10
  • 有了 我用  ->vb net listbox drag drop Draw Insertion Line 找到了

    雖然是C#的範例 但比其他的範例簡單很多很多 我就看得懂了OTZ 不需要用到Windows API的樣子

    private void listBox1_DragOver(object sender, DragEventArgs e)
    {
        Point mLoc = listBox1.PointToClient(Cursor.Position);
        int idx = listBox1.IndexFromPoint(mLoc);
        if (idx  < 0) return;
        if (idx == prevItem) return;
    
        listBox1.Refresh();
        using (Graphics g = listBox1.CreateGraphics())
        {
            Rectangle rect = listBox1.GetItemRectangle(idx);
            g.DrawLine(Pens.Red, rect.Left, rect.Top, rect.Right, rect.Top);
        }
        prevItem = idx;
    }
    下面是我改過後的版本
      Dim prevItem As Integer = -1
        Dim rect As Rectangle
        Private Sub ListBox1_DragOver(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragOver
            e.Effect = DragDropEffects.Move
    
            Dim mLoc As Point = ListBox1.PointToClient(Cursor.Position)
            Dim idx As Integer = ListBox1.IndexFromPoint(mLoc)
    
            '如果目前的項目 跟之前的一樣 就不顯示線
            If (idx = prevItem) Then Return
            Dim g As Graphics = ListBox1.CreateGraphics()
            ListBox1.Refresh()
            Try
                rect = ListBox1.GetItemRectangle(idx)
                If idx < prevItem Then
    
                    g.DrawLine(Pens.Red, rect.Left, rect.Top - IIf(idx = -1, rect.Height, 0), rect.Right, rect.Top - IIf(idx = -1, rect.Height, 0))
                Else
                    g.DrawLine(Pens.Red, rect.Left, rect.Top + rect.Height, rect.Right, rect.Top + rect.Height)
    
                End If
            Catch ex As Exception
    
            End Try
            prevItem = idx
        End Sub




    2019年10月6日 上午 03:02
  • 透過 Windows API 及回呼函數玩遍控制項的,可參考 Charles Petzold 的書,中譯本有純 Windows API / VBNET (2002) / C# (2002) / WPF 。

    每本都號稱聖經及巨作,都厚的跟字典一樣,沒有入門篇幅浪費時間,須跨過入門的人才適合閱讀。


    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2019年10月6日 上午 07:31
  • 可以找這本的中譯本

    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2019年10月6日 上午 07:34
  • 好的好的

    這個方法不需要用到就是了(

    2019年10月6日 上午 10:32