BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











O drag-and-drop ciąg dalszy: Wizualizacja przenoszonych obiektów

30-09-2004 07:37 | wdon7304

1. Wprowadzenie

Standardowa funkcjonalność środowiska .NET Framework do wizualizacji procesu drag-and-drop używa jedynie zmieniającego się w zależności od aktualnej wartości efektu kursora myszy. Bardzo pożądane byłoby, aby użytkownik poza samym faktem przeprowadzania operacji drag-and-drop mógł dodatkowo widzieć, co przenosi. Osiągalne jest to najlepiej poprzez wyświetlanie podczas całej operacji dodatkowego obrazka przy kursorze myszy, co wymaga od programisty uporania się z dwoma zasadniczymi problemami:

  1. Możliwość reagowania na każdy ruch myszy w celu uaktualniania położenia obrazka. Nie możemy tu niestety skorzystać ze zdarzenia GiveFeedback, gdyż wywoływane jest ono jedynie wtedy, gdy kursor znajduje się w obszarze kontrolki mogącej być potencjalnym obiektem docelowym operacji drag-and-drop, my zaś potrzebujemy mieć możliwość uaktualniania położenia naszego obrazka przy każdej zmianie położenia myszy w dowolnym miejscu ekranu.

  2. Wyświetlanie obrazka w dowolnym miejscu całego obszaru ekranu. Istnieje wiele potencjalnych technik zmierzających do osiągnięcia tego celu, jednak w praktyce zachowanie należytej płynności ruchów i uniknięcie niepożądanych efektów migotania nie należy do najprostszych zadań.

W niniejszym artykule zaproponujemy jedną z możliwych metod uporania się z tymi problemami i uzyskania eleganckiej wizualizacji przenoszonego obiektu.

2. Przechwytywanie ruchu myszy

Do przechwytywania globalnych zdarzeń myszy wykorzystać można dostępny w systemach rodziny Microsoft Windows mechanizm haków (ang. hooks). Jest to potężne narzędzie, za pomocą którego określona funkcja może przechwytywać zdarzenia, zanim dotrą one do aplikacji. Funkcja taka, nosząca nazwę funkcji filtrującej, ma możliwość operowania na przechwytywanych zdarzeniach, a w szczególności ich modyfikacji czy odrzucenia. Nie będziemy zagłębiać się na łamach tego artykułu w sam temat haków, ponieważ jest to zagadnienie zbyt rozległe i odbiegające od niego tematycznie. Istotny dla nas jest jedynie fakt, że funkcja podpięta do haka zdarzeń myszy będzie otrzymywała wszystkie zdarzenia dotyczące aktywności myszy pochodzące ze wszystkich działających w systemie procesów, zaś sama procedura podpięcia realizowana jest za pośrednictwem służących do tego, dobrze udokumentowanych funkcji API. Na końcu artykułu znajdują się odnośniki do kilku obszernych opracowań dotyczących zagadnienia.

