ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • WPF 한 ListView(ListBox)에서 Drag Drop
    C#/WPF 2018. 1. 30. 10:47

    WPF에서 ListView or ListBox(이하 List 컨트롤)와 같은 List 컨트롤 들에서 Item을 Drag, Drop하는 일이 자주 있습니다.


    그런데 한글 자료로는 별로 없고 해외 자료들은 두 List 컨트롤 간에 Drag Drop이 주를 이루더군요.



    그래서 여러 자료들을 찾고, 참고하여 한 List 컨트롤 내에서 Drag Drop으로 순서 바꾸기를 구현했습니다.


    2018.01.29 - 아직 Item 사이에 커서가 위치한 상태로 Drop을 하면 맨 아래로 Drop되는 문제가 있습니다.



    우선 List컨트롤에 아래의 EventHandler들을 추가해주어야 합니다.

    • PreviewMouseLeftButtonDown
    • PreviewMouseMove
    • Drop


    또한 AllowDrop 속성을 True로 바꿔주어야 합니다.

    • AllowDrop="True"


    아래는 예시 ListView입니다.


    1
    2
    3
    4
    5
    6
    <ListView Name="myList" HorizontalAlignment="Left" VerticalAlignment="Top" 
        Width="200" Height="300" Margin="85,65,0,0"
        PreviewMouseLeftButtonDown="MyList_PreviewMouseLeftButtonDown"
        PreviewMouseMove="MyList_PreviewMouseMove"
        Drop="MyList_Drop"
        AllowDrop="True"/>
    cs



    Drag를 위해 사용하는 변수가 있습니다.


    1
    2
    private bool isDrag = false;
    private int indexDrag = -1;
    cs


    isDrag : 현재 Drag를 하려는 중인지 확인

    indexDrag : Drag하려는 item의 index


    다음은 EventHandler들입니다.


    1. MyList_PreviewMouseLeftButtonDown


    1
    2
    3
    4
    5
    6
    7
    private void MyList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var temp = FindAncestor<ListViewItem>
            ((DependencyObject)(myList.InputHitTest(e.GetPosition(myList))));
        if (temp != null)
            isDrag = true;
    }
    cs


    마우스 왼 버튼을 누르기 전, List 컨트롤의 Item을 눌렀다면(not null) Drag모드 True



    2. MyList_PreviewMouseMove


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    private void MyList_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if ((e.LeftButton == MouseButtonState.Pressed) && isDrag == true)
        {
            ListView listView = sender as ListView;
            ListViewItem listViewItem = FindAncestor<ListViewItem>
                                            ((DependencyObject)e.OriginalSource);
     
            if (listViewItem == null)
                return;
     
            indexDrag = myList.Items.IndexOf(listViewItem.Content.ToString());
     
            string content = (string)(listViewItem.Content);
     
            DataObject data = new DataObject("GGMusic", content);
            DragDrop.DoDragDrop(listViewItem, data, DragDropEffects.Move);
        }
    }
    cs


    마우스를 움직이기 전, 왼쪽 버튼이 눌린 상태, Drag모드가 True라면 Drag 시작

    선택한 Item의 index 확인

    (GGMusic 은 다른것으로 바꾸어도 무방)

    현재 List컨트롤에서는 Item의 Content가 string형이기 때문에 string형으로 형변환



    3. MyList_Drop


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    private void MyList_Drop(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent("GGMusic"))
        {
            string content = e.Data.GetData("GGMusic"as string;
     
            ListView listView = sender as ListView;
     
            int index = -1;
            var temp = FindAncestor<ListViewItem>
                            ((DependencyObject)(myList.InputHitTest(e.GetPosition(myList))));
     
            if (temp == null)
                index = myList.Items.Count - 1;
            else
                index = myList.Items.IndexOf(temp.Content.ToString());
     
            myList.Items.RemoveAt(indexDrag);
     
            if ((indexDrag < index) && (temp != null))
                index--;
            myList.Items.Insert(index, content);
            myList.Items.Refresh();
     
            indexDrag = -1;
            isDrag = false;
        }
    }
    cs


    GGMusic(사용자 임의) 으로 변환이 가능할 경우 진행합니다.

    content를 저장해 둡니다.


    현재 커서가 위치해 있는 곳의 ListViewItem을 찾아서 해당 Item의 index를 찾습니다.

    만일 Item에 커서가 없다면 가장 마지막 index가 됩니다.


    Drag로 가져온 Item을 indexDrag를 통해 지우고 새 index에 삽입합니다.




    위 Handler들에서 사용하는 FindAncestor함수입니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private static T FindAncestor<T>(DependencyObject obj)
                where T : DependencyObject
    {
        do
        {
            if (obj is T)
            {
                return (T)obj;
            }
            obj = VisualTreeHelper.GetParent(obj);
        }
        while (obj != null);
        return null;
    }
    cs


    위 함수는 전달받은 컨트롤에서 visual tree를 따라 parent로 이동하면서 T 타입을 찾고, 있을경우 해당 object를 반환합니다.



    ps.

    기본적인 Drag Drop 방법입니다. 이를 포함 다른 기능들은 MusicPlayer에서 사용할 예정입니다.


    따라서 이 게시글도 개선해 나갈 것입니다.





    참고 (링크1) - wpf tutorial

     (링크2) - code project

    'C# > WPF' 카테고리의 다른 글

    WPF Drag, Drop이 되는 ListBox  (0) 2018.02.27
    WPF ListView(ListBox) 드래그 하는 중 스크롤  (0) 2018.01.31
    WPF 새 윈도우 위치 지정  (2) 2018.01.05
    WPF 실행파일 위치 알아내는 법  (0) 2017.12.29
    WPF 폰트(글꼴) 포함 배포  (0) 2017.12.20

    댓글

GiGong