Благодарим за ответы и разъяснения!
Ты дал мне решающий удар, поэтому я понял это. Я добавил свои примеры на purpouse.
Следуя вашим советам (Ron B I & Dennis) Я сначала хотел узнать больше о ViewModel.
Под http://msdn.microsoft.com/en-ca/magazine/dd419663.aspx есть примеры с нестационарным классом. Таким образом, решение было просто добавить новый класс в моем пользовательском элементе управления (точно, как показано на упомянутом сайте - Рисунок 3 - некоторые имя изменено - Copyright принадлежит Джошу Смиту joshsmithonwpf.wordpress.com):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace WpfCommandControl
{
class CommandImplementation : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public CommandImplementation(Action<object> execute)
: this(execute, null)
{
}
public CommandImplementation(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
}
Тогда в Контроль пользователя "окно"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace WpfCommandControl
{
public partial class CommandControl : UserControl, INotifyPropertyChanged
{
#region [ Private Members ]
private bool _canActivated = false;
private int _counter = 0;
CommandImplementation _activateCommand;
#endregion
#region [ Properties ]
public int CommandCounter
{
get
{
return _counter;
}
set
{
_counter = value;
OnNotifyPropertyChanged("CommandCounter");
}
}
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
#endregion
#region [ Property_Changed_Utilities ]
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(String info)
{
// Note: Do not forget to add interface "INotifyPropertyChanged" to your class.
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
# region [ Commands ]
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
#endregion
#region [ Constructor ]
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
#endregion
#region [ Methods ]
void Activate()
{
CommandCounter++;
}
#endregion
}
}
Наиболее важная часть:
Команда реализована в собственности:
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
Так убеждается он возвращает фактический экземпляр, связанный команды, который был экземпляр с лямбда-выражения в конструкторе управления пользователя:
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
лямбда - выражение делает подключение к логической begind:
param => this.Activate()
Для Activate() функции которым будет выполняться как команда обжигают
void Activate()
{
CommandCounter++;
}
И
param => this.CanActivated
Для передачи локальной логики для свойства ICommand CanExecute, что дает вам возможность контролировать, когда команда может быть выполнена.
В моем случае я использовал свойство, которое можно связать с флажком, но вы также можете сделать это по-другому ...
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
Опять же, как показано с Джошем Смитом joshsmithonwpf.wordpress.com - Я просто изменил его, чтобы создать экземпляр в конструкторе, а не проверять, является ли частный член нулевым и доставляет новый экземпляр, если это необходимо в части GET свойства Command.
Остальная часть кода - это просто реализация необходимых свойств и OnNotifyPropertyChanged, как показано на MSDN.
XAML прост - только для доказательства концепции.
<UserControl x:Class="WpfCommandControl.CommandControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfCommandControl"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<StackPanel>
<CheckBox Content="Activate" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl},
Path=ActivateCommand}"
Content="Click me"
IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl},
Path=CanActivated}" />
<Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CommandCounter}" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
</StackPanel>
</Grid>
Как вы можете видеть, что есть только CheckBox - Binding обеспечит включение/отключение кнопки. Нажмите кнопку «Кнопка», чтобы вызвать команду, которая просто увеличит счетчик - на ярлыке снова появится привязка.
Собираем все вместе:
Просто один простой XAML формы с четырьмя пользовательских элементов управления:
<Window x:Class="CommandsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommandsTest"
xmlns:uctrl="clr-namespace:WpfCommandControl;assembly=WpfCommandControl"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<uctrl:CommandControl Grid.Row="0" Grid.Column="0" />
<uctrl:CommandControl Grid.Row="0" Grid.Column="1" />
<uctrl:CommandControl Grid.Row="1" Grid.Column="0" />
<uctrl:CommandControl Grid.Row="1" Grid.Column="1" />
</Grid>
розжига команды на каждый элемент управления точно так, как необходимо внутри элемента.
Все решается в способе WPF - с использованием команд и привязок без прямого взаимодействия с элементами GUI, поэтому графический интерфейс можно обменивать без необходимости обновления кода.
Еще раз благодарим вас за то, что вы показали мне, что есть еще один (безопасный) способ реализовать пользовательские команды в WPF.
Я думаю, что проблемы будут похожи на свойства зависимостей: http://stackoverflow.com/questions/3660696/non-static-dependency-properties –
Я думаю, что вы неправильно понимаете команды. Нет ничего, что создавало бы команды, связанные со статическими классами. – Euphoric
@Euphoric: документация MSDN, связанная с командами, действительно отстойная, потому что она заполнена такими образцами (когда команды находятся в статическом классе). В то же время подход MVVM, который является основным для WPF, предполагает, что эта команда является частью модели представления. Я понимаю, почему этот вопрос появился. – Dennis