zdarzenie MouseRightButtonDown - jak to działa?

0

Pytanie z cyklu "a dlaczego?".

Utworzyłem kolekcję obiektów ListBox, którą chciałem obsługiwać przez RMB i LMB.
Pomijając problemy (rozwiązane) z oznaczeniem numeru właściwego indexu zauważyłem że:

PreviewMouseRightButtonDown działa (wykonuje akcję RMB )
PreviewMouseRightButtonUp działa (kończy akcję RMB)
MouseRightButtonUp (kończy akcję RMB zaraz za [Preview])

Z kolei klikając na element Item w ListBox zdarzenie MouseRightButtonDown już nie działa.

Czy możecie podpowiedziec dlaczego i jak zmusić MouseRightButtonDown do akcji?
MouseLeftButtonDown podobnie jak RMB - także nie działa.

Aktywności uzyskałem z prostego kodu, generując 4 zdarzenia RBM dla ListBox:

        private void myList_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            right_click = false;          //stała lokalna opisująca zachowanie RMB
            int k = 1;                         //liczba do wyświetlenia
            indx += 10;                     //funkcja
            Update_Title(k);
        }

        private void myList_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            right_click = true;            //ten event nie działa dla ListBox, zarówno RMB jak i LMB
            int k = 2;
            indx += 100;
            Update_Title(k);
        }

        private void myList_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            right_click = true;
            int k = 3;
            indx += 1000;
            Update_Title(k);
        }

        private void myList_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            right_click = false;
            int k = 4;
            indx += 10000;
            Update_Title(k);
        }

        private void Update_Title(int k)
        {
            if (right_click == true)
            { btnTest.Content = k.ToString() + "R push "; }
            else if (left_click == true)
            { btnTest.Content = k.ToString() + "L push "; }
            else
            { btnTest.Content = k.ToString() + "not push "); }
        }

2

OK, to działa tak. W WinForms miałeś tylko jeden rodzaj zdarzenia. Np. OnButtonDown. On był wywoływany przez kontrolkę i obsługiwany np. przez formę. Ale poza OnButtonDown, guzik łapał jeszcze komunikat z Windowsa (WM_MESSAGE) i dopiero w tym komunikacie było odpalenie OnButtonDown.

WPF działa trochę inaczej. Lepiej/gorzej... Na pewno inaczej :)
W WPF masz tzw. Input Events (czyli kliknięcia, downy itd), które są podzielone na dwie kolejne grupy: "Bubbling events" i "Tunneling Events" (ale sparowane ze sobą).

Tunneling Events to te zaczynające się od Preview. Bubbling - bez tego przedrostka.

Załóżmy, że masz taki układ -> forma -> stack panel -> button

Teraz, jeśli klikasz na button, najpierw odpala się PreviewMouseDown. ALE w związku z tym, że to jest tunneling event, zaczyna się on od formy. Jeśli nie zostanie obsłużony na formie, idzie na stack panel. Jeśli nie zostanie obsłużony na stack panelu, idzie do buttona. Następnie button odpala MouseDown (bubbling event), który wędruje w górę drzewa do stack panelu. Jeśli stack panel nie obsłuży tego zdarzenia, to wędruje dalej w górę do formy.

Każde zdarzenie w parametrach ma taką właściwość Handled. I to właśnie mówi, czy zdarzenie zostało obsłużone.

I teraz tunneling events idą od góry drzewa w dół w kierunku źródła (Source), a bubbling events idą w górę drzewa od źródła.

Zauważ, że jeśli gdzieś obsłużysz tunneling event (Handled = true), to bubbling event nie zostanie już wywołane.
Jeśli obsłużysz gdzieś bubbling event (Handled = true), to on już wyżej nie przejdzie.

1

MouseRightButtonDown - ale kiedy klikasz w ListBox (czyli jak nie ma żadnych elementów), czy ListBoxItem (gdy klikasz w jakiś element wewnątrz listy)?
Zobacz ten przykład:

         <ListBox x:Name="hListBox" ItemsSource="{Binding Path=Lista}" HorizontalContentAlignment="Stretch" MouseRightButtonDown="hListBox_MouseRightButtonDown">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" MouseRightButtonDown="TextBlock_MouseRightButtonDown"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            var TextBlock = sender as TextBlock;
            string Value = TextBlock.DataContext.ToString();
            Debug.WriteLine("Kliknięto w ListBoxItem " + Value);
        }

        private void hListBox_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("Kliknięto w ListBox");
        }
    }

screenshot-20210325134338.png

Lista jest rozszerzona na całe okno. gdy klikniesz na jeden z trzech elementów tak naprawdę klikasz w TextBlock, Wykonuje się zatem metoda przypisana do TextBlock.MouseRightButtonDown, czyli TextBlock_MouseRightButtonDown. gdy kliknę w pole puste, czyli te które nie zajmują jeszcze elementy klikam po prostu w kontener ListBox i wywoła się ListBox.MouseRightButtonDown
Wynikiem kliknięcia w każdy przycisk po kolei i na koniec w puste pole będzie następujący:

Kliknięto w ListBoxItem str 1
„WpfApp1.exe” (CLR v4.0.30319: WpfApp1.exe): załadowano „C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework-SystemXml\v4.0_4.0.0.0__b77a5c561934e089\PresentationFramework-SystemXml.dll”. 
Kliknięto w ListBoxItem str 2
Kliknięto w ListBoxItem str 3
Kliknięto w ListBox
Program „[3640] WpfApp1.exe” zakończył działanie z kodem 0 (0x0).

1 użytkowników online, w tym zalogowanych: 0, gości: 1