W przykładzie skorzystamy z gotowej klasy UserActivityHook (autorstwa George’a Mamaladze, opublikowanej w serwisie The Code Project w artykule Processing Global Mouse and Keyboard Hooks in C#), która wykorzystując zarówno hak zdarzeń myszy jak i klawiatury, udostępnia programiście globalne zdarzenia OnMouseActivity, KeyDown, KeyPress oraz KeyUp, z czego nas interesować będzie jedynie to pierwsze. Obsługa omawianej klasy nie przedstawia żadnych wartych opisywania trudności – jej instancja rozpoczyna przechwytywać zdarzenia zaraz po utworzeniu, zaś dodatkowe metody Stop() oraz Start() umożliwiają manualne zatrzymywanie oraz wznawianie przechwytywania.

3. Wyświetlanie obrazka

Drugi z przedstawionych we wstępie problemów możemy obejść niemal całkowicie, stosując odpowiednio „spreparowany” dodatkowy formularz pomocniczy jako „płótno” dla przemieszczanego obrazu. Takie rozwiązanie eliminuje całkowicie problem związany z rysowaniem obrazu w dowolnym miejscu ekranu i przemieszczaniem go, wymagającym w klasycznym podejściu ręcznego odtwarzania tła. Tutaj zmieniamy po prostu położenie całego formularza (nie ma znaczenia co na nim umieścimy), zaś systemowy mechanizm rysowania formularzy zawsze będzie nam gwarantował płynność ruchów, brak niekorzystnego migotania obrazu oraz wysoką wydajność. Dodatkowo, korzystając z różnych technik nadawania formularzom dowolnych kształtów oraz efektów przeźroczystości, a także z faktu że formularz jest kontenerem (czyli może zawierać w swoim obszarze inne kontrolki) możemy uzyskać niemal dowolne efekty wizualizacyjne.

Wspomniane „odpowiednie spreparowanie” formularza pomocniczego wymaga w zasadzie jedynie nadania odpowiednich wartości czterem jego własnościom:

 

Własność

Wartość

Cel

FormBorderStyle

None

Wyłączenie obramowania formularza.

ShowInTaskbar

False

Wyłączenie pokazywania formularza na pasku zadań.

StartPosition

Manual

Umożliwienie ręcznego ustalenia początkowej pozycji formularza.

TopMost

True

Rysowanie formularza zawsze na wierzchu wszystkich innych okien.

 

4. Implementacja

Poniższy kod przedstawia implementację użytego w przykładzie formularza pomocniczego:

private GlobalHookDemo.UserActivityHook actHook;
public Form2()
{
     ...               
     // Tworzymy instancję klasy UserActivityHook
     actHook= new GlobalHookDemo.UserActivityHook();
     // Na początku zatrzymujemy przechwytywanie globalnych zdarzeń myszy
     actHook.Stop();
     // Podpinamy naszą funkcję obsługi globalnego zdarzenia MouseMove
     actHook.OnMouseActivity+=new MouseEventHandler(Global_MouseMove);
}
private void Global_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
     // Ustawiamy położenie formularza pomocniczego zgodnie z nowym położeniem kursora
     this.SetDesktopLocation(e.X + 5,e.Y + 5);
}
new public void Show()
{
     // Zapamiętujemy aktywny formularz, aby można było przywrócić go
     // z powrotem jako aktywny po wyświetlaniu formularza pomocniczego.
     Form active=Form.ActiveForm;
     // Ustawiamy współrzędne formularza przy kursorze myszy.
     this.DesktopLocation=System.Windows.Forms.Cursor.Position;
     // Uruchamiamy przechwytywanie globalnych zdarzeń myszy.
     actHook.Start();
     // Właściwe wyświetlenie formularza na ekranie.
     base.Show();               
     // Przywracamy formularz macierzysty jako aktywny
     active.Activate();
}
new public void Hide()
{
     // Zatrzymujemy przechwytywanie globalnych zdarzeń myszy.
     actHook.Stop();
     // Właściwe ukrycie formularza.
     base.Hide();
}
// Metoda publiczna umożliwiająca zmianę tekstu kontrolki label1
public void SetText(string text)
{
     label1.Text=text;
}

W konstruktorze formularza tworzymy obiekt przechwytujący, zatrzymujemy przechwytywanie zdarzeń i podpinamy funkcję obsługi zdarzenia, której zadaniem jest uaktualnienie położenia formularza. Zastępujemy tu również metody Show() oraz Hide(), tak aby wyświetlaniu i ukrywaniu formularza towarzyszyło wznawianie oraz wstrzymywanie przechwytywania zdarzeń, oraz aby po jego ukazaniu się aktywny pozostawał formularz macierzysty. W załączonym przykładzie dla zwiększenia efektu wizualnego ustawiliśmy dodatkowo własność Opacity formularza pomocniczego na 50%.

