Reputation: 103
I wanted to use SaveFileDialog in my ViewModel, But since it's not correct that bind to a View from ViewModel, I searched for ways to do that. But I found a few answers that doesn't completely separate View form ViewModel, Like this:
public interface IOService
{
void IMessageBox(string Message);
string ISaveFileDialog(string DefaultPath);
}
public class IDialog : IOService
{
public void IMessageBox(string Message)
{
System.Windows.MessageBox.Show(Message);
}
public string ISaveFileDialog(string DefaultPath)
{
System.Windows.Forms.SaveFileDialog dg = new SaveFileDialog
{
InitialDirectory = DefaultPath,
Filter = "PDF files (*.pdf) | *.pdf"
};
dg.ShowDialog();
if (dg.FileName == null)
dg.FileName = string.Empty;
return dg.FileName;
}
}
They said that, this is a Service and using it will separate View from ViewModel. But we have make an Instance from this in ViewModel:
IDialog iDialog = new IDialog();
So I wanna know, What's the diffrence between this method and calling MessageBox or SaveFileDialog from ViewModel directly?
Note: Also I find something that said I could use a Service like the above, But implement it like this:
public class ExportViewModel : BaseViewModel
{
IOService _IOService;
public ExportViewModel(IOService ioservice)
{
_IOService = ioservice;
.
.
}
}
But I don't know how send IOService as a parameter to ExportViewModel (Because we can't create Instances from an Interface!)
Upvotes: 1
Views: 685
Reputation:
It's an abstraction used to separate UI concerns from the view model, and to allow you a means to intercept these calls within unit tests. Intercepting these calls allows you to both prevent showing a dialog during a test (which will block execution of your tests, not good) and verify the view model is acting as expected.
There is nothing special about calling it a "Service" or calling the abstraction "IService". It's just an allusion to a common pattern
You could just as well call it "IDontTouchUIStuffInMuhViewModel", but that's not as elegant.
Your second problem is solved trivially at runtime by providing an implementation of the interface to your view model. Your view model shouldn't care how it's implemented (polymorphism), so you can pass in a legit implementation at runtime and a fake one during testing.
You can also accomplish this by using Dependency Injection or Inversion of Control. By using a library such as Unity to instantiate your view models, all dependencies are automatically provided, based on how you configured the container.
Upvotes: 0
Reputation: 7705
You shouldn't pop up dialogs directly from your VM for automated testability.
If you call MessageBox.Show(), your test will get stuck until a person closes the dialog.
If, instead, you use "IMessageBox", for unit tests, you can inject an implementation that doesn't actually show the dialog, but rather returns a specific value (the result).
Upvotes: 3