How to create and implement a custom command? - c#

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.

Related

WPF switching views

I am implementing a WPF application and I am switching view models on button click. I had to implement an navigation store by youtube tutorial. When I click a button, navigateCommand will execute, creating a new viewModel and notifying view to change. However I dont understand what is method OnCurrentViewModelChanged() doing and why is it needed, action CurrentViewModelChanged is returning void, and is empty? Or am I missing something? What is CurrentViewModelChanged doing? Can someone please explain?
public class NavigationStore
{
public event Action CurrentViewModelChanged;
private NotifyPropertyChanged currentViewModel;
public NotifyPropertyChanged CurrentViewModel
{
get => currentViewModel;
set
{
currentViewModel = value;
OnCurrentViewModelChanged();
}
}
private void OnCurrentViewModelChanged()
{
CurrentViewModelChanged?.Invoke();
}
}
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;
public NavigateCommand(NavigationStore navigationStore, Func<TViewModel> createViewModel)
{
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}
public override void Execute()
{
_navigationStore.CurrentViewModel = _createViewModel();
}
}
public class MainViewModel : NotifyPropertyChanged
{
private readonly NavigationStore _navigationStore;
public NotifyPropertyChanged CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(NavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.CurrentViewModelChanged += OnCurrentViewModelChanged;
}
private void OnCurrentViewModelChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}
So first of all, I also followed his tutorials (it's most likely SingletonSean's) and I don't share #BenicCode's opinion on that (tho I'm not a professional at WPF like he may be), I really like his explanations and solutions to problems. Besides, he keeps changing the project throughout the guide, implementing better solutions and explaining why it's better to use this than that.
The OnCurrentViewModelChanged() method raises an event so that any method that is subscribed to it will be invoked. However, you actually don't need it, you can implement NavigationStore like this:
NavigationStore.cs
public class NavigationStore : INavigationStore
{
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel
{
get => _currentViewModel;
set
{
_currentViewModel?.Dispose();
_currentViewModel = value;
NavigationStateChanged?.Invoke();
}
}
public event Action? NavigationStateChanged;
}
And now, in your MainViewModel, you can simply subscribe the NavigationStateChanged action to OnCurrentViewModelChanged() instead of having one more method in your navigation store.
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private readonly INavigationStore _navigationStore;
public ViewModelBase? CurrentViewModel => _navigationStore.CurrentViewModel;
public MainViewModel(INavigationStore navigationStore)
{
_navigationStore = navigationStore;
_navigationStore.NavigationStateChanged += OnNavigator_NavigationStateChanged;
}
private void OnNavigator_NavigationStateChanged()
{
OnPropertyChanged(nameof(CurrentViewModel));
}
}
It's basically the same, but a bit simpler (correct me if I'm wrong). By subscribing NavigationStateChanged to OnNavigator_NavigationStateChanged, whenever NavigationStateChanged is raised, OnNavigator_NavigationStateChanged will fire too, which will notify your UI to change (since you bind the ContentControl's Content property to the CurrentViewModel property).
MainWindow.xaml
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
At this point of the tutorial he just wanted to demonstrate really basic navigation. As you progress further, things get cleaner and more complicated. I really suggest finishing his tutorials, there might be better guides, but as a starting point, I couldn't find any better channel.

Designs of a WPF MVVM Application

I have been doing tons of research using MVVM (Model View ViewModel) with WPF. I am developing a desktop application. This application consists of a main window. This main window has some buttons which do something. Also, there is a button that opens a OpenFileDialog box.
Currently, this is my ViewModel to which the main window binds to:
MainWindowPresenter Class
namespace BMSVM_Simulator.ViewModel
{
class MainWindowPresenter : ObservableObject
{
private bool logLoaded; // true if a log is currently loaded, false otherwise
public MainWindowPresenter()
{
logLoaded = true;
}
public ICommand load_data_button_pressed
{
get { return new DelegateCommand(doLoadData); }
}
private void doLoadData()
{
// DO LOAD DATA COMMANDS
}
public ICommand exit_button_pressed
{
get { return new DelegateCommand(doExit); }
}
private void doExit()
{
// DO EXIT COMMANDS
}
}
}
QUESTION 1: I am concerned that this is the "wrong" implementation. Is it correct (per MVVM) for each button to have a property of type ICommand and then a corresponding method implementing the functionality? A main window with a lot of buttons would have a very large ViewModel class, no?
QUESTION 2: If one of the buttons was a File->Open File button. So, in that case it would open up a new OpenFileDialog window. Would this be done in the same way I previously done it above (i.e. have a public ICommand open_file_dialog_button_pressed property and a corresponding public void doOpenFileDialog() method? This seems like I am mixing the "view" of the open file dialog into the ViewModel, although the view is already defined by the built in wpf OpenFileDialog class.
QUESTION 3: Is it true that each "view" of our application should have only a single "presenter" class (which is part of the ViewModel) to which that view binds to? In the example above, my main window view binds to only the MainWindowPresenter class. If I were to make another view (say a graph generated with Microsoft's Dynamic Data Display library in it's own popout window), I would need an additional "presenter" class in my ViewModel, correct?
Thank you very much!
Rich
For reference, I've included these classes, but they may not be useful:
DelegateCommand Class
namespace BMSVM_Simulator.ViewModel
{
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;
#pragma warning restore 67
}
}
ObservableObject Class
namespace BMSVM_Simulator.ViewModel
{
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//basic ViewModelBase
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
}
}
1) Yes that's correct. You need to create a command property for each command. But thanks to your relay command you don't need to implement it directly. To prevent your ViewModel from busting i would recommend to move all commands into a separate CommandsViewModel serving as command source. Your View then binds to it.
2) Opening the Dialog can be achieved in XAML via routed commands using the CommandBinding property. So the tasks remains in the view. You basically try to avoid the dependency on any view related object. .NET provides some ready to use commands for common purposes (MSDN - ApplicationCommands)
3) You can share ViewModels among Views of course. That's one reason you structure your implementation into Model View ViewModel to be independent from changes and for reusability. It can become critical when more than one view is updating the same source simultaneously.