Aby dodać wyświetlanie przenoszonego tekstu przy kursorze podczas operacji drag-and-drop wystarczy teraz jedynie przy inicjowaniu operacji ustawić odpowiedni tekst w formularzu pomocniczym przy użyciu metody SetText oraz wyświetlić go na ekranie przy użyciu zmodyfikowanej metody Show(). Po zakończeniu operacji formularz pomocnicy należy ukryć, używając zmodyfikowanej metody Hide().

private Form2 form2=new Form2();
private void listBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
     ...
          // Ustawiamy odpowiednio tekst w formularzu pomocniczym...
          form2.SetText(s);
          // i wyświetlamy go.
          form2.Show();
          DragDropEffects ee=DoDragDrop(s,DragDropEffects.Move | DragDropEffects.Copy);
          // Ukrywamy formularz pomocniczy.
          form2.Hide();
     ...
}

Podany powyżej przykład jest oczywiście tylko kroplą w morzu możliwości wykorzystania przedstawionej tu metody, która przy zastosowaniu odpowiednich trików umożliwia przy relatywnie małym nakładzie pracy uzyskanie niemal każdego efektu wizualnego.

Załączony do artykułu plik zawiera aplikację z poprzedniego artykułu zmodyfikowaną poprzez dodanie opisanej tutaj funkcjonalności.

Ciekawą rzeczą jest, że niektóre standardowe kontrolki systemu Windows zawierają wbudowanie pewne bardziej zaawansowane możliwości wizualizacyjne, lecz udostępniające je w środowisku .NET Framework klasy opakowujące nie eksponują wszystkich ich funkcjonalności. Z taką sytuacją mamy na przykład do czynienia w kontrolce ImageList, której implementacja zawarta w bibliotece ComCtl32.DLL zapewnia funkcje umożliwiające wyświetlanie dowolnej grafiki symbolizującej przenoszony obiekt – obrazek przemieszczany jest przy tym gładko, nie powodując niepożądanych efektów migania kursora. Dodatkowo, w systemach Windows 2000/XP obrazek może być półprzeźroczysty z zachowaniem wszystkich efektów cieniowania alfa, co w znacznym stopniu poprawia efekt wizualny. Niestety, nie wiadomo z jakich powodów dostępna w środowisku .NET Framework klasa opakowująca ImageList nie eksponuje tej funkcjonalności.

Do jej pełnego wykorzystania potrzebne jest jednak jedynie 8 funkcji API, które mogą zostać w prosty sposób opakowane w nową klasę. Jak tego dokonać pokazuje autor artykułu Sophisticated Drag-Drop Images dostępnego w serwisie VBAccelerator.com.

 

5. Referencje

  1. MSDN Library: Win32 Hooks

  2. Microsoft Knowledge Base Article – 318804: HOW TO: Set a Windows Hook in Visual C# .NET

  3. MSDN Library: About Hooks

  4. Sophisticated Drag-Drop Images

 

Załączniki:

Komentarze 6

ptomcz5014
ptomcz5014
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Artykul bardzo ciekawy pod wzgledem merytorycznym. Czytelnik w przejrzysty i jasny sposob moze sie wiele dowiedziec na temat przydatnego zagadnienia jakim jest D&D, nawet jesli wczesniej nie mial z nim styku.
aostrowska08824
aostrowska08824
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Artykul bardzo ciekawy.
uzytkownik usuniety
uzytkownik usuniety
3556 pkt.
Guru
21-01-2010
oceń pozytywnie 0
artykuł jest całkiem OK. Zaletą jest zwięzłość... szybko się czyta ;-) nie powala jednak z nóg.
User 79899
User 79899
35 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0
W porządku, ale nie rewelacja.
godlewskigo
godlewskigo
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
cóż... artykuł bardzo przpomina poprzedni
User 79355
User 79355
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Bardzo zgrabny artykul i ciekawy artykul...
pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.