{"id":70,"date":"2011-11-27T16:05:00","date_gmt":"2011-11-27T16:05:00","guid":{"rendered":"http:\/\/tpodolak.com.hostingasp.pl\/blog\/2011\/11\/27\/commands-czyli-interakcja-widoku-z-viewmodelem-cz-2\/"},"modified":"2016-01-31T00:43:19","modified_gmt":"2016-01-31T00:43:19","slug":"commands-czyli-interakcja-widoku-z-viewmodelem-cz-2","status":"publish","type":"post","link":"https:\/\/tpodolak.com\/blog\/2011\/11\/27\/commands-czyli-interakcja-widoku-z-viewmodelem-cz-2\/","title":{"rendered":"Commands &#8211; czyli interakcja widoku z ViewModelem cz.2"},"content":{"rendered":"<p>Tak jak wspomnia\u0142em w poprzednim wpisie, tym razem zajmiemy si\u0119 tworzeniem w\u0142asnej klasy implantuj\u0105cej interfejs <span style=\"font-style: italic;\">ICommand <\/span>.<\/p>\n<h3>1. Implementacja interfejsu <span style=\"font-style: italic;\">ICommand<\/span> &#8211; WPF <\/h3>\n<p>Klasa implementuj\u0105ca <span style=\"font-style: italic;\">ICommand <\/span>mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b<\/p>\n<pre lang=\"csharp\">\r\npublic class DelegateCommand : ICommand\r\n{\r\n    private Action<object> execute;\r\n    private Func<object, bool> canExecute;\r\n    \r\n    public event EventHandler CanExecuteChanged\r\n    {\r\n        add\r\n        {\r\n            CommandManager.RequerySuggested += value;\r\n        }\r\n        remove\r\n        {\r\n            CommandManager.RequerySuggested -= value;\r\n        }\r\n    }\r\n    \r\n    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)\r\n    {\r\n        this.execute = execute;\r\n        this.canExecute = canExecute;\r\n    }\r\n    \r\n    public bool CanExecute(object parameter)\r\n    {\r\n        if (canExecute == null) return true;\r\n        return canExecute(parameter);\r\n    }\r\n    \r\n    public void Execute(object parameter)\r\n    {\r\n        if (execute == null) return;\r\n        execute((object)parameter);\r\n    }\r\n}\r\n<\/pre>\n<p>Klasa <span style=\"font-style: italic;\">DelegateCommand <\/span>przyjmuje w konstruktorze dwa parametry &#8211; delegaty do funkcji. Pierwszy z nich jest to delegat do funkcji, kt\u00f3ra ma si\u0119 wykona\u0107, gdy komenda mo\u017ce zosta\u0107 wykonana.Drugi natomiast jest to delegat do funkcji, kt\u00f3ra sprawdza czy dana komenda mo\u017ce zosta\u0107 wykonana.Wa\u017cnym elementem jest tutaj nast\u0119puj\u0105cy kawa\u0142ek kodu :<\/p>\n<pre lang=\"csharp\">\r\npublic event EventHandler CanExecuteChanged\r\n{\r\n    add\r\n    {\r\n        CommandManager.RequerySuggested += value;\r\n    }\r\n    remove\r\n    {\r\n        CommandManager.RequerySuggested -= value;\r\n    }\r\n}\r\n<\/pre>\n<p>kt\u00f3ry pod\u0142\u0105cza nasz\u0105 klas\u0119 do WPF-owego systemu komend.<br \/>\nTeraz wystarczy w ViewModelu stworzy\u0107 obiekt typu <span style=\"font-style: italic;\">DelegateCommand <\/span>przekaza\u0107 do niego odpowiednie funkcje, a nast\u0119pnie zbindowa\u0107 go do widoku. Mo\u017ce to wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<ul>\n<li>ViewModel<\/li>\n<pre lang=\"csharp\">\r\npublic ICommand AddCommand { get; set;}\r\n\r\npublic WatchListViewModel()\r\n{\r\n    AddCommand = new DelegateCommand(val => Add(), val => CanAdd());\r\n}\r\n<\/pre>\n<li>Widok (XAML)<\/li>\n<pre lang=\"xml\">\r\n<Button Command=\"{Binding AddCommand}\"\/>\r\n<\/pre>\n<\/ul>\n<h3>2. Implementacja interfejsu <span style=\"font-style: italic;\">ICommand<\/span> Silverlight<\/h3>\n<p>Przedstawiony powy\u017cej przyk\u0142ad skompiluje si\u0119 jedynie w aplikacji WPF-owej. Silverlight niestety nie posiada <span style=\"font-style: italic;\">CommandManagera<\/span>, dlatego te\u017c gdy chcemy u\u017cywa\u0107 komend w\u0142a\u015bnie w tej technologii musimy zmodyfikowa\u0107 nasz kod na nast\u0119puj\u0105cy:<\/p>\n<pre lang=\"csharp\">\r\npublic class DelegateCommand : ICommand\r\n{\r\n    public event EventHandler CanExecuteChanged;\r\n    private Action<object> execute;\r\n    private Func<object, bool> canExecute;\r\n    private bool previousState;\r\n    \r\n    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)\r\n    {\r\n        this.execute = execute;\r\n        this.canExecute = canExecute;\r\n    }\r\n    \r\n    public DelegateCommand(Action<object> execute)\r\n    {\r\n        this.execute = execute;\r\n    }\r\n    \r\n    public bool CanExecute(object parameter)\r\n    {\r\n        if (canExecute == null) return true;\r\n        bool currentState = canExecute((object)parameter);\r\n        if (currentState != previousState)\r\n        {\r\n            previousState = currentState;\r\n            if (CanExecuteChanged != null)\r\n                CanExecuteChanged(this, EventArgs.Empty);\r\n            return currentState;\r\n        }\r\n        return currentState;\r\n    }\r\n    \r\n    public void Execute(object parameter)\r\n    {\r\n        if (execute == null) return;\r\n        execute((object)parameter);\r\n    }\r\n    \r\n    public void RaiseCanExecute()\r\n    {\r\n        if (CanExecuteChanged != null)\r\n            CanExecuteChanged(this, EventArgs.Empty);\r\n    }\r\n}\r\n<\/pre>\n<p>Jak wida\u0107 w funkcji<\/p>\n<pre lang=\"csharp\">\r\npublic bool CanExecute(object parameter)\r\n<\/pre>\n<p>sami musimy zadba\u0107 o wywo\u0142anie zdarzenia<\/p>\n<pre lang=\"csharp\">\r\npublic event EventHandler CanExecuteChanged;\r\n<\/pre>\n<p>Ponadto dopisana zosta\u0142a funkcja<\/p>\n<pre lang=\"csharp\">\r\npublic void RaiseCanExecute()\r\n<\/pre>\n<p>funkcj\u0119 t\u0105 musimy wywo\u0142ywa\u0107 zawsze gdy zmieni si\u0119 jaka\u015b w\u0142a\u015bciwo\u015b\u0107, kt\u00f3ra ma mie\u0107 wp\u0142yw na stan przycisku zbindowanego do naszej komendy.<\/p>\n<h3>3. Przyk\u0142ad<\/h3>\n<p>Za\u0142\u00f3\u017cmy,\u017ce na widoku mamy przycisk edytuj, kt\u00f3ry powinien by\u0107 aktywny tylko i wy\u0142\u0105cznie wtedy, gdy zaznaczono jaki\u015b element na gridzie. Przycisk ten jest zbindowany do komendy <span style=\"font-style: italic;\">EditCommand<\/span>. Jak ju\u017c wcze\u015bniej wspomniano, z powodu braku w Silverlighcie <span style=\"font-style: italic;\">CommandManagera<\/span> sami musimy wywo\u0142a\u0107 zdarzenie informuj\u0105ce o potrzebie zmiany stanu przycisku. Dlatego te\u017c, w ViewModelu zosta\u0142a stworzona w\u0142a\u015bciwo\u015b\u0107 <span style=\"font-style: italic;\">SelectedItem<\/span><\/p>\n<pre lang=\"csharp\">\r\npublic Department SelectedItem\r\n{\r\n    get { return selectedItem; }\r\n    set\r\n    {\r\n        selectedItem = value;\r\n        NotifyPropertyChanged(() => SelectedItem);\r\n        EditCommand.RaiseCanExecute();\r\n    }\r\n}\r\n<\/pre>\n<p>kt\u00f3ra reaguje na zmiany zaznaczenia na gridzie. W chwili, gdy zaznaczenie si\u0119 zmienia, wywo\u0142ywany jest seter z tej w\u0142a\u015bciwo\u015bci, w kt\u00f3rym to wywo\u0142ujemy funkcj\u0119 <span style=\"font-style: italic;\">EditCommand.RaiseCanExecute()<\/span>. Dzi\u0119ki czemu informujemy kontrolk\u0119 o ewentualnej potrzebie zmiany jej stanu.<\/p>\n<h3>4. Podsumowanie<\/h3>\n<p>Komendy w bardzo prosty i wygodny spos\u00f3b pozwalaj\u0105 nam wywo\u0142ywa\u0107 metody bezpo\u015brednio z ViewModelu. Niestety maj\u0105 one r\u00f3wnie\u017c swoje ograniczenia:<\/p>\n<ul>\n<li>reaguj\u0105 jedynie na zdarzenie <span style=\"font-style: italic;\">Click<\/span>,<\/li>\n<li>mo\u017cna je wykorzysta\u0107 jedynie na kontrolkach dziedzicz\u0105cych po klasie <span style=\"font-style: italic;\">ButtonBase<\/span> oraz kontrolkach typu <span style=\"font-style: italic;\">MenuItem<\/span><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Tak jak wspomnia\u0142em w poprzednim wpisie, tym razem zajmiemy si\u0119 tworzeniem w\u0142asnej klasy implantuj\u0105cej interfejs ICommand . 1. Implementacja interfejsu ICommand &#8211; WPF Klasa implementuj\u0105ca ICommand mo\u017ce wygl\u0105da\u0107 w nast\u0119puj\u0105cy spos\u00f3b public class DelegateCommand : ICommand { private Action execute; private Func canExecute; public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[152,151,153,44,45],"tags":[278,279,280,190,191],"class_list":["post-70","post","type-post","status-publish","format-standard","hentry","category-commands","category-commanmanager","category-icommand","category-silverlight","category-wpf","tag-commands","tag-commanmanager","tag-icommand","tag-silverlight","tag-wpf"],"_links":{"self":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/70","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=70"}],"version-history":[{"count":5,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/70\/revisions"}],"predecessor-version":[{"id":576,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/posts\/70\/revisions\/576"}],"wp:attachment":[{"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/media?parent=70"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/categories?post=70"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tpodolak.com\/blog\/wp-json\/wp\/v2\/tags?post=70"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}