środa, 11 listopada 2009

Drag & Drop

Ciężko w dzisiejszych czasach wyobrazić sobie można aplikacji, która nie zawiera w sobie elementów Drag and Drop (przeciągnij i upuść). Implementacja tego zadania dla kontrolek w WF nie jest trudna. Aby zrozumieć mechanizm łatwego „przerzucania” elementów z jednej kontrolki do drugiej, warto zapoznać się z procesami, które zachodzą w momencie takiej operacji. Całość omówimy na przykładzie dwóch kontrolek ListBox.
Jak widać na screenie, na formatce położyłem dwa labele oraz dwie kontrolki ListBox (listBox1 i listBox2). ListBox1 zawiera elementy które będziemy chcieli umieścić w drugim ListBoxie. Tak więc możemy się umówić że na listBox1 będziemy mówili źródłowy, a listBox2 docelowy.
Zacznijmy od kontrolki źródłowej. Drag & Drop rozpoczyna się przeważnie od „chwycenia myszką” zawartości kontrolki. Jak więc nie trudno się domyślić będziemy musieli obsłużyć zdarzenie MouseDown. W tym momencie kontrolka źródłowa powinna zainicjować operację przenoszenia elementu. Do wykonania tej części będzie konieczne wywołanie metody DoDragDrop. Metoda ta posiada dwa parametry: data typu object (czyli dane które chcemy przenieść) oraz typ wyliczeniowy DragDropEffects. W typie wyliczeniowym możemy znaleźć takie opcje jak:
None – kontrolka docelowa nie pozwala na kopiowanie
Copy – powoduje skopiowanie danych z kontrolki źródłowej do docelowej
Move – przenosi dane z kontrolki źródłowej do docelowej
Link – zostaje utworzone połączenie (logiczne) z elementem docelowym
Scroll – pozwala na przewijanie kontrolki docelowej podczas przenoszenia
All – połączenie efektów Copy, Move i Scroll
Jak więc widać mamy do dyspozycji wiele ciekawych efektów.
To wszystko jeśli chodzi o odpowiedzialność kontrolki źródłowej. Przejdźmy do kontrolki docelowej. Aby możliwe było upuszczenie na docelową kontrolkę jakiegoś elementu, kontrolka musi obsłużyć dwa zdarzenia: DragEnter oraz DragDrop. Zdarzenia te otrzymują parametr DragEventArgs, który zawiera informacje wymagane do wykonania procedury przenoszenia.
Spójrzmy na elementy składające się na DragEventArgs:
AllowedEffect – efekt który jest wspierany przez kontrolkę źródłową
Data – zwraca obiekt typu IDataObject w którym przechowywane są dane z kontrolki źródłowej
Effect – ustala efekt dla kontrolki docelowej
X,Y – współrzędne myszy
KeyState – zwraca stan klawiszy i myszy jako integer:
1 – wciśnięty lewy przycisk myszy
2 – wciśnięty prawy przycisk myszy
5 – wciśnięty klawisz Shift
9 – wciśnięty klawisz Ctrl
16 –wciśnięty środkowy przycisk myszy
32 – wciśnięty przycisk Alt
 
A więc zbierając wszystkie informacje:
W zdarzeniu DragEnter:
 - Za pomocą metody Data.GetDataPresent sprawdzamy czy przekazujemy poprawny typ danych do     kontrolki docelowej (np. łańcuch znakowy)
- właściwości Effect używamy aby powiadomić kontrolkę źródłową w jaki sposób kontrolka docelowa odbierze dane
W zdarzeniu DragDrop:
- za pomocą Data.DetData dostajemy się do danych które biorą udział w całym procesie
Przejdźmy teraz może do przykładu. W przykładzie tym spowodujemy, że przy przenoszeniu normalnym dane będą przenoszone, a przy wciśniętym dodatkowo klawiszu Shift kopiowane:
    1         /// <summary>
    2         /// Obsługa zdarzenia MoudeDown dla kontrolki źródłowej
    3         /// </summary>
    4         /// <param name="sender"></param>
    5         /// <param name="e"></param>
    6         private void listBox1_MouseDown(object sender, MouseEventArgs e)
    7         {
    8             //sprawdzamy czy przeciągamy element zawierający jakąkolwiek treść
    9             if (listBox1.SelectedIndex >= 0 && listBox1.SelectedItem.ToString() != "")
   10             {
   11                 //zapamiętujemy index
   12                 int index = listBox1.SelectedIndex;
   13                 DragDropEffects effect;
   14                 effect = DoDragDrop(listBox1.Items[index].ToString(), DragDropEffects.Move | DragDropEffects.Copy);
   15                 //w przypadku przenoszenia kasujemy obiekt z listy
   16                 //w przypadku kopiowania obiekt pozostanie na liście
   17                 if (effect == DragDropEffects.Move)
   18                 {
   19                     listBox1.Items.RemoveAt(index);
   20                 }
   21             }
   22
   23         }
   24
   25         /// <summary>
   26         /// Obsługa zdarzenia DragEnter kontrolki docelowej
   27         /// </summary>
   28         /// <param name="sender"></param>
   29         /// <param name="e"></param>
   30         private void listBox2_DragEnter(object sender, DragEventArgs e)
   31         {
   32             //sprawdzamy czy typy się zgadzają, jeśli nie zabraniamy przenoszenia
   33             if (e.Data.GetDataPresent(typeof(string)))
   34             {
   35                 if ((e.KeyState & 4) == 4)
   36                 {
   37                     e.Effect = DragDropEffects.Copy;
   38                 }
   39                 else
   40                 {
   41                     e.Effect = DragDropEffects.Move;
   42                 }
   43             }
   44             else
   45             {
   46                 e.Effect = DragDropEffects.None;
   47             }
   48         }
   49
   50         /// <summary>
   51         /// Obsługa zdarzenia DragDrop kontrolki docelowej
   52         /// </summary>
   53         /// <param name="sender"></param>
   54         /// <param name="e"></param>
   55         private void listBox2_DragDrop(object sender, DragEventArgs e)
   56         {
   57             //dodajemy nowy element do listy
   58             string newItem = e.Data.GetData(typeof(string)).ToString();
   59             listBox2.Items.Add(newItem);
   60         }
Podsumowując, aby zaimplementować Drag & Drop musieliśmy przejść następujące etapy:
 
To na tyle jeśli chodzi o implementację Drag & Drop. Oczywiście powyższy kod to tylko przykład. Można by jeszcze dodać obsługę dwustronną przeciągania itp. Oprócz tekstu można przeciągać także inne elementy jak np. grafikę. Myślę że teraz implementacja Drag & Drop nie sprawi nikomu trudności. Powodzenia i udanych eksperymentów.

1 komentarz: