Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
 ICommand.CanExecuted - параметр?  [new]
неделя_на_wpf
Guest
Всех привествую.
Есть treeview со следующим шаблоном элементов:

        <HierarchicalDataTemplate DataType="{x:Type local:DatabaseFolder}" ItemsSource="{Binding Path=Childs}">
            <StackPanel Orientation="Horizontal">
                <Image Height="16">
                    <Image.Source>
                        <BitmapImage UriSource="/Icons/iconfolder.bmp" />
                    </Image.Source>
                </Image>
                <TextBlock Text="{Binding Path=itemName}">
                    <TextBlock.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header = "Добавить подкаталог" Command="{x:Static local:WireCatalog.AddNewFolderCommand}" CommandParameter="{Binding}"></MenuItem>
                            <MenuItem Header = "Добавить МЦ из Windchill" Command="{x:Static local:WireCatalog.AddNewMCItemCommand}" CommandParameter="{Binding}"></MenuItem>
                            <MenuItem Header = "Удалить" Command="{x:Static local:WireCatalog.RemoveFolderCommand}"  CommandParameter="{Binding}"/>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:DatabaseMCItem}">
            <StackPanel Orientation="Horizontal">
                <Image Height="16">
                    <Image.Source>
                        <BitmapImage UriSource="/Icons/iconfile.bmp" />
                    </Image.Source>
                </Image>
                <TextBlock Text="{Binding Path=itemName}" />
            </StackPanel>
        </HierarchicalDataTemplate>


Команда удаления каталога (Command="{x:Static local:WireCatalog.RemoveFolderCommand}") должна работать лишь тогда, когда каталог пустой. соотвественно хочу проверить это в CanExecuted, написал примитивную проверку:
        public class RemoveFolderFromWireKatalog : ICommand
        {

            public bool CanExecute(object parameter)
            {
                return (parameter is DatabaseFolder) && (((DatabaseFolder)parameter).Childs.Count == 0);
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                throw new NotImplementedException();
            }
        }

но на входе у CanExecute постоянно null. Что я делаю не так?
и сразу второй вопрос - заметил, что wpf дёргает CanExecute в момент отрисовки элемента в дереве, а не в момент отображения контекстного меню, это нормальное поведение? Переползаю на впф с винформс, пока всё как-то в новинку)
10 янв 14, 15:56    [15397723]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
netivan
Member

Откуда:
Сообщений: 8768
неделя_на_wpf,

ну в момент отрисовки да null. А когда команду вызовите придет ваш item.
10 янв 14, 18:54    [15398846]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
неделя_на_wpf
Guest
netivan,
в Execute()-то итем придёт, но вот только поскольку в CanExecute() при отображении менюшки пришёл null -> CanExecute возвращает false -> пункт меню не активен -> до вызова дело не дойдёт вовсе)
Сама команда работала до тех пор, пока не убрал безвариантное return true в CanExecute() :)
10 янв 14, 23:27    [15400027]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6029
неделя_на_wpf,

проверьте DataContext у MenuItem'ов - он часто слетает, особенно в случае элементов, сгенерированных через DataTemplate: https://www.google.ru/search?q=wpf contextmenu datacontext is null
Может помочь привязка датаконтекста самого ContextMenu через DataContext PlacementTarget'а. Или можно использовать DataContextSpy .
11 янв 14, 05:04    [15400822]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
maratoss
Member

Откуда: от верблюда
Сообщений: 137
неделя_на_wpf,

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

После того как событие CanExecuteChanged сработает,
все подписчики вызовут CanExecute у команды и обновят UI в зависимости, от того что этот метод вернет.

поэтому можно сделать следующее:
<ContextMenu>
	<MenuItem Header = "Добавить подкаталог" Command="{x:Static local:WireCatalog.AddNewFolderCommand}" CommandParameter="{Binding}"></MenuItem>
	<MenuItem Header = "Добавить МЦ из Windchill" Command="{x:Static local:WireCatalog.AddNewMCItemCommand}" CommandParameter="{Binding}"></MenuItem>
	<MenuItem Header = "Удалить">
		<MenuItem.Command>
			<!-- 
				только тут есть одна проблема, биндить можно только к DependencyProperty
				поэтому тут нужно прибиндить либо через AttachProperty, либо через Behavior либо MarkupExtension
				
				ну или самое простое, команда должна отнаследовать класс DependencyObject
				и свойство DatabaseFolder - переделать в DependencyProperty
			-->
			<local:WireCatalog.RemoveFolderCommand DatabaseFolder="{Binding}" />
		</MenuItem.Command>
	</MenuItem>
</ContextMenu>


public class RemoveFolderFromWireKatalog : ICommand
{
	private DatabaseFolder _databaseFolder;
	public DatabaseFolder DatabaseFolder
	{ 
		get { return _databaseFolder; }
		set { _databaseFolder = value; OnDatabaseFolderChanged(); }
	}
	
	public bool CanExecute(object parameter)
	{
		return !DatabaseFolder.Childs.Any();
	}

	public event EventHandler CanExecuteChanged;

	public void Execute(object parameter)
	{
		throw new NotImplementedException();
	}
	
	private void OnDatabaseFolderChanged()
	{
		DatabaseFolder.CollectionChanged += (s, e) => CanExecuteChanged(this, EventArgs.Empty);
	}
}
12 янв 14, 20:20    [15405351]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
неделя_на_wpf
Guest
maratoss,

спасибо за помощь, но теперь получил null вообще везде)

<MenuItem Header = "Удалить">
                                <MenuItem.Command>
                                    <local:RemoveFolderFromWireKatalog DbFolder="{Binding}" />
                                </MenuItem.Command>
                            </MenuItem>


        public class RemoveFolderFromWireKatalog : DependencyObject, ICommand
        {

            public static readonly DependencyProperty DbFolderProperty = DependencyProperty.Register("DbFolder", typeof(DatabaseFolder), typeof(RemoveFolderFromWireKatalog));

            public DatabaseFolder DbFolder
            {
                set { SetValue(DbFolderProperty, value); OnDatabaseFolderChanged(); }
                get { return (DatabaseFolder)GetValue(DbFolderProperty); }
            }


            public bool CanExecute(object parameter)
            {
                //return !DatabaseFolder.Childs.Any();
                return true;
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
                //throw new NotImplementedException();
                MessageBox.Show(DbFolder.GetType().ToString()); // <----------- падаем (nullvalue)    
            }

            private void OnDatabaseFolderChanged()
            {
                DbFolder.Childs.CollectionChanged += (s, e) => CanExecuteChanged(this, EventArgs.Empty);
            }

        }
13 янв 14, 13:51    [15407970]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
maratoss
Member

Откуда: от верблюда
Сообщений: 137
неделя_на_wpf,

Оказывается что меню находится за пределами визуального дерева
ну и как писал Сон Веры Павловны установи DataContext через PlacementTarget

<ContextMenu DataContext="{Binding PlacementTarget.DataContext, 
	RelativeSource={RelativeSource Self}}" >


http://www.codeproject.com/Articles/162784/WPF-ContextMenu-Strikes-Again-DataContext-Not-Upda
13 янв 14, 15:52    [15408801]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
netivan
Member

Откуда:
Сообщений: 8768
maratoss,

интересно. Хотя обойти это можно используя нормальный MVVM и иметь SelectedItem в модели.
13 янв 14, 16:18    [15409030]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
неделя_на_wpf
Guest
maratoss,

DataContext в ContextMenu пробовал сразу устанавливать, но проблема вроде бы не в этом, ибо я опять вернулся к тому же самому. Ради интереса попробовал:
<MenuItem Header="Удалить" CommandParameter="{Binding}">
<MenuItem.Command>
<local:RemoveFolderFromWireCatalog DbFolder="{Binding}" />
</MenuItem.Command>
</MenuItem> 


в итоге в icommand.Execute получаю - parameter=DatabaseFolder (то есть привязка работает), а свойство команды DbFolder = null
Пока ковыряюсь с другим, решения так и не придумал
13 янв 14, 16:40    [15409200]     Ответить | Цитировать Сообщить модератору
 Re: ICommand.CanExecuted - параметр?  [new]
неделя_на_wpf
Guest
Взлетело с RoutedCommand - в CanExecute e.Parameter честно установлен. Спасибо тем, кто помогал.
15 янв 14, 13:52    [15418490]     Ответить | Цитировать Сообщить модератору
Все форумы / WPF, Silverlight Ответить