问题
Sometimes we used complex ways so many times, we forgot the simplest ways to do the task.
I know how to do command binding, but i always use same approach.
Create a class that implements ICommand interface and from the view model i create new instance of that class and binding works like a charm.
This is the code that i used for command binding
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
testCommand = new MeCommand(processor);
}
ICommand testCommand;
public ICommand test
{
get { return testCommand; }
}
public void processor()
{
MessageBox.Show("hello world");
}
}
public class MeCommand : ICommand
{
public delegate void ExecuteMethod();
private ExecuteMethod meth;
public MeCommand(ExecuteMethod exec)
{
meth = exec;
}
public bool CanExecute(object parameter)
{
return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
meth();
}
}
But i want to know the basic way to do this, no third party dll no new class creation. Do this simple command binding using a single class. Actual class implements from ICommand interface and do the work.
回答1:
Prism already provided Microsoft.Practices.Prism.Commands.DelegateCommand
I'm not sure is it considered as 3rd party. At least it's official and documented on MSDN.
Some native build-in commands such copy, paste implements ICommand interface. IMHO it following the Open(for extends)/Close(for changes) principle. so that we can implement our own command.
Update
As WPF Commanding documented here, an excerpt...
WPF provides a set of predefined commands. such as Cut, BrowseBack and BrowseForward, Play, Stop, and Pause.
If the commands in the command library classes do not meet your needs, then you can create your own commands. There are two ways to create a custom command. The first is to start from the ground up and implement the ICommand interface. The other way, and the more common approach, is to create a RoutedCommand or a RoutedUICommand.
I've tried RoutedCommand model at the beginning and ended up with implementing ICommand.
sample XAML binding
<CommandBinding Command="{x:Static custom:Window1.CustomRoutedCommand}"
Executed="ExecutedCustomCommand"
CanExecute="CanExecuteCustomCommand" />
RoutedCommand is not different from RoutedEvent. this seems like a better button's 'Clicked' event handler. It serves the purpose: To separate app logic from View but require some attach DependencyProperty or code-behind.
personally I feel more comfortable with just implement my ICommand.
回答2:
I tend to use the MVVM light framework which comes with a RelayCommand built in.
You add an ICommand property to your view model, then assign a relaycommand to it:-
ICommand ClickMeCommand {get;set;}
private void InitCommands()
{
ClickMeCommand = new RelayCommand(()=>HasBeenClicked=true);
//or
ClickMeCommand = new RelayCommand(ClickMeEvent);
}
public void ClickMeEvent()
{
HasBeenClicked=true;
}
In the xaml you just use normal binding:-
<Button Content='Push me' Command='{Binding ClickMeCommand}' />
回答3:
Please use routed command in case of you don't want to create new class. here is a small snippet. I have created a routed command as Save and bind it to command binding of window , raise the command from button.and voila!..... I hope this may help you
public partial class Window1 : Window
{
public static readonly RoutedCommand Foo = new RoutedCommand();
public Window1()
{
InitializeComponent();
}
void Foo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// The Window gets to determine if the Foo
// command can execute at this time.
e.CanExecute = true;
}
void Foo_Executed(object sender, ExecutedRoutedEventArgs e)
{
// The Window executes the command logic when the user wants to Foo.
MessageBox.Show("The Window is Fooing...");
}
}
<Window.CommandBindings>
<CommandBinding
Command="{x:Static local:Window1.Foo}"
CanExecute="Foo_CanExecute"
Executed="Foo_Executed"
/>
I hope I understood your problem well.
PS: The need of command design pattern is to decouple the logic of execution and invoker of command.So Command class is a way to encapsulate the logic.
回答4:
Judging from what you do without running the Code in VS, your problem is that you call InitializeComponent
(render XAML), then set the DataContext
without any value being in your test
Property and finally set a private member. How is the UI supposed to notice that your Command is not null anymore (which was the last value it found in the Property)?
Why not instanciate the Command lazily like so :
public ICommand test
{
get
{
if(testCommand== null)
{
testCommand= new MeCommand(processor);
}
return testCommand;
}
}
That way it will be there as soon as it's required and you don't need Change notification (unless you change that command later at runtime).
By the way, there's a few spots where you get the feeling no code is being executed in the Command Binding scenario :
1) CanExecute() returns false : just return true or evaluate according to your business case.
2) The conditions of CanExecute change, so that it would return true, but it doesn't get called: You can hook your Command up with the CommandManager class, see here. Make sure 1) is solved before you try this. This ensures that your UI will requery the CanExecute
pretty frequently, and if that is still not enough, call the Method CommandManager.InvalidateRequerySuggested()
explicitly.
3) Your Binding is wrong. Change the binding code like so:
Command="{Binding Path=test, PresentationTraceSources.TraceLevel=High}"
This will spam your output window whenever the value of the binding is pulled or pushed. Look out for the term "using final value". If it's null, your Command isn't where it's supposed to be (yet).
回答5:
Well, you almost have it. Just make sure that CanExecute() will return true, otherwise your code won't be executed. (I think it actually will, but still). Also make sure that add "NotifyPropertyChanged('yourCommand')" to
public ICommand test
{
get { return testCommand; }
set { testCOmmand = value; NotifyPropertyChanged("test"); }
}
to here.
You can alternatively do test = new MeCOmmand(); DataContext = this;
回答6:
In the WPF Window constructor, to link a keyboard shortcut, simply add a binding with a delegate, and associate it with a key gesture.
public YourWindow() //your constructor
{
...
//bind keyboard command shortcuts
InputBindings.Add(new KeyBinding( //add a new key-binding, bind it to your command object which takes a delegate
new WindowCommand(this)
{
ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
}, new KeyGesture(Key.P, ModifierKeys.Control)));
...
}
Create a simple WindowCommand class which takes an execution delegate to fire off any method set on it.
public class WindowCommand : ICommand
{
private MainWindow _window;
public Action ExecuteDelegate { get; set; }
public WindowCommand(MainWindow window)
{
_window = window;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (ExecuteDelegate != null)
{
ExecuteDelegate();
}
else
{
throw new InvalidOperationException();
}
}
}
来源:https://stackoverflow.com/questions/13838884/how-to-bind-a-command-in-wpf