Reputation: 510
I have two identical views View1.xaml
and View2.xaml
and they both have a button button1
and a textfield textfield1
. The idea is that when you press the button, the corresponding textfield is filled with some information. Both views use the same method for filling in the textfield (the views are literally identical in that sense).
My question is: how to write generic code using OOP principles and not break the MVVM pattern? My current way of performing this with RelayCommand:
The same code for ViewModel1
and ViewModel2
:
public RelayCommand ButtonCommand { get; private set; }
#Constructor
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
#EndConstructor
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
Binding for the button in View1.xaml
and View2.xaml
:
<Button Command="{Binding Path=ButtonCommand, Mode=OneWay}" />
This is bad, because I have to write the same code for both ViewModels. I was trying to make a class ButtonCommand
which inherits from RelayCommand, but because not every view will have this functionality, I can't achieve it using this method.
Upvotes: 3
Views: 1106
Reputation: 3851
This is an XY problem. You're asking for a way to solve Y (not duplicate the same ButtonCommand
but in actuality), your problem is X (you already have duplication in your code)
I have two identical views View1.xaml and View2.xaml
I'd like to add, that you've also stated you don't have only two identical views, there's more.
The best way to resolve this is to have a parent ParentViewModel
that can construct the child ViewModels
So first, we'll need an interface for the child view model
public interface IMyViewModel
{
void Load();
}
Next, the implementation
public class MyViewModel : ViewModelBase, IMyViewModel
{
public MainViewModel()
{
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
}
public RelayCommand ButtonCommand { get; private set; }
public void Load()
{
//Example load logic
InvalidateCommands();
}
private void InvalidateCommands()
{
ButtonCommand.RaiseCanExecuteChanged();
}
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
And lastly the ParentViewModel
which has the responsibility of creating the view models. Please note, I did not tell it WHEN to create the ViewModels, I will leave that up to you.
public class ParentViewModel : ViewModelBase
{
private Func<IMyViewModel> _myVmCreator;
public ParentViewModel(Func<IMyViewModel> myVmCreator)
{
_friendEditVmCreator = friendEditVmCreator;
}
public ObservableCollection<IMyViewModel> MyViewModels { get; private set; }
private IMyViewModel CreateAndLoadMyViewModel()
{
var myVm = _myVmCreator();
MyViewModels.Add(myVm);
myVm.Load();
return myVm;
}
}
This will allow you to create any number of MyViewModels
, or any other type of ViewModel as long as it implements IMyViewModel
.
The above example is derived from this course : https://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels
I highly recommend it.
Upvotes: 2
Reputation: 5308
Rather than having a "Base" view model and two derived view models, have your two view models both use the same code defined elsewhere (ideally, both calling the same interface, injected with dependency injection).
This is the Composition over Inheritance principle.
When you're writing your tests, test that both view models call the interface, and test that the implementation of that interface does what it is supposed to do once.
This way, not only can you avoid writing your code twice, you can also avoid testing it twice, and it also allows you to follow other principles like the single responsibility principle.
Upvotes: 2
Reputation: 1864
This could be an way to go:
1 - Create a base viewmodel class:
public class YourBaseViewModel
{
public Object YourBaseProperty{get; set;}
public RelayCommand ButtonCommand { get; private set; }
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
2 - Inherit from the base viewmodel:
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
EDIT: If you have another base class you could do:
public class YourBaseViewModel:YourReallyBaseViewModel
{
// ....
}
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
Upvotes: 2