Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/7: Рейтинг темы: голосов - 7, средняя оценка - 5.00
879 / 558 / 291
Регистрация: 21.11.2012
Сообщений: 1,553
1

CanExecute команды и привязка через Interaction

25.02.2020, 18:35. Показов 1412. Ответов 16
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
есть команда, которая устанавливает в переменную какое-то значение
C#
1
2
3
4
5
6
7
8
9
10
11
12
        private ICommand _TestCommand;
 
        public ICommand TestCommand
        {
            get
            {
                if (_TestCommand == null) _TestCommand = new RelayCommand(x => MyProp = "Hallo, World!", y => CanExecuteHalloWorld());
                return _TestCommand;
            }
        }
 
       private bool CanExecuteHalloWorld(){...}
следующая привязка работает отлично и если метод CanExecuteHalloWorld возвращает false, кнопка будет деактивирована

XML
1
<Button Content="test" Command="{Binding TestCommand}"/>
как сделать так или можно ли вообще, чтобы в следующей привязке кнопка также деактивировалась?:

XML
1
2
3
4
5
6
7
8
<Button Content="test">
    <i:Interaction.Triggers>
           <i:EventTrigger EventName="Click">
                 <i:InvokeCommandAction  Command="{Binding TestCommand}" >
                 </i:InvokeCommandAction>
           </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.02.2020, 18:35
Ответы с готовыми решениями:

Привязка команды к менюшке
Есть менюшка: &lt;Menu&gt; &lt;Menu.CommandBindings&gt; &lt;CommandBinding...

Привязка команды к элементам TreeView
Доброго времени суток. Есть treeview с привязкой к датасету из БД, так вот хочу привязать команду к...

Привязка команды во View к другой ViewModel
Есть View. У нее есть ViewModel. Нужно при нажатии кнопки вызвать команду из другой ViewModel. Как...

Привязка команды к кнопке внутри ListBox WPF
Есть разметка Xaml: &lt;Window x:Class=&quot;WpfApp1.MainWindow&quot; ...

16
Эксперт .NET
1838 / 1346 / 427
Регистрация: 10.06.2011
Сообщений: 2,126
25.02.2020, 18:56 2
Цитата Сообщение от hamin Посмотреть сообщение
как сделать так или можно ли вообще, чтобы в следующей привязке кнопка также деактивировалась?
Зачем, если вариант сверху работает?
0
879 / 558 / 291
Регистрация: 21.11.2012
Сообщений: 1,553
25.02.2020, 19:48  [ТС] 3
novikov.ea,

проблема в том, что у меня есть некая дефолтная команда, которая должна вызываться в любом случае после этой команды,т.е.

XML
1
2
3
4
5
6
7
8
<Button Content="test" Command="{Binding DefaultCommand}">
    <i:Interaction.Triggers>
           <i:EventTrigger EventName="Click">
                 <i:InvokeCommandAction  Command="{Binding TestCommand}" >
                 </i:InvokeCommandAction>
           </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>
таким макаром все вызывается, но не работает деактивация, а если меняю привязки местами деактивация работает, но дефолтная вызывается перед основной что меня не устраивает

Добавлено через 3 минуты
Цитата Сообщение от novikov.ea Посмотреть сообщение
Зачем, если вариант сверху работает?
ну и еще если вешать команду на какое-то другое событие
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
25.02.2020, 19:52 4
Цитата Сообщение от hamin Посмотреть сообщение
как сделать так или можно ли вообще, чтобы в следующей привязке кнопка также деактивировалась?:
Для события Click - это не имеет смысла.
Для другого же события, откуда элемент (в частности кнопка) может знать, что своё свойство IsEnabled он должен привязать к CanExecute команды выполняющейся по неизвестно какому событию?

Надо же элементу это как-то "объяснить".
Самый простой способ - простая привязка к свойству опращивающему CanExecute команды. Но при изменении состоянии команды, надо кидать, соответсвующий, PropertyChanged.

Если задача частая для вас, то можно расширить функционал типа реализующего ICommand добавив в него такое свойство и интерфейс INPC. Внутри типа, по событию CanExecuteChanged, кидать сразу и PropertyChanged.

