I've inherited some code that implements WPF Commands as follows:
public ICommand pvToggleSelectMapCommand
{
get
{
return new CommandHandler(() => pvToggleSelectMap(), true);
}
}
This is fine without parameters and doesn't use a generic RelayCommand-like class to set up the command handling. I need to put a parameter into this now, and am struggling to find a simple way to handle on using this way of command handling.
Any suggestions?
Usually, when using some form of delegate ICommand, you can just add an object input parameter to get your CommandParameter object. Try this:
public ICommand pvToggleSelectMapCommand
{
get
{
return new CommandHandler((params) => pvToggleSelectMap(params), true);
}
}
...
public void pvToggleSelectMap(object params) { ... }
Of course, this might not work with your CommandHandler class as you didn't provide any information for that here.
Related
I am currently trying to make a console for my game, and decided making a class called Command which can then be used to create commands easily was a good idea. I made the class but of course these classes are going to do vastly different thing, as such I was thinking of making a property which would basically act like a function, aka I could construct a command with properties commandName, arguments and then the customizable code block which would then be executed upon writing the command. How would I go about this?
public class Command : MonoBehaviour
{
string inputCommand;
int arguments;
void execution()
{
//this is where to codeblock to be executed upon typing the command would go
}
}
Edit:
I made what seems to be progress but still can't seem to get it right. Also each action needs to be able to have different amounts of arguments (for example "runes.add" needs an integer for runes to add and "updatestore" needs none). Any help would be greatly appreciated
public class Command : MonoBehaviour
{
public string InputCommand { get; set; }
public int Arguments { get; set; }
public Action ExecuteAction { get; set; }
}
public class Commands
{
public List<Command> commandCollection = new List<Command>()
{
new Command()
{
InputCommand = "",
Arguments = 1,
ExecuteAction = new Action(()=>{code to execute goes here})
}
};
}
First of all, you shouldn't derive Command from MonoBehaviour if you want to construct Command with object constructor (not Instantiate).
I think you should make abstract Command class and create commands as classes derived from Command class.
Also what you call "code block" can be done using polymorphism.
So, what you need to do:
Create Command class
public abstract class Command
{
public abstract void Execute(string[] args);
}
Execute method is abstract so we can override realisation of this method in subclasses. This methods takes an array of command arguments as the parameter.
Create some test commands
public class TestCommand : Command
{
public override void Execute(string[] args)
{
Debug.Log("Test command invoked, passed parameters count: " + args.Length);
}
}
Create CommandRegistry class (it's your Commands class)
public class CommandRegistry
{
private Dictionary<string, Command> _commands;
public CommandRegistry()
{
_commands = new Dictionary<string, Command>();
}
public void RegisterCommand(string name, Command command)
{
// You should also check here if command already exists
if(_commands.ContainsKey(name))
{
// Print error here or throw an exception
return;
}
_commands[name] = command;
}
public void RegisterAllCommands()
{
// Add here every new command to register it
RegisterCommand("test", new TestCommand());
}
// Returns false if command not found
public bool ExecuteCommand(string commandName, string[] args)
{
if(_commands.ContainsKey(commandName) == false)
return false;
_commands[commandName].Execute(args);
return true;
}
}
That's it. You need to call ExecuteCommand method to execute a command and pass a name and arguments of the command.
You should check argument count inside a Command.Execute method.
Also if you need to access your game methods/fields (for example to add runes) you should provide static access to this fields/methods or create something like CommandContext class (or GameContext).
An instance of this class will be passed to every command and it contains references to objects that can do things like adding runes.
Then you will need to add a new parameter (CommandContext) to GameRegistry.ExecuteCommand and Command.Execute method.
My application has been code like this but I am not sure if this is correct. In particular "return aButtonClickedCommand ??" which I assume means if the command has not already been defined then create it.
Rather than doing it this way would it be cleaner to just declare the command in the constructor and if so how could I do that and use it?
public partial class PhrasesFrameViewModel : ObservableObject
{
private ICommand aButtonClickedCommand;
public ICommand AButtonClickedCommand
{
get
{
return aButtonClickedCommand ?? (aButtonClickedCommand =
new Command(() => {
App.DB.IncrementPoints(Settings.cfs, phrasesFrame.phrase, (int)Settings.aBtn, 1);
Change.points = true;
phrasesFrame.CancelTimer2();
}));
}
}
You you can do it in the constructor, or another command pattern and may look a little cleaner and also saves putting it in the constructor
private ICommand abuttonClickedCommand;
public ICommand AButtonClickedCommand => aButtonClickedCommand ?? (aButtonClickedCommand = new Command(ProcessButtonClickedCommand));
private void ProcessButtonClickedCommand()
{
App.DB.IncrementPoints(Settings.cfs, phrasesFrame.phrase, (int)Settings.aBtn, 1);
Change.points = true;
phrasesFrame.CancelTimer2();
}
You are basically correct, if its not created (then create it), however some people like the constructor approach as well
Probably I'm searching for some wrong Keyword, but I couldn't find anything on this matter: I'm tinkering around with WPF MVVM and I'm now at the Point, I'm looking for a solution to give a ViewModel the possibility to request the Container to change to anhother ViewModel.
My approach is to pass a Callback to the created ViewModel, which is held on the Abstract BaseClass and can called, if desired. The method in the Container looks like this:
private void ApplyViewModel<T>()
where T : ViewModelBase
{
_exHandler.HandledAction(
() =>
{
var vm = _viewModelFactory.CreateViewModel<T>();
vm.ApplyViewModel = ApplyViewModel<T>;
CurrentContent = vm;
});
}
The interesting point is the second Line, where I pass the Method itself to the ViewModel.
For this matter, I created also a Delegate:
public delegate void ApplyViewModelDelegate<T>()
where T : ViewModelBase;
The problem lies now in the definition of the Callback in the ViewModelBase: It seems not possible to define it as an open generic Delegate. So I would like to create a Property with the following Signature:
public ApplyViewModelDelegate<T> ApplyViewModel { get; set; }
Is there kindahow a possibility to pass a Delegate as open generic Type, until I'd really like to call it with a specific Type?
Edit:
If I define the Property like this:
public ApplyViewModelDelegate<ViewModelBase> SwitchToViewModel { get; set; }
I can assign the Property from the Container, but my final idea would be to call this Property to target a specific ViewModel. For example, a Command could look like this:
public ViewModelCommand ToTest2
{
get
{
return new ViewModelCommand(
"To Test2",
new ActionCommand(() =>
{
SwitchToViewModel<Test2ViewModel>();
}));
}
}
I would like to know how to pass two or more parameters to Prism event aggregator (event class inherits CompositePresentationEvent)?
I know I can create wrapper class like EventArgs and create property for each value I need, but I would rather to pass two distinct parameters.
Is this possible?
Unfortunately, the event aggregator is only set up to pass a single parameter. HOWEVER, that parameter can be a class or structure.
Here is an example of a "message" I pass with event aggregator, including a callback parameter.
public class OpenViewPayload
{
public string ViewName;
public object Context;
public Action callback;
}
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
public class OpenViewEvent : CompositePresentationEvent<OpenViewPayload>
{
}
Usage:
_eventAggregator.GetEvent<OpenViewEvent>().Publish(new OpenViewPayload() { ViewName = "CustomerView", Context = _selectedCustomerID, callback= ()=> { /* Close Current View */ } });
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);
}
}