I found this: Close Window from ViewModel which gets me started down the path of modifying my DelegateCommand class to handle parameters. But I am not able to get the syntax worked out.
Here is my DelegateCommand class, and the DelegateCommand class that I'm trying to create with little success:
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}
public class DelegateCommand<T> : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}
And here is what I do in the viewmodel:
public ICommand RowEditEndingAction
{
get { return new DelegateCommand(RowEditEnding); }
}
public ICommand UpdateDatabaseClick
{
get { return new DelegateCommand<object>(UpdateDatabase); } //THIS LINE HAS THE ERROR
}
And the actual method that will get called:
public void UpdateDatabase(object parameter)
{
Window w = (Window)parameter;
// A bunch of stuff that works is removed for brevity
w.Close();
}
The compiler does not like my UpdateDatabaseClick, specifically saying there is something wrong with the arguments to DelegateCommand, but I am at a loss as to what I am doing wrong (though I am thinking it is syntax . . . ). What do I need to change? This all worked before I added the parameter to UpdateDatabase, and just had the DelegateCommand (no template class). But in that case I could not close the window.
Here's the constructor of your DelegateCommand<T> class that you are calling:
public DelegateCommand(Action action)
And here is how you call it:
new DelegateCommand<object>(UpdateDatabase)
In there, UpdateDatabase is declared as follows:
public void UpdateDatabase(object parameter)
Now, the constructor you are invoking expects an Action. That is a parameterless method without a return value.
However, you are passing in a method with one parameter. That is what the compiler complains about.
What you actually probably want to do is to accept any method with one parameter - for that, you can use the type Action<T>. As your parameter should presumeably have the same type that is passed as a type argument to your DelegateCommand<T> class, you can declare your constructor like this:
public DelegateCommand(Action<T> action)
Now, you also need to update the type of your backing field where you store the action:
private readonly Action<T> _action;
Lastly, as _action now expects an argument, you need to pass that argument when invoking _action in DelegateCommand<T>.Execute. Normally, you want to hand over the parameter object you receive as an argument to the Execute method. However, that value is always typed to object, whereas you want to work with a strongly-typed value of type T in your method. Therefore, you will also have to add an additional cast:
public void Execute(object parameter)
{
_action((T)parameter);
}
I suggest you try Prism framework. It has all the component, tools, you need to work with WPF application with MVVM model.
Related
In general, I want to know, what is the effect of the event keyword behind the action and what is the difference between it and action without event and what feature does it add?
Hint: Please answer this question like this, because I did not find exactly that.
public event Action action; // event action
public Action action; // action
When you declare public Action action; you have a public delegate. It can be reassigned anywhere, which may be dangerous. When you declare public event Action action; you have a class with a hidden delegate field, and the class exposes only some functionality of delegates; in particular you can only attach and detach methods to it using += and -=, but not outright reassign using =. To clarify what this means: suppose you have the following code:
using System;
class Worker
{
public Action action;
public void DoWork() {
action();
}
}
static class Test
{
public static void Foo() {
Console.WriteLine("Foo!");
}
public static void Main() {
Worker w = new Worker();
w.action = Foo;
w.DoWork();
}
}
The class Worker has a public delegate named action. In Main we create a new instance w and make its action point to the method Foo. Later we may reassign it by saying something like w.action = Bar. This may be seen as dangerous.
If the field action of class Worker were defined instead as event, i.e. public event Action action; then a direct assignment such as w.action = Foo would be illegal. Instead we can attach a particular handler to the event using +=. The following then works:
using System;
class Worker
{
public event Action action;
public void DoWork() {
action();
}
}
static class Test
{
public static void Foo() {
Console.WriteLine("Foo!");
}
public static void Main() {
Worker w = new Worker();
w.action += Foo;
w.DoWork();
}
}
I wanted to implement a custom WPF command and I searched and found the following code:
public static class CustomCommands
{
public static readonly RoutedUICommand Exit = new RoutedUICommand
(
"Exit",
"Exit",
typeof(CustomCommands),
new InputGestureCollection()
{
new KeyGesture(Key.F4, ModifierKeys.Alt)
}
);
//Define more commands here, just like the one above
}
There are two things that I can't figure out.
Is it necessary to have commands static readonly? Cant we just declare it using const?
What exactly new InputGestureCollection() { new KeyGesture(Key.F4, ModifierKeys.Alt) } is? If it is calling default constructor and initializing properties so there should be a property to be assigned to, but there is nothing to be assigned. InputGestureCollection has braces, but inside braces it is not initialing any properties. How? What is this type of statement?
First of all, you need to get some basic understanding of WPF with MVVM.
You have a class that you are going to bind to your UI. That class is not the .xaml.cs
It is completely independent of the View. You need to put an instance of the class into the DataContext of the Window you can do this in the .xaml.cs by calling sth like that:
this.DataContext = new MyViewModel();
Now your class MyViewModel needs a Property of type ICommand.
Best practice is to make a class that implements ICommand. Normally you call it DelegateCommand or RelayCommand.
Example:
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
Then in your ViewModel you create a property with an Instance of that class in it. Like that:
public class MyViewModel{
public DelegateCommand AddFolderCommand { get; set; }
public MyViewModel(ExplorerViewModel explorer)
{
AddFolderCommand = new DelegateCommand(ExecuteAddFolderCommand, (x) => true);
}
public void ExecuteAddFolderCommand(object param)
{
MessageBox.Show("this will be executed on button click later");
}
}
In your view you can now bind the command of a button to that property.
<Button Content="MyTestButton" Command="{Binding AddFolderCommand}" />
A routed command is something that already exists in the framework by default (copy, paste etc). If you're a beginner to MVVM you shouldn't be thinking of creating routed commands before you get the basic understanding of "normal" commands.
To answer your first question: It is absolutely not nessesary to make Commands static and/or const. (See class MyViewModel)
Your 2nd question: You can initialize Lists with default values that you put into the {-brackets.
Example:
var Foo = new List<string>(){ "Asdf", "Asdf2"};
You don't have a object you initialize properties of. You have a list you initialize and then the Add() is called with the parameters you put in the {-brackets.
That's what basicly happens in your case too. You have a collection that you initialize with some values.
To answer your second question:
new InputGestureCollection()
{
new KeyGesture(Key.F4, ModifierKeys.Alt)
}
This is an example of a collection initializer and is equivalent to:
var collection = new InputGestureCollection();
collection.Add(new KeyGesture(Key.F4, ModifierKeys.Alt));
It's just a shorthand, and something that ReSharper suggests.
I was primarily following this tutorial: http://www.codeproject.com/Articles/238657/How-to-use-Commands-in-WPF
But then I realize the RelayCommand is part of another framework that I can't use. This is the code I have:
public ICommand TestCommand
{
get;
internal set;
}
private bool CanExecuteTestCommand()
{
return !string.IsNullOrEmpty(txtUsername);
}
private void CreateTestCommand()
{
TestCommand = new TestCommand(TestExecute, CanExecuteTestCommand);
}
public void TestExecute(object parameter)
{
obj.TestConnection();
}
And the XAML:
<Button Content="Test Connection" Command="{Binding Path=TestConCmd}" />
But this won't compile because TestCommand is, obviously, an invalid type.
I've looked over this tutorial as well:
http://www.codeproject.com/Articles/274982/Commands-in-MVVM
But similarly, Command doesn't seem to be a type even though I've added using System.Windows.Input.
Then all the other tutorials I've looked at just use built-in commands like closing the application, pasting from the clipboard and a few other things like that.
So... How do I actually create my command?
Command is not a type, ICommand is. You must derive from/implement it:
public class TestCommand : ICommand
{
public override void Execute(object parameter)
{
//Do stuff
}
}
And subsequently implement the methods, especially Execute(object parameter). Then you can do:
TestCommand = new TestCommand();
in your View Model as before. Of course, you can re-implement RelayCommand or something like it. Is Josh Smith's implementation of the RelayCommand flawed? Shows some code and easy to make mistakes.
I want to create a special class of Command (I've called it DropDownRelayCommand, which are to be used on a dropdown button which has a content which consists of buttons with command.
The idea is that the dropdown button should be disabled (or possibly collapsed) if no buttons in it's content are enabled. I want to create a general solution.
I have been unable to do this in XAML (in a general way, anyway).
I want this DropDownRelayCommand to have it's own CanExecute methods, which should be examiing all CanExecute of its containing commands. The problem is this error (on the marked argument in the code below) : An object reference is required for the non-static field, method, or property 'Js.Mvvm.DropDownRelayCommand.CanDropDownExecute(T)'.
I cannot use "this" because that is not allowed in a constructor.
Here's my code (I am aware that the containing commands will propably be recieving canexecute request, making this a bit ineffective. But typically I would have only a handful commands so performance is propably not so important:
public class DropDownRelayCommand<T> : RelayCommand<T>
{
private readonly List<RelayCommand<T>> _commands = new List<RelayCommand<T>>();
private bool _hasAnyCanExecute;
#region Constructors
public DropDownRelayCommand(Action<T> execute, params RelayCommand<T>[] commands)
: base(execute, **CanDropDownExecute**)
{
AddCommandRange(commands);
}
public bool CanDropDownExecute(T argument)
{
_hasAnyCanExecute = false;
foreach (RelayCommand<T> command in _commands)
{
_hasAnyCanExecute = _hasAnyCanExecute || command.CanExecute(argument);
}
return _hasAnyCanExecute;
}
#endregion
#region Public Methods
public void AddCommandRange(params RelayCommand<T>[] commands)
{
if (commands == null) return;
foreach (RelayCommand<T> command in commands)
{
_commands.Add(command);
}
}
public void RemoveCommandRange(params RelayCommand<T>[] commands)
{
if (commands == null) return;
foreach (RelayCommand<T> command in commands)
{
if (_commands.Contains(command))
{
_commands.Remove(command);
}
}
}
#endregion
}
I have a closecommand defined inside my viewmodel for my dialog window. I have another command defined inside that viewmodel. Now I have that command binded to a control in my view. After performing certain command actions, I want it to call closecommand to close the window. Is that possible?
Yes. You can use a CompositeCommand that wraps both (or any number) of your other commands. I believe this is in Prism, but if you don't have access to that in your project, it isn't terribly difficult to implement similar functionality on your own, especially if you're not using parameters - all you do is implement ICommand with a class and then have a private List of ICommands inside the class.
Here's more on the CompositeCommand class from Prism:
http://msdn.microsoft.com/en-us/library/microsoft.practices.composite.presentation.commands.compositecommand_members.aspx
My own admittedly short and possibly non-canonical implementation follows. To use it, all you need to do is have this be referenced on your VM, and then bind to it instead. You can call .AddCommand for all the other commands that you want to run. Probably the Prism one is implemented differently, but I believe this will work:
public class CompositeCommand : ICommand {
private List<ICommand> subCommands;
public CompositeCommand()
{
subCommands = new List<ICommand>();
}
public bool CanExecute(object parameter)
{
foreach (ICommand command in subCommands)
{
if (!command.CanExecute(parameter))
{
return false;
}
}
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
foreach (ICommand command in subCommands)
{
command.Execute(parameter);
}
}
public void AddCommand(ICommand command)
{
if (command == null)
throw new ArgumentNullException("Yadayada, command is null. Don't pass null commands.");
subCommands.Add(command);
}
}