Добавлено через 2 минуты
Цитата Сообщение от hamin Посмотреть сообщение
проблема в том, что у меня есть некая дефолтная команда, которая должна вызываться в любом случае после этой команды,т.е.
Если команда DefaultCommand "знает" о команде TestCommand, то в своём CanExecute она может проверять CanExecute команды TestCommand.
1
Эксперт .NET
1838 / 1346 / 427
Регистрация: 10.06.2011
Сообщений: 2,126
25.02.2020, 21:35 5
Цитата Сообщение от hamin Посмотреть сообщение
проблема в том, что у меня есть некая дефолтная команда, которая должна вызываться в любом случае после этой команды
Я думаю, что не стоит описывать привязки команд таким способом. Почему бы не сделать одну команду и просто привязывать её к кнопке? А в коде ViewModel задать правило для CanExecute? Я думаю, что это было бы проще.

Цитата Сообщение от hamin Посмотреть сообщение
ну и еще если вешать команду на какое-то другое событие
Это конкретный случай в вашей программе? Или просто задумка "на будущее". Если второе, то не следует заниматься оверинжинирингом. Если первое, то не совсем понятно, зачем кнопке дизеблиться на другие события, кроме клика?
1
879 / 558 / 291
Регистрация: 21.11.2012
Сообщений: 1,553
26.02.2020, 11:21  [ТС] 6
Цитата Сообщение от novikov.ea Посмотреть сообщение
Это конкретный случай в вашей программе? Или просто задумка "на будущее". Если второе, то не следует заниматься оверинжинирингом. Если первое, то не совсем понятно, зачем кнопке дизеблиться на другие события, кроме клика?
скажем так, иногда можно подцепить команду на событие previewMouseLeftButtonDown какого-то контрола и бывают ситуации, когда этот контрол должен дизеблиться. Такое работать не будет, как я понял. И нужно решать это другими способами, а жаль.

свою проблему решил с помощью 1 команды и нормальной привязки. всем спасибо за ответы
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
26.02.2020, 12:26 7
Цитата Сообщение от hamin Посмотреть сообщение
свою проблему решил с помощью 1 команды и нормальной привязки. всем спасибо за ответы
Можно заменить <i:Interaction.Triggers> <i:EventTrigger ... на свой Behavior.
В котором делать подписку команды на событие, и подписку IsEnabled элемента на CanExecuteChanged команды.
1
879 / 558 / 291
Регистрация: 21.11.2012
Сообщений: 1,553
26.02.2020, 14:16  [ТС] 8
Цитата Сообщение от Элд Хасп Посмотреть сообщение
В котором делать подписку команды на событие, и подписку IsEnabled элемента на CanExecuteChanged команды.
как вариант можно и так, но все-таки красивее таки создать отдельное свойство и привязать его к IsEnabled. Я просто думал есть какое-нибудь стандартное решение без изобретения велосипедов
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
26.02.2020, 14:55 9
Цитата Сообщение от hamin Посмотреть сообщение
как вариант можно и так, но все-таки красивее таки создать отдельное свойство и привязать его к IsEnabled. Я просто думал есть какое-нибудь стандартное решение без изобретения велосипедов
Нет - стандартного не будет.
Это, как уже написал novikov.ea, очень не типично для WPF решений.

Даже просто проверка по событию CanExecute не снимает всех вопросов, так как для CanExecute нужен параметр. И одна и та же команды может выполняться с разными параметрами.

Можно сделать универсальный конвертер через который биндить IsEnabled к команде и параметру
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    /// <summary>Конвертер проверяющий состояние команды</summary>
    public class IsCanExecuteCommandConverter : IMultiValueConverter
    {
        /// <summary>Возвращает состояние команды</summary>
        /// <param name="values">В первом элементе - команда, во втором - параметр команды. 
        /// Если нет второго параметра, то проверяется для <see langword="null"/></param>
        /// <param name="targetType">Не используется</param>
        /// <param name="parameter">Не используется</param>
        /// <param name="culture">Не используется</param>
        /// <returns>Состояние командя для указанног параметра</returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length < 1 || !(values[0] is ICommand command))
                return false;
 
            if (values.Length < 2)
                return command.CanExecute(null);
            return command.CanExecute(values[1]);
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
0
879 / 558 / 291
Регистрация: 21.11.2012
Сообщений: 1,553
26.02.2020, 15:19  [ТС] 10
Элд Хасп,

а если параметр в canExecute не является параметром команды? т.е.

C#
1
mycommand = new RelayCommand(x=> DoIt(), y=> CanDoIt("Hallo, World"));
как в таком случае конвертер себя поведет?)
0
1577 / 583 / 183
Регистрация: 05.12.2015
Сообщений: 935
26.02.2020, 15:24 11
Цитата Сообщение от hamin Посмотреть сообщение
Я просто думал есть какое-нибудь стандартное решение без изобретения велосипедов
можно просто привязать сколько угодно команд через MultiBinding.
выполняться будут по очереди, в том числе и CanExecute для каждой команды
XML
1
2
3
4
5
6
7
8
            <MenuItem Header="multi">
                <MenuItem.Command>
                    <MultiBinding Converter="{StaticResource multiConv}">
                        <Binding Path="startComm"/>
                        <Binding Path="pauseComm"/>
                    </MultiBinding>
                </MenuItem.Command>
            </MenuItem>
конвертер:
C#
1
2
3
4
5
6
7
8
9
10
11
12
 class MultiCommConverter : IMultiValueConverter
    {
        public object Convert( object [] values, Type targetType, object parameter, CultureInfo culture )
        {
            return new MultiCommand( values );
        }
 
        public object [] ConvertBack( object value, Type [] targetTypes, object parameter, CultureInfo culture )
        {
            throw new NotImplementedException();
        }
    }
команда- обертка:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    class MultiCommand : RelayCommand
    {
        List<RelayCommand> commList;
        public MultiCommand( object [] values )
        {
            commList = new List<RelayCommand>( values.OfType<RelayCommand>() );
        }
        public override bool CanExecute( object param = null )
        {
            foreach ( RelayCommand comm in commList )
                if ( !comm.CanExecute( param ) )
                    return false;
            return true;
        }
        public override void Execute( object param = null )
        {
            foreach ( RelayCommand comm in commList )
                comm.Execute( param );
        }
    }
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
26.02.2020, 15:33 12
Цитата Сообщение от hamin Посмотреть сообщение
как в таком случае конвертер себя поведет?)
Такая проверка срабатывает независимо от значения параметра.
Ведь в лямбде вы просто отбрасываете параметр y=> CanDoIt("Hallo, World") - y не испльзуется.
Для такой команды можно привязать любой параметр или, вообще, не задавать его.
Тогда будет передан null - прочитайте комментарии в коде.

Можно добавить второй конвертер, если параметр не нужен.
Такой конвертер будет проще использовать в XAML, но возникает проблема: Когда проверять привязку?
Мультиконвертер проверяет при изменении привязанной команды или её параметра.
Но если состояние команды зависит от данных, то WPF просто не узнает что надо обновить привязку.

Вот в вашем примере когда надо вызывать y=> CanDoIt("Hallo, World")?
В WPF+FW, в реализации Новая реализация RelayCommand с исправлениями от proa33 и kolorotur [WPF, Элд Хасп], состояние команды проверяется при любых изменениях в окне. Это происходит очень часто. Попробуйте поставить в отладке в методе точку останова. Вы даже выйти из отладчика не сможете. Вызовы будут следовать один за другим.

В Core уже такая реализация RelayCommand работатьне будет. Там надо явно вызывать метод Invalidate().

В общем, "чем дальше в лес...".
Нужные подробности реализации, чтобы понять какой функционал должен быть для конкретной реализации.
0
Эксперт .NET
1838 / 1346 / 427
Регистрация: 10.06.2011
Сообщений: 2,126
26.02.2020, 15:35 13
proa33, есть упущение: данный MultiCommand не отслеживает CanExecuteChanged у декорируемых команд. Следует подправить реализацию.
Но вообще, чем проще - тем лучше. Проще и очевиднее иметь одну команду на каждое событие.
0
1577 / 583 / 183
Регистрация: 05.12.2015
Сообщений: 935
26.02.2020, 15:40 14
novikov.ea, понятно, что это базовая, минимальная реализация.
тут главное, что все под контролем - вызовы методов и параметры.
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
26.02.2020, 15:44 15
hamin, вот вариант конвертера который можно использовать и как мультиконвертер, и как простой конвертер без передачи команды.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    /// <summary>Конвертер проверяющий состояние команды</summary>
    /// <remarks>Mожет использоваться как мультиконверер с передачей параметра или без, 
    /// и как простой конвертер с привязкой только к команде</remarks>
    public class IsCanExecuteCommandConverter : IMultiValueConverter, IValueConverter
    {
        /// <summary>Возвращает состояние команды</summary>
        /// <param name="values">В первом элементе - команда, во втором - параметр команды. 
        /// Если нет второго параметра, то проверяется для <see langword="null"/></param>
        /// <param name="targetType">Не используется</param>
        /// <param name="parameter">Не используется</param>
        /// <param name="culture">Не используется</param>
        /// <returns>Состояние командя для указанног параметра</returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length < 1 || !(values[0] is ICommand command))
                return false;
 
            if (values.Length < 2)
                return command.CanExecute(null);
            return command.CanExecute(values[1]);
        }
 
        /// <summary>Возвращает состояние команды</summary>
        /// <param name="value">Команда</param>
        /// <param name="targetType">Не используется</param>
        /// <param name="parameter">Не используется</param>
        /// <param name="culture">Не используется</param>
        /// <returns>Состояние командя для параметра = <see langword="null"/></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || !(value is ICommand command))
                return false;
                return command.CanExecute(null);
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
Добавлено через 4 минуты
Цитата Сообщение от novikov.ea Посмотреть сообщение
есть упущение: данный MultiCommand не отслеживает CanExecuteChanged у декорируемых команд
Да, так и есть.
Но не пришло в голову общего решения, для универсального конвертера.
Это же надо подписываться на все команды, а конвертер не знает на какую команду он уже подписался.
И куда потом кидать PropertyChanged? Как определять, что можно уже отписаться от конкретного экземпляра команды.
Целая куча вопросов.

При использовании Behavior это всё разрешимо.
0
1577 / 583 / 183
Регистрация: 05.12.2015
Сообщений: 935
26.02.2020, 15:56 16
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Целая куча вопросов.
но есть простой способ прверки - работает данная реализация в КОНКРЕТНОМ случае для КОНКРЕТНОЙ задачи или нет?
если все устраивает и работает, то зачем эта "куча вопросов", решение который окажется бесполезным?
0
Модератор
Эксперт .NET
15466 / 10712 / 2786
Регистрация: 21.04.2018
Сообщений: 31,531
Записей в блоге: 2
26.02.2020, 16:05 17
Цитата Сообщение от proa33 Посмотреть сообщение
но есть простой способ прверки - работает данная реализация в КОНКРЕТНОМ случае для КОНКРЕТНОЙ задачи или нет?
если все устраивает и работает, то зачем эта "куча вопросов", решение который окажется бесполезным?
Вариант для проверки уже дали и вы, и я.

Пусть hamin, проверит, если оно ему нужно.
А вопросы, возникли из-за моего размышления о общей реализации "на все случаи жизни".
0
26.02.2020, 16:05
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
26.02.2020, 16:05
Помогаю со студенческими работами здесь

Привязка команды для элемента контекстное меню TreeView
Имеется дерево. Как сделать разное контекстное меню для элемента разобрался. Команда тоже...

Спам CanExecute в RelayCommand
Добрый день, я думал что CanExecute выполняется по клику или еще какому-то действую над объектом,...

CanExecute или как обновить команду
Хай Есть кнопка, которую можно нажать только тогда когда дерево treeview построено. Я его строю в...

Creating ActionScripted Animation and Interaction
доброе время суток) друзья, подскажите, как правильно написать код в классах на АС3 чтобы создать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru