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.
Related
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.
I have to create a WPF application with Caliburn.Micro 2.0.2 for my Bachelor exam.
In this application three different Views will be shown in a single Shell (Window) and they have to communicate with each other. So I need the Event Aggregator.
I also need the Window Manager to show additional dialogs.
My actual problem is that I have to bring all this together with full Design Time Support.
Unfortunately there is no example for that case.
The documentation of Caliburn.Micro says that a default constructor is needed in the view model, to provide design time support. However the Event Aggregator and the Window Manager are used as constructor parameters in the view model, so there is no default constructor at first.
The documentation also says that in such a case the ViewModelLocator should be used to get Design Time Support.
Unfortunately the section about the ViewModelLocator doesn't give me enough information about how to do that.
Another idea might be to chaining constructors like this:
public class ExampleViewModel : PropertyChangedBase
{
private readonly IEventAggregator eventAggregator;
private readonly IWindowManager windowManager;
public ExampleViewModel() : this(null)
{
}
public ExampleViewModel(IEventAggregator eventAggregator) : this(eventAggregator, null)
{
}
public ExampleViewModel(IEventAggregator eventAggregator, IWindowManager windowManager)
{
this.eventAggregator = eventAggregator;
this.windowManager = windowManager;
// doing everything needed for the Design Time Support
}
}
But I have no idea if that will work at last.
I hope somebody here can help me with this issue.
You can use a separate DataContext (ViewModel) for design time. You need to add in your XAML where the view model is used:
<UserControl
...
xmlns:dt="clr-namespace:YourProject.DesignTimeHelpers;assembly=YouAssembly"
d:DataContext="{Binding Source={x:Static dt:DesignTimeModels.ExampleViewModelForDesignTime}}">
There is the DesignTimeModels static class with the view model:
public static class DesignTimeModels
{
public static ExampleViewModel ExampleViewModelForDesignTime { get; set; }
// static constructor
static DesignTimeModels()
{
ExampleViewModelForDesignTime =
new ExampleViewModel(new EventAggregator(), new WindowManager());
}
}
The main idea is creating an instance of the view model by a static initializer with arguments what you need.
If you would like to use a IoC container (Caliburn for example) for instantination of the EventAggregator or the WindowManager, you can use a ServiceLocator pattern. For example:
// static constructor
static DesignTimeModels()
{
var eventAggregator = ServiceLocator.Get<IEventAggregator>();
var windowManager = ServiceLocator.Get<IWindowManager>();
ExampleViewModelForDesignTime =
new ExampleViewModel(eventAggregator , windowManager);
}
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 to this other question: How to inject an action into a command using Ninject?
Based on the comments on the above-referenced question, I take it that I would just need to create some command classes and inject them in my view model so that the view's controls just need to bind to them. I conceptually agree and understand the benefits. Besides, I wish to be as clean as possible using Ninject, DI and Constructor Injection.
Following these important rules, here's what I've come with so far.
CreateCategoryCommand
public class CreateCategoryCommand : ICommand {
public CreateCategoryCommand(CreateCategoryView view) {
if(view == null) throw new ArgumentNullException("view");
this.view = view;
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { view.Show(); }
private readonly CreateCategoryView view;
}
CategoriesManagementViewModel
public class CategoriesManagementViewModel {
public CategoriesManagementViewModel(ICommand createCommand) {
if (createCommand == null) throw new ArgumentNullException("createCommand");
this.createCommand = createCommand;
}
public ICommand CreateCommand { get { return createCommand; } }
private readonly ICommand createCommand;
}
So now when the CategoriesManagementView is initialized, it is constructor-injected with the CategoriesManagementViewModel, which in turn is constructor-injected with the CreateCategoryCommand, which in turn is constructor-injected with the CreateCategoryView, so no redundant dependency, neither any cycle-dependency.
Now, when I the CategoriesManagementView.CreateButton, it shall trigger the bound CategoriesManagementViewModel.CreateCommand, which will show the CreateCategoryView to the user, and this view shall have its own proper commands as well injected the same way.
Finally, this would render the RelayCommand class as useless...
Is that it?
First, I agree that RelayCommand and DelegateCommand and the like are ways of implementing commands that violate SOLID principles, so your solution here to replace them with a separate class is the correct one. Doing so also keeps your ViewModels much cleaner.
That said, you're violating MVVM pretty badly by having a class in your ViewModels layer (the CreateCategoryCommand) have knowledge of a concrete that is in your Views layer (CreateCategoryView). Nothing in your ViewModels layer should have a direct reference to anything in your Views layer.
Imagine it this way - you've separated your layers out into different dlls - Views.dll, ViewModels.dll, Models.dll, DataLayer.dll. If something in your ViewModels has a reference to a concrete in your Views, and obviously your Views will have a reference to ViewModels (as is necessary), then you have a circular reference problem.
The solution is to have your View object implement an interface (Interface Segregation Principle) like IDialog or IUiDisplay (choose the name depending on how abstract you want to be), and have your command have a dependency on that interface, NOT the direct concrete type, like so:
In Views:
public class CreateCategoryView : ..., IUiDisplay
{
...
}
In ViewModels:
public interface IUiDisplay
{
void Show();
}
public class CreateCategoryCommand : ICommand
{
public CreateCategoryCommand(IUiDisplay uiDisplay) {
if(display == null) throw new ArgumentNullException("uiDisplay");
this.display = uiDisplay;
}
private readonly IUiDisplay display;
...
}
Now, your Command no longer has a direct dependency on a concrete (so it is now mockable and testable!) from a higher layer. Now you can have your DI/IOC resolve the command dependency to the specific view class you want to inject. (I'd personally inject a view factory into the command instead, and only create the view lazily, but that's a different discussion).
One related note - if you implement commands by directly having them implement ICommand, then you're going to repeat yourself a lot (DRY). My suggestion is to create an abstract base class (CommandBase or something) that implements the requirements of ICommand. You'll find that all your commands that derive from it will only override Execute() and sometimes CanExecute(). This saves you from having to implement the event (and code to raise the event) in every command, and in many cases saves you from having to implement CanExecute since most commands just return true.
We have a large-ish app with a ribbon. The ribbon buttons are all bound to commands in my main view model (the data context of the main app window).
The constructor for MainViewModel is starting to grow as we create lots of RelayCommands (bound to the various ribbon commands). It looks something like this:
public MainWindowViewModel()
{
this.OpenProjectCommand = new RelayCommand(() =>
{
// buncha code
});
this.ProjectTypesCommand = new RelayCommand(() =>
{
// more code
});
this.NewSectionCommand = new RelayCommand(() =>
{
// code code code...
});
// ... only three ribbon buttons down, this is gonna get huge...
}
I'd prefer to have separate classes implementing each of the commands, rather than tons of inline code in MainViewModel's constructor. (Or creating lots of delegates in the MainViewModel, e.g. OpenProject, CanOpenProject, and then passing in references to them to the RelayCommand constructors).
Why don't I simply implement ICommand in a CommandBase and then create separate commands? Because I want to be "standard friendly" as per this question.
Is there a standard ICommand implementation I can use so that my commands are in separate classes?
I'd prefer not to add more MVVM frameworks into the mix since I'm already using MVVM Light. But I also don't want to reinvent the wheel.
Update: MainViewModel.cs doesn't need to be cluttered with scores of #regions or command methods. Extension methods aren't a good fit either IMHO.
The way I do is that I have "sub-viewmodels". For example, in the case of the MainViewModel, let's imagine that you have a PrintCommand and a CancelPrintCommand. You can have a new class called PrinterViewModel, and expose an instance of this class in the MainViewModel. Have the PrintCommand and the CancelPrintCommand in this PrinterViewModel (this also allows modular unit testing, which is neat).
Then in XAML:
Command="{Binding Main.Printer.PrintCommand}"
Alternatively, you could do
new RelayCommand(() => Printer.DoSomething())
Does that make sense?
Cheers
Laurent
You could at least create them in the getter.
You can use the ?? operator.
http://msdn.microsoft.com/en-us/library/ms173224.aspx
This basically says: Return _testCommand, but create it first if it's still null.
This way, the command is not created until it's needed!
public class TestViewModel : ViewModelBase
{
#region OpenCommand
private RelayCommand _testCommand;
public RelayCommand TestCommand {
get {
return _testCommand = _testCommand
?? new RelayCommand(
this.ExecuteOpenCommand,
this.CanOpenCommandExecute);
}
}
private void ExecuteOpenCommand()
{
// do stuff
}
private bool CanOpenCommandExecute()
{
return true;
}
#endregion
}
If your goal is to organize, you can use #region and #endregion. And like we said, if your goal is to shrink the constructing process, use the ?? operator in getters. If you just hate inline code, create private methods in combination with RelayCommand, in getters.