{"id":67,"date":"2011-11-28T18:20:00","date_gmt":"2011-11-28T18:20:00","guid":{"rendered":"http:\/\/tpodolak.com.hostingasp.pl\/blog\/2011\/11\/28\/routed-events-nowy-rodzaj-zdarzen-w-wpf-ie\/"},"modified":"2016-01-31T00:38:38","modified_gmt":"2016-01-31T00:38:38","slug":"routed-events-nowy-rodzaj-zdarzen-w-wpf-ie","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2011\/11\/28\/routed-events-nowy-rodzaj-zdarzen-w-wpf-ie\/","title":{"rendered":"Routed Events &#8211; nowy rodzaj zdarze\u0144 w WPF-ie"},"content":{"rendered":"<p><span style=\"font-style: italic;\">RoutedEvent <\/span>jest to nowy typ zdarze\u0144, kt\u00f3ry po raz pierwszy zosta\u0142 zaprezentowany w WPF-ie. G\u0142\u00f3wnym za\u0142o\u017ceniem <span style=\"font-style: italic;\">RoutedEvent\u00f3w <\/span>jest to, \u017ce w momencie wywo\u0142ania takiego zdarzenia mo\u017ce ono podr\u00f3\u017cowa\u0107 w g\u00f3r\u0119, lub w d\u00f3\u0142 drzewa wizualnego oraz drzewa logicznego. Ka\u017cdy <span style=\"font-style: italic;\">RoutedEvent <\/span>mo\u017ce przyjmowa\u0107 jedn\u0105 z trzech strategii poruszania si\u0119 po drzewie:<\/p>\n<ul>\n<li><span style=\"font-weight: bold;\">Bubbling<\/span>&#8211; zdarzenie najpierw jest wywo\u0142ywane w elemencie \u017ar\u00f3d\u0142owym, a nast\u0119pnie podr\u00f3\u017cuje ono w g\u00f3r\u0119 drzewa wizualnego (od naszego elementu do korzenia drzewa), a\u017c do roota (lub do momenty gdy nie zostanie obs\u0142u\u017cone poprzez e.Handled = true)<\/li>\n<li><span style=\"font-weight: bold;\">Tunelling <\/span>&#8211; zdarzenie wywo\u0142ywane jest w korzeniu drzewa, a nast\u0119pnie podr\u00f3\u017cuje w d\u00f3\u0142 drzewa, a\u017c osi\u0105gnie element \u017ar\u00f3d\u0142owy (lub gdy nie zostanie obs\u0142u\u017cone poprzez e.Handled = true)<\/li>\n<li><span style=\"font-weight: bold;\">Direct <\/span>&#8211; zdarzenie jest wywo\u0142ywane tylko i wy\u0142\u0105cznie w elemencie \u017ar\u00f3d\u0142owym &#8211; czyli zdarzenie to zachowuje si\u0119 tak samo jak standardowe .NET-owe zdarzenia<\/li>\n<\/ul>\n<p>Definiowanie w\u0142asnych <span style=\"font-style: italic;\">RoutedEvents <\/span>wygl\u0105da nast\u0119puj\u0105co. W pierwszym kroku przy pomocy <span style=\"font-style: italic;\">EventManagera <\/span>i jego funkcji RegisterRoutedEvent rejestrujemy nasze zdarzenie.<\/p>\n<pre lang=\"csharp\">\r\npublic static RoutedEvent PreviewTrippleClickEvent = EventManager.RegisterRoutedEvent(\"PreviewTrippleClick\", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MyContentControl));\r\n<\/pre>\n<p>nast\u0119pnie piszemy wrapper RoutedEventa na zwyk\u0142e zdarzenie<\/p>\n<pre lang=\"csharp\">\r\npublic event RoutedEventHandler PreviewTrippleClick\r\n{\r\n    add\r\n    {\r\n        AddHandler(MyContentControl.PreviewTrippleClickEvent, value);\r\n    }\r\n    remove\r\n    {\r\n        RemoveHandler(MyContentControl.PreviewTrippleClickEvent, value);\r\n    }\r\n}\r\n<\/pre>\n<p>Funkcja <span style=\"font-style: italic;\">RegisterRoutedEvent <\/span>przyjmuje cztery parametry:<\/p>\n<ul>\n<li><span style=\"font-weight: bold;\">Nazwa naszego zdarzenia<\/span> &#8211; taka sama jak nazwa standardowego eventa opakowuj\u0105cego <span style=\"font-style: italic;\">RoutedEvent<\/span><\/li>\n<li><span style=\"font-weight: bold;\">Strategia routingu<\/span> &#8211; czyli czy nasze zdarzenie b\u0119dzie typu <span style=\"font-style: italic;\">Bubble<\/span>, <span style=\"font-style: italic;\">Tunnel <\/span>lub <span style=\"font-style: italic;\">Direct<\/span><\/li>\n<li><span style=\"font-weight: bold;\">Typ handlera <\/span>&#8211; czyli typ delegata\/funkcji jaki b\u0119dzie mo\u017cna pod\u0142\u0105czy\u0107 do zdarzenia, \u017ceby je obs\u0142u\u017cy\u0107<\/li>\n<li><span style=\"font-weight: bold;\">Typ w\u0142a\u015bciciela <\/span>&#8211; czyli typ klasy do kt\u00f3rej nale\u017cy dany event<\/li>\n<\/ul>\n<p>Jako, \u017ce stworzenie nowego <span style=\"font-style: italic;\">RoutedEventa <\/span>jest do\u015b\u0107 czasoch\u0142onne (w por\u00f3wnaniu ze zwyk\u0142ym zdarzeniem) warto zassa\u0107 sobie snippet, kt\u00f3ry znacz\u0105co przy\u015bpiesza tworzenie routed event\u00f3w. Snippet taki mo\u017cna znale\u017a\u0107 tutaj <a href=\"http:\/\/interactiveasp.net\/blogs\/natesstuff\/archive\/2008\/10\/15\/routed-event-code-snippet.aspx\">RoutedEventSnippet<\/a><br \/>\nMaj\u0105c ju\u017c zdefiniowany nowy <span style=\"font-style: italic;\">RoutedEvent<\/span> mo\u017cna go wywo\u0142a\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<pre lang=\"csharp\">\r\nRaiseEvent(new RoutedEventArgs(MyContentControl.TrippleClickEvent, this));\r\n<\/pre>\n<p>Funkcja <span style=\"font-style: italic;\">RaiseEvent <\/span>w parametrze przyjmuje obiekt typu <span style=\"font-style: italic;\">RoutedEventArts<\/span>. W przypadku gdyby nie by\u0142 on dla nas wystarczaj\u0105cy (potrzebujemy przes\u0142a\u0107 wi\u0119cej parametr\u00f3w itp.), musimy napisa\u0107 klas\u0119 rozszerzaj\u0105c\u0105 klas\u0119 <span style=\"font-style: italic;\">RoutedEventArts<\/span>.<\/p>\n<p>Routowanie zdarzenia mo\u017ce zosta\u0107 zatrzymane, poprzez ustawienie flagi <span style=\"font-style: italic;\">e.Handled<\/span> na <span style=\"font-style: italic;\">true<\/span>. Gdzie <span style=\"font-style: italic;\">e<\/span> jest to obiekt klasy <span style=\"font-style: italic;\">RoutedEventArgs<\/span>. Przyk\u0142adowe zatrzymanie routowania eventa mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b<\/p>\n<pre lang=\"csharp\">\r\nprivate void Window_MouseRightButtonDown(object sender, MouseButtonEventArgs e)\r\n{\r\n    e.Handled = true;\r\n}\r\n<\/pre>\n<p>UWAGA<br \/>\nMo\u017cna zadeklarowa\u0107 zdarzenie w taki spos\u00f3b aby warto\u015b\u0107 flagi <span style=\"font-style: italic;\">e.Handled<\/span> by\u0142a pomijana. Jednak mo\u017cna to zrobi\u0107 jedynie z poziomu kodu. Za pomoc\u0105 metody<span style=\"font-style: italic;\"> AddHandler(RoutedEvent, Delegate, bool)<\/span> je\u017celi ustawimy ostatni parametr na true, w\u00f3wczas nasza metoda wykona si\u0119 nawet w przypadku gdy <span style=\"font-style: italic;\">e.Hadnled == true<\/span>. Metoda ta dost\u0119pna jest dla klasy UIElement i wszystkich klas po niej dziedzicz\u0105cych.<br \/>\n<b><span style=\"font-family: Verdana,sans-serif; font-size: large;\">Attached events <\/span><\/b><br \/>\n<span style=\"font-style: italic;\">Attached event<\/span> dzia\u0142aj\u0105 podobnie jak <span style=\"font-style: italic;\">attached dependency properties<\/span>. Pozwalaj\u0105 one rozszerzy\u0107 kolekcj\u0119 zdarze\u0144 danej kontrolki o dodatkowe zdarzenia(nawet je\u017celi nie mamy dost\u0119pu do \u017ar\u00f3de\u0142 danej kontrolki. Przyk\u0142adowo, istnieje mo\u017cliwo\u015b\u0107 obs\u0142ugi eventu click na kontrolce, kt\u00f3ra tak naprawd\u0119 takiego eventu nie posiada. Ka\u017cdy <span style=\"font-style: italic;\">RoutedEvent<\/span> mo\u017ce zosta\u0107 u\u017cyty jako <span style=\"font-style: italic;\">attached event<\/span>.<\/p>\n<pre lang=\"xml\">\r\n<Window x:Class=\"MCTSTrainingChapter1.ThirdMainWindow\"\r\n        xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\r\n        xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\r\n        Title=\"ThirdMainWindow\" Height=\"300\" Width=\"300\">\r\n    <Grid Button.Click=\"Grid_Click\">\r\n        <Grid.RowDefinitions>\r\n            <RowDefinition><\/RowDefinition>\r\n            <RowDefinition><\/RowDefinition>\r\n            <RowDefinition><\/RowDefinition>\r\n        <\/Grid.RowDefinitions>\r\n        <Button Grid.Row=\"0\">Top<\/Button>\r\n        <Button Grid.Row=\"1\">Middle<\/Button>\r\n        <Button Grid.Row=\"2\">Bottom<\/Button>\r\n    <\/Grid>\r\n<\/Window>\r\n<\/pre>\n<p>Na gridzie mamy zdefiniowane 3 przyciski, chcieliby\u015bmy aby po naci\u015bni\u0119ciu ka\u017cdego z tych przycisk\u00f3w, wywo\u0142ywa\u0142a si\u0119 odpowiednia funkcja.<br \/>\nJednym ze sposob\u00f3w napisania takiej funkcjonalno\u015bci jest pod\u0142\u0105czenie eventa <span style=\"font-style: italic;\">Click <\/span>z ka\u017cdego przycisku do jednego handlera. Rozwi\u0105zanie to jest jak najbardziej poprawne, jednak\u017ce w przypadku du\u017cej ilo\u015bci przycisk\u00f3w mo\u017ce by\u0107 to czasoch\u0142onne. Mo\u017cna zrobi\u0107 to pro\u015bciej, przy wykorzystaniu attached events. Jak widzimy w listingu przedstawionym wy\u017cej, do grida &#8220;przypi\u0119to&#8221; event <span style=\"font-style: italic;\">Click <\/span>z klasy <span style=\"font-style: italic;\">Button<\/span>. Od teraz<br \/>\nza ka\u017cdym razem gdy jakikolwiek przycisk b\u0119d\u0105cy w obr\u0119bie Grida zostanie naci\u015bni\u0119ty, zdarzenie to zostanie przechwycone przez handler zdefiniowany w gridzie &#8211; <span style=\"font-style: italic;\">Grid_Click<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>RoutedEvent jest to nowy typ zdarze\u0144, kt\u00f3ry po raz pierwszy zosta\u0142 zaprezentowany w WPF-ie. G\u0142\u00f3wnym za\u0142o\u017ceniem RoutedEvent\u00f3w jest to, \u017ce w momencie wywo\u0142ania takiego zdarzenia mo\u017ce ono podr\u00f3\u017cowa\u0107 w g\u00f3r\u0119, lub w d\u00f3\u0142 drzewa wizualnego oraz drzewa logicznego. Ka\u017cdy RoutedEvent mo\u017ce przyjmowa\u0107 jedn\u0105 z trzech strategii poruszania si\u0119 po drzewie: Bubbling&#8211; zdarzenie najpierw jest wywo\u0142ywane [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143,27,145,144,45],"tags":[270,271,272,273,191],"class_list":["post-67","post","type-post","status-publish","format-standard","hentry","category-bubbling","category-events","category-routedevent","category-tunelling","category-wpf","tag-bubbling","tag-events","tag-routedevent","tag-tunelling","tag-wpf"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/67","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/comments?post=67"}],"version-history":[{"count":3,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/67\/revisions"}],"predecessor-version":[{"id":572,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/67\/revisions\/572"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=67"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=67"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=67"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}