Is it okay to use ICommand in view-model

Most of the WPF mvvm applications, we are using ICommand in the view-model. But it is referring to System.Windows.Input. so the view-model is now tightly couple with System.Windows.Input namespace. according to my understanding view-model should be able to use in normal C# winform application or asp.net application.
Normally we are using following code lines to the command with RelayCommand implementation.
private RelayCommand testCommand;// or private ICommand testCommand;
public ICommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
What i feel is we need to remove all the ICommand and use RelayCommand instead. So we can eliminate the System.Windows namespace from the view-model. so final code will looks like this,
private RelayCommand testCommand;
public RelayCommand TestCommand
{
get
{
return testCommand ??
(testCommand = new RelayCommand(param => Test()));
}
}
public void Test()
{
}
Any suggestions on this approach? or is there any way to eliminate the System.Windows namespace from the view-model?
Any suggestions on this approach?
This still doesn't decouple you from System.Windows.Input as RelayCommand still must implement ICommand, even if it's indirectly implementing it.
Implementing ICommand within the ViewModel is one of those things that tends to be required in order to be pragmatic. Ideally, ICommand (or a similar interface) would have been implemented in a namespace that wasn't XAML specific. That being said, it is supported directly within the Portable Class Libraries, so it is not tied to a specific framework (WPF, Silverlight, Phone, etc) as much as XAML in general.
Pretty simple to avoid coupling your ViewModel to ICommand, if you want to. Probably not a bad idea, WPF will probably go the way of MFC one day. Overkill? maybe, but here is a how:
In your view:
<StackPanel>
<Button Command="{Binding Path=MyCommand}"> Do it! Kill me Now!</Button>
<TextBlock Text="{Binding Path=Message}"></TextBlock>
</StackPanel>
Inject your ViewModel into your DataContext, Take the responsibility for the native commands, out of your view model:
public class ViewModel : INotifyPropertyChanged
{
public string Message { get; set; }
public object MyCommand { get; set; }
public void OnMyCommand(object parameter)
{
Message += "I Ran something" + Environment.NewLine;
}
public bool CanMyCommand(object parameter)
{
return true;
}
// Injected Native Command handler
public ViewModel(ICommandFactory factory)
{
MyCommand = factory.CreateInstance(OnMyCommand, CanMyCommand);
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note I'm using FODY to weave in the property change handler. INotifyPropertyChanged is System.dll btw.
Now, Bind this contract:
public interface ICommandFactory
{
object CreateInstance(Action<object> action, Func<object, bool> predicate);
}
... to something that will give you a native Command object;
public class NativeCommand : ICommand
{
private readonly Action<object> _action;
private readonly Func<object, bool> _predicate;
public NativeCommand(Action<object> action, Func<object, bool> predicate)
{
_action = action;
_predicate = predicate;
}
public bool CanExecute(object parameter)
{
return _predicate(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public event EventHandler CanExecuteChanged;
}
public class NativeCommandFactory : ICommandFactory
{
public object CreateInstance(Action<object> action, Func<object, bool> predicate)
{
return new NativeCommand(action, predicate);
}
}
Bind<ICommandFactory>().To<NativeCommandFactory>();
VoilĂ , decoupled commands.
Also note, your injection is done at initial application start. Your ViewModel is decoupled from whatever IoC container you choose.
Well, in theory, you are pretty much right. It would if nice of ICommand was completely UI-platform-independent.
But from a practical standpoint, if you are using MVVM in a WPF app, there's a pretty good chance you are fairly dependent on WPF's databinding and datatemplating capabilities anyway. Trying to stick a WinForms UI on top of something like that would likely require a significant amount of extra effort.
I've worked on some fairly large WPF/MVVM projects in the past. We considered MVVM to be a way of separating the specific details of the UI from the code - not so that we could switch to WinForms/ASP.NET/whatever, but so that we could change the look and feel of our UI (i.e. edit the XAML) without having to change the ViewModels. In this respect, MVVM worked perfectly.
If you are really concerned about sharing code across multiple types of projects, it might be better to try and put your common code in a typical 'Business Layer'-type class library, instead of in view model.

Best / neatest way to declare RelayCommands

I've been trying to find a nice neat and succinct way to declare RelayCommands in my ViewModels.
The best I can come up with is:
public class MyViewModel
{
public ICommand StopCommand { get; private set; }
public MyViewModel()
{
StopCommand = new RelayCommand(OnStop);
}
private OnStop(object sender)
{
//hammertime
}
}
What I'd really like to do it remove the two stage declaration/construction, something like:
public class MyViewModel
{
public readonly ICommand StopCommand = new RelayCommand(OnStop);
private OnStop(object sender)
{
//hammertime
}
}
However, this fails to compile with
error CS0236: A field initializer cannot reference the non-static
field, method, or property 'MyViewModel.OnStop(object)'
It there a neater / "standard" way that people use?
I've used the first format you specified quite a bit and it works fine for me.
Also - if you're using WPF, binding doesn't work with fields anyway so even if you can get the second approach to compile, it won't hook up to your UI.
One option is to abandon commanding which has it's limitations, and use another mechanism such as Actions provided by Caliburn.Micro. Then, you just need your view model verb:
public void Save()
{
}
<Button x:Name="Save">Save</Button>
I was using something like:
public ICommand StopCommand
{
get{return new RelayCommand(OnStop);}
}

Can I call a command inside a command?

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);
}
}

Categories