{"id":71,"date":"2011-11-27T15:21:00","date_gmt":"2011-11-27T15:21:00","guid":{"rendered":"http:\/\/tpodolak.com.hostingasp.pl\/blog\/2011\/11\/27\/commands-czyli-interakcja-widoku-z-viewmodelem-cz-1\/"},"modified":"2016-01-31T00:44:44","modified_gmt":"2016-01-31T00:44:44","slug":"commands-czyli-interakcja-widoku-z-viewmodelem-cz-1","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2011\/11\/27\/commands-czyli-interakcja-widoku-z-viewmodelem-cz-1\/","title":{"rendered":"Commands  &#8211; czyli interakcja widoku z ViewModelem cz.1"},"content":{"rendered":"<h3>1. Wst\u0119p<\/h3>\n<p>Ka\u017cdy programista, kt\u00f3ry napisa\u0142 chocia\u017c par\u0119 linijek kody w WinFormsach wie, \u017ce kontrolki u\u017cywaj\u0105 event\u00f3w do powiadamiania o zmianach swojego stanu. W przypadku, gdy interesuje nas odpowiednia reakcja na zdarzenie, podpinamy si\u0119 do niego odpowiedni\u0105 funkcj\u0105 i wykonujemy za\u0142o\u017cone przez nas operacj\u0119. W analogiczny spos\u00f3b mo\u017cna post\u0119powa\u0107 w WPF-ie oraz w Silverlighcie, jednak\u017ce podej\u015bcie takie niejako mija si\u0119 z modelem MVVM. Posiadanie event handler\u00f3w w kodzie, \u015bci\u015ble wi\u0105\u017ce nam widok (<span style=\"font-style: italic;\">XAML<\/span>) z code behind lub nawet z samym ViewModelem danego widoku. Ponadto u\u017cywanie event\u00f3w jest problematyczne (a w\u0142a\u015bciwie niemo\u017cliwe), gdy widoki zostan\u0105 zdefiniowane w Resourcach. Dodatkow\u0105 wad\u0105 u\u017cywania event handler\u00f3w jest fakt, \u017ce aplikacje w kt\u00f3rych widok jest mocno zwi\u0105zany z <span style=\"font-style: italic;\">codebehind <\/span>nie nale\u017c\u0105 do aplikacji, kt\u00f3re w \u0142atwy spos\u00f3b mo\u017cna podda\u0107 testom automatycznym.<\/p>\n<h3>2. Interfejs <span style=\"font-style: italic;\">ICommand<\/span><\/h3>\n<p>W celu rozlu\u017anienia po\u0142\u0105czenia mi\u0119dzy widokiem a ViewModelem wprowadzono interfejs<\/p>\n<pre lang=\"csharp\">\r\npublic interface ICommand\r\n{  \r\n    event EventHandler CanExecuteChanged;    \r\n    bool CanExecute(object parameter);    \r\n    void Execute(object parameter);\r\n}\r\n<\/pre>\n<p>Funkcja<\/p>\n<pre lang=\"csharp\">\r\npublic bool CanExecute(object parameter)\r\n<\/pre>\n<p>ma za zadanie sprawdzi\u0107, czy dan\u0105 komend\u0119 mo\u017cna wykona\u0107. Je\u017celi nie,funkcja zwraca false i kontrolka zbindowana do danej komendy jest wygaszana (w\u0142a\u015bciwo\u015b\u0107 <span style=\"font-style: italic;\">IsEnabled <\/span>danej kontrolki ustawiana jest na false). Z kolei funkcja<\/p>\n<pre lang=\"csharp\">\r\npublic void Execute(object parameter)\r\n<\/pre>\n<p>jest to odpowiednik event handlera pod\u0142\u0105czonego do zdarzenia. Zdarzenie<\/p>\n<pre lang=\"csharp\">\r\npublic event EventHandler CanExecuteChanged;\r\n<\/pre>\n<p>informuje widok o tym czy dana komenda mo\u017ce si\u0119 wykona\u0107 czy te\u017c nie &#8211; dzi\u0119ki temu kontrolka wie kiedy si\u0119 od\u015bwie\u017cy\u0107(zmieni\u0107 warto\u015b\u0107 w\u0142a\u015bciwo\u015bci <span style=\"font-style: italic;\">IsEnabled<\/span>).<\/p>\n<h3>3. Spos\u00f3b u\u017cycia<\/h3>\n<p>Za\u0142\u00f3\u017cmy, \u017ce mamy okno na kt\u00f3rym znajduje si\u0119 przycisk. Po naci\u015bni\u0119ciu przycisku chcieliby\u015bmy aby wykona\u0142a si\u0119 jaka\u015b czynno\u015b\u0107. Ka\u017cdy obiekt typu Window oraz Control posiada kolekcj\u0119 CommandBindings, kt\u00f3ra przechowuje wszystkie komendy danego okna.<br \/>\nW celu dodania nowej komendy nale\u017cy zrobi\u0107 nast\u0119puj\u0105c\u0105 rzecz. Po pierwsze musimy stworzy\u0107 now\u0105 komend\u0119. Robimy to przy pomocy nast\u0119puj\u0105cego kodu:<\/p>\n<pre lang=\"csharp\">\r\nprivate static RoutedCommand AddCommand = new RoutedCommand();\r\n<\/pre>\n<p>W celu dodania komendy do danego okna\/kontrolki wykonujemy nast\u0119puj\u0105c\u0105 operacj\u0119<\/p>\n<pre lang=\"csharp\">\r\nthis.CommandBindings.Add(new CommandBinding(AddCommand,ExecuteAddCommand,CanExecuteAddCommand));\r\n<\/pre>\n<p>Funkcja <span style=\"font-style: italic;\">Add <\/span>z klasy <span style=\"font-style: italic;\">CommandBindingCollection <\/span>przyjmuje w parametrze obiekt typu <span style=\"font-style: italic;\">CommandBinding<\/span>. Konstruktor takiego obiektu sk\u0142ada si\u0119 z trzech parametr\u00f3w:<\/p>\n<ul>\n<li>RoutedCommand &#8211; obiekt typu <span style=\"font-style: italic;\">RoutedCommand<\/span><\/li>\n<li>delegat typu <span style=\"font-style: italic;\">ExecutedRoutedEventHandler <\/span>&#8211; jest to delegat do funkcji, kt\u00f3ra ma si\u0119 wykona\u0107 w przypadku gdy dana komenda spe\u0142nia warunki do \u201curuchomienia\u201d<\/li>\n<li>delegat typu <span style=\"font-style: italic;\">CanExecuteRoutedEventHandler <\/span>&#8211; jest to delegat do funkcji, kt\u00f3ra sprawdza czy dan\u0105 komend\u0119 mo\u017cna wykona\u0107. W celu zabronienia wykonania komendy nale\u017cy ustawi\u0107 argument <span style=\"font-style: italic;\">ExecutedRoutedEventArgs <\/span><span style=\"font-style: italic;\">CanExecute <\/span>na false.<\/li>\n<\/ul>\n<p>Na koniec nale\u017cy jeszcze zbindowa\u0107 nasz\u0105 komend\u0119 z przyciskiem. Przyk\u0142adowy kod dodawania komendy mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b.<\/p>\n<pre lang=\"csharp\">\r\npublic partial class WatchList : Window\r\n{\r\n    private static RoutedCommand AddCommand = new RoutedCommand();\r\n    \r\n    public WatchListViewModel viewModel = new WatchListViewModel();\r\n    \r\n    public WatchList()\r\n    {  \r\n        InitializeComponent();\r\n        this.CommandBindings.Add(new CommandBinding(AddCommand, ExecuteAddCommand, CanExecuteAddCommand));\r\n    }\r\n    \r\n    private void CanExecuteAddCommand(object sender, CanExecuteRoutedEventArgs e)\r\n    {\r\n        e.CanExecute = viewModel.CanAdd();\r\n    }\r\n    \r\n    private void ExecuteAddCommand(object sender, ExecutedRoutedEventArgs args)\r\n    {\r\n        viewModel.Add();\r\n    }\r\n}\r\n<\/pre>\n<p>Bindowanie w przycisku do komendy mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b (XAML)<\/p>\n<pre lang=\"xml\">\r\n<Button {x:Static local:WatchList.AddCommand}><\/Button>\r\n<\/pre>\n<p>gdzie <span style=\"font-style: italic;\">local <\/span>jest aliasem na namespace, w kt\u00f3rym znajduje si\u0119 okno.<\/p>\n<h3>4. Podsumowanie<\/h3>\n<p>U\u017cycie komend w pewnym stopniu oddzieli\u0142o nam widok od codebehind-a, jednak\u017ce wci\u0105\u017c nie jest to rozwi\u0105zanie idealne. Z codebehind-a wywo\u0142ujemy metody ViewModelu, wi\u0119c niejako ci\u0105gle jeste\u015bmy bezpo\u015brednio zale\u017cni od widoku. W cz\u0119\u015bci drugiej zaprezentuj\u0119 w jaki spos\u00f3b mo\u017cna stworzy\u0107 w\u0142asny obiekt implementuj\u0105cy interfejs <span style=\"font-style: italic;\">ICommand<\/span>, dzi\u0119ki czemu ograniczymy do minimum ilo\u015b\u0107 kodu pisanego w <span style=\"font-style: italic;\">codebehind<\/span>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Wst\u0119p Ka\u017cdy programista, kt\u00f3ry napisa\u0142 chocia\u017c par\u0119 linijek kody w WinFormsach wie, \u017ce kontrolki u\u017cywaj\u0105 event\u00f3w do powiadamiania o zmianach swojego stanu. W przypadku, gdy interesuje nas odpowiednia reakcja na zdarzenie, podpinamy si\u0119 do niego odpowiedni\u0105 funkcj\u0105 i wykonujemy za\u0142o\u017cone przez nas operacj\u0119. W analogiczny spos\u00f3b mo\u017cna post\u0119powa\u0107 w WPF-ie oraz w Silverlighcie, jednak\u017ce [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[155,152,154,45],"tags":[281,278,282,191],"class_list":["post-71","post","type-post","status-publish","format-standard","hentry","category-bindingcommand","category-commands","category-viewmodel","category-wpf","tag-bindingcommand","tag-commands","tag-viewmodel","tag-wpf"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/71","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=71"}],"version-history":[{"count":4,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/71\/revisions"}],"predecessor-version":[{"id":577,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/71\/revisions\/577"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=71"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=71"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=71"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}