passing data to a mvvm usercontrol - wpf

I'm writting a form in WPF/c# with the MVVM pattern and trying to share data with a user control. (Well, the User Controls View Model)
I either need to:
Create a View model in the parents and bind it to the User Control
Bind certain classes with the View Model in the Xaml
Be told that User Controls arn't the way to go with MVVM and be pushed in the correct direction. (I've seen data templates but they didn't seem ideal)
The usercontrol is only being used to make large forms more manageable so I'm not sure if this is the way to go with MVVM, it's just how I would of done it in the past.
I would like to pass a class the VM contruct in the Xaml.
<TabItem Header="Applicants">
<Views:ApplicantTabView>
<UserControl.DataContext>
<ViewModels:ApplicantTabViewModel Client="{Binding Client} />
</UserControl.DataContext>
</Views:ApplicantTabView>
</TabItem>
public ClientComp Client
{
get { return (ClientComp)GetValue(ClientProperty); }
set { SetValue(ClientProperty, value); }
}
public static readonly DependencyProperty ClientProperty = DependencyProperty.Register("Client", typeof(ClientComp),
typeof(ApplicantTabViewModel),
new FrameworkPropertyMetadata
(null));
But I can't seem to get a dependancy property to accept non static content.
This has been an issue for me for a while but assumed I'd find out but have failed so here I am here.
Thanks in advance,
Oli

Oli - it is OK (actually - recommended) to split portions of the View into UserControl, if UI became too big - and independently you can split the view models to sub view models, if VM became too big.
It appears though that you are doing double-instantiations of your sub VM. There is also no need to create Dependency Property in your VM (actually, I think it is wrong).
In your outer VM, just have the ClientComp a regular property. If you don't intend to change it - the setter doesn't even have to fire a property changed event, although it is recommended.
public class OuterVm
{
public ClientComp Client { get; private set; }
// instantiate ClientComp in constructor:
public OuterVm( ) {
Client = new ClientComp( );
}
}
Then, in the XAML, put the ApplicantTabView, and bind its data context:
...
<TabItem Header="Applicants">
<Views:ApplicantTabView DataContext="{Binding Client}" />
</TabItem>

I answered a similar question as yours recently: passing a gridview selected item value to a different ViewModel of different Usercontrol
Essentially setting up a dependency property which allows data from your parent view to persist to your child user control. Abstracting your view into specific user controls and hooking them using dependency properties along with the MVVM pattern is actually quite powerful and recommended for Silverlight/WPF development, especially when unit testing comes into play. Let me know if you'd like any more clarification, hope this helps.

Related

WPF MVVM and passing viewmodels to a view

I am pretty new to WPF and right now I am trying to get used to the MVVM pattern. Right now I have a simple application in which I have a collection of ViewModels that I display in a grid. When I doubleclick on the row in the grid I want to show a details View of the ViewModel.
The problem I am having right now is that I already have a fully instanced ViewModel, but I can't seem to pass it into the view. When I try to load that View it turns up empty. I already found out that this is due to the fact that when a View gets loaded it creates it's own instance of the backing ViewModel. So obviously I need to get around this behaviour and somehow pass the instanced ViewModel into the View when it is created. I could use a constructor in the View that takes a ViewModel and set the datasource in there. However, taking this approach but would mean that I need to construct the View in the ViewModel and thus making the ViewModel aware of the View. This I something I would like to avoid since I am trying to uphold the MVVM pattern.
So what should I do in this case? Should I just break the MVVM pattern or are there some nice and clean sollutions for this that fit in the MVVM pattern?
There are many ways of passing a view model to a view, as you call it, or setting a view model as the DataContext of either a Window or UserControl, as others may call it. The simplest is just this:
In a view constructor:
public partial class SomeView
{
InitializeComponent();
DataContext = new SomeViewModel();
}
A more MVVM way might be to define DataTemplates in App.xaml for each view model that defines which view each will use:
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
<YourViewsPrefix:YourView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type YourViewModelsPrefix:AnotherViewModel">
<YourViewsPrefix:AnotherView />
</DataTemplate>
Now whenever the Framework comes across an instance of these view model classes, it will render the associated view. You can display them by having a property of the type of your view model using a ContentControl like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
Or even in a collection like this:
<ListBox ItemsSource="{Binding YourViewModelCollectionProperty}" />
"Should I just break the MVVM pattern?"
Well, please consider to learn more about the pattern, to know what it is to "break it". The main purpose of this pattern is to keep responsability clear, thus to obtain testable and maintainable code. There are a lot of ressource for that as show in this question:
MVVM: Tutorial from start to finish?
Anyway to be more specific about your question, what you are looking for is how to set the DataContext.
"somehow pass the instanced ViewModel into the View when it is created"
Yes, you get it, if you assign the dataContext with a viewModel in the constructor of your view, it could work but it it is acceptable only if the viewModel has the responsability to create the view (which could be acceptable in really few situation). You could even write something like that to directly set DataContext from outside your view:
var l_window = new MyView { DataContext = new MyViewModel() };
l_window.Show();
Of course the main drawback is that this code is not testable. If you would like to test it you should use a mockable service to manage the view creation.
A more common solution is to inject the dataContext with an IOC container (like prism). You create all required ViewModel when the software started and you store them in this IOC container. Then, when the view is created, you ask this container to get you an instance of your viewModel.
An example could be: export your viewModel in PRISM:
[Export]
public class MyViewModel {...}
And then Import it in your view:
[Import]
private MyViewModel ViewModel
{
set { this.DataContext = value; }
get { return this.DataContext as MyViewModel; }
}
Hope it helps.
I agree with #Sheridan's answer and would only like to add another way to instantiate a view with a view model: you could use the Factory Pattern, maybe like this:
public class ViewFactory
{
public UIElement Create(object context)
{
// Create the view model
// You can pass in various information by parameters
// as I do with context (Constructor Injection)
var viewModel = new ViewModel(context);
// Create the view and set the view model as data context
var view = new View { DataContext = viewModel };
return view;
}
}
You can call this factory from within a method of your view model and then assign it to e.g. a property that is data bound to the UI. This allows for a bit more flexibility - but #Sheridan's solution is also fine.

Nested Data Context with Unity

I took a course on VB.Net + WPF at university last year. For the final project, I decided to give MVVM a go (we hadn't discussed it at all in the course, I had just researched it and thought it would be a useful exercise). It was a good experience however I'm rather sure I might have made some poor choices when it came to design.
I've since graduated and my job has nothing to do with WPF or Windows development however I'm developing a small application in my own time and thought it would be fun to use C# and WPF (C# is a language I very much like to work with and I enjoyed working with WPF so it's a pretty logical choice).
Anyway, I'm using this as an opportunity to learn more about MVVM and try and implement it in a better way than I did previously. I've done a bit more reading and am finding it a lot easier to graph than I had when trying to implement it alongside learning WPF.
I've used In The Box MVVM Training as a guide and will be using Unity for dependency injection at this.
Now, in the sample app developed in the guide, there is a single view model (MainWindowViewModel). The MainWindow is pretty much a container with 3 or 4 UserControls which all share the DataContext of the MainWindow.
In my app, I'd like to have a tab-based interface. As such, the MainWindow will be primary concerned with displaying a list of buttons to switch the current view (i.e. move from the 'add' view to the 'list view'). Each view will be a self-contained UserControl which will implement it's own DataContext.
The same code in the app is as follows:
MainWindow window = container.Resolve<MainWindow>();
window.DataContext = container.Resolve<MainWindowViewModel>();
window.Show();
That's fine for setting data context of the MainWindow, however how will I handle assigning each user context it's own ViewModel as a DataContext?
EDIT: To be more specific, when I say tab-based interface, I don't mean it in the sense of tabs in a text editor or web browser. Rather, each 'tab' is a different screen of the application - there is only a single active screen at a time.
Also, while Slauma's post was somewhat helpful, it didn't really explain how I'd go about injecting dependencies to those tabs. If the NewStatementView, for example, was required to output it's data, how would I inject an instance of a class that implements the 'IStatementWriter' interface?
EDIT: To simplify my question, I'm basically trying to figure out how to inject a dependency to a class without passing every dependency through the constructor. As a contrived example:
Class A has Class B.
Class B takes as a constructor paramater needs an implementation of Interface I1.
Class B uses Class C.
Class C takes as a constructor paramater needs an implementation of Interface I2.
How would I handle this scenario using DI (and Unity)? What I don't want to do is:
public class A(I1 i1, I2 i2) { .... }
I could register everything using Unity (i.e. create I2, then C, then I1 and B, and then finally insert these into A) but then I would have to instantiate everything when I want to use A even if I might not even need an instance of B (and what if I had a whole bunch of other classes in the same situation as B?).
MVVM has lots of benefits, but in my experience wiring up the view models and the views is one of the biggest complexities.
There are two main ways to do this:
1:
Wire the view models to the views.
In this scenario, the XAML for the MainWindow contains the child controls. In your case, some of these views would probably be hidden (because you are only showing one screen at a time).
The view models get wired to the views, usually in one of two ways:
In the code behind, after the InitializeComponents() call or in a this.Loaded event handler, let this.DataContext = container.Resolve<MyViewModelType>();
Note that in this case the container needs to be globally available. This is typical in applications that use Unity. You asked how children would resolve interfaces like IStatementWriter. If the container is global, the child view models could simply call container.Resolve<IStatementWriter>();
Another way to wire the view models into the views is to create an instance of the view model in XAML like this:
<UserControl ...>
<UserControl.DataContext>
<local:MyViewModelType/>
</UserControl.DataContext>
...
</UserControl>
This method is not compatible with Unity. There are a few MVVM frameworks that allow you to resolve types in XAML (I believe Caliburn does). These frameworks accomplish this through markup extensions.
2:
Wire the view up to the view model.
This is usually my preferred method, although it makes the XAML tree more complicated. This method works very well when you need to perform navigation in the main view model.
Create the child view model objects in the main view model.
public class MainViewModel
{
public MyViewModelType Model1 { get; private set; }
public ViewModelType2 Model2 { get; private set; }
public ViewModelType3 Model3 { get; private set; }
public MainViewModel()
{
// This allows us to use Unity to resolve the view models!
// We can use a global container or pass it into the constructor of the main view model
// The dependencies for the child view models could then be resolved in their
// constructors if you don't want to make the container global.
Model1 = container.Resolve<MyViewModelType>();
Model2 = container.Resolve<ViewModelType2>();
Model3 = container.Resolve<ViewModelType3>();
CurrentViewModel = Model1;
}
// You will need to fire property changed notifications here!
public object CurrentViewModel { get; set; }
}
In the main view, create one or more content controls and set the content(s) to the view models that you want to display.
<Window ...>
...
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelType}">
<local:MyViewType/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType2}">
<local:ViewType2/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelType3}">
<local:ViewType3/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
...
</Window>
Notice that we tie the child views to the view models through data templates on the ContentControl. These data templates could have been defined at the Window level or even the Application level, but I like to put them in context so that it's easier to see how the views are getting tied to the view models. If we only had one type of view model for each ContentControl, we could have used the ContentTemplate property instead of using resources.
EDIT: In this method, the view models can be resolved using dependency injection, but the views are resolved through WPF's resource resolution mechanism. This is how it works:
When the content for a ContentPresenter (an underlying component in the ContentControl) is set to an object that is NOT a visual (not derived from the Visual class), WPF looks for a data template to display the object. First it uses any explicit data templates set on the host control (like the ContentTemplate property on the ContentControl). Next it searches up the logical tree, examining the resources of each item in the tree for a DataTemplate with the resource key {x:Type local:OBJECT_TYPE}, where OBJECT_TYPE is the data type of the content. Note that in this case, it finds the data templates that we defined locally. When a style, control template, or data template is defined with a target type but not a named key, the type becomes the key. The Window and Application are in the logical tree, so resources/templates defined here would also be found and resolved if they were not located in the resources of the host control.
One final comment. If a data template is not found, WPF calls ToString() on the content object and uses the result as the visual content. If ToString() is not overridden in some meaningful way, the result is a TextBlock containing the content type.
<--
When you update the CurrentViewModel property on the MainViewModel, the content and view in the main view will change automatically as long as you fire the property changed notification on the main view model.
Let me know if I missed something or you need more info.
For a Tab-based interface this classical article about MVVM pattern in WPF might be very useful. (It also offers a downloadable sample application.)
The basic idea to connect each tab with a UserControl is as follows (only a rough sketch, details are in the article):
The MainWindow View has a ContentControl ...
<ContentControl Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}" />
... which binds to a collection of "Workspaces" in the MainWindowViewModel:
public ObservableCollection<WorkspaceViewModel> Workspaces { get; private set; }
This WorkspaceViewModel serves as a base class for all ViewModels you want to display as a tab.
The WorkspacesTemplate is a DataTemplate which binds a TabControl to the collection of WorkspaceViewModels:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}" />
</TabControl>
</DataTemplate>
And for every specific Tab you have a UserControl with a ViewModel which derives from WorkspaceViewModel ...
public class MySpecialViewModel : WorkspaceViewModel
... and which is related to the UserControl by a DataTemplate:
<DataTemplate DataType="{x:Type vm:MySpecialViewModel}" >
<v:MySpecialUserControl />
</DataTemplate>
Now, if you want to open a tab you would have a Command in the MainWindowViewModel which creates the ViewModel belonging to that tab and add it to the Workspaces collection of the MainWindowViewModel:
void CreateMySpecialViewModel()
{
MySpecialViewModel workspace = new MySpecialViewModel();
Workspaces.Add(workspace);
}
The rest is done by the WPF binding engine. The TabControl recognizes automatically that this special workspace item in the collection is of type MySpecialViewModel and selects the right View/UserControl through the DataTemplate we have defined to connect ViewModel and View and displays it in a new Tab.
At the point where you resolve your Views deriving from UserControl, use property injection to resolve a new ViewModel for each one and set the DataContext property of the view to it.

How is the DataContext typically set?

I've created a new WPF project, and threw in a DataGrid. Now I'm trying to figure out the easiest way to bind a collection of data to it.
The example I downloaded seems to do it in the window c'tor:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
But then the bindings don't seem to appear in the Visual Studio's Properties window. I'm pretty sure there's a way to set the data context in XAML too... it would make me even happier if I could do it directly through the properties window, but all the Binding options are empty. What's the typical approach?
Edit: At 14 minutes, he starts to talk about other methods of setting the data context, such as static resources, and some "injection" method. I want to learn more about those!
What I typically do is use MVVM. You can implement a simplified version by setting the data context in your code behind and having a model type class that holds your data.
Example: In your code behind
DataContext = Model; // where Model is an instance of your model
then in your view
<DataGrid .... ItemsSource="{Binding SomeProperty}">....
Where SomeProperty is an enumerable property on your view model
You can also set a data context in XAML by using the DataContext property
<uc:SomeUserControl DataContext="{Binding AnotherProperty}"....
This will run your user control within the DataContext of the AnotherProperty on your model.
Note that this is grosely simplified but it'll get you on your way.
Have a look at the MVVM design pattern. This pattern is very suitable for wpf applications.
There is described where to store your data and how to bind your ui to the data.

Should a user control have its own view model?

I have a window made up of several user controls and was wondering whether each user control have its own view model or should the window as a whole have only one view model?
Absolutely, positively
NO
Your UserControls should NOT have ViewModels designed specifically for them. This is, in fact, a code smell. It doesn't break your application immediately, but it will cause you pain as you work with it.
A UserControl is simply an easy way to create a Control using composition. UserControls are still Controls, and therefore should solely be concerned with matters of UI.
When you create a ViewModel for your UserControl, you are either placing business or UI logic there. It is incorrect to use ViewModels to contain UI logic, so if that is your goal, ditch your VM and place the code in that control's codebehind. If you're placing business logic in the UserControl, most likely you are using it to segregate parts of your application rather than to simplify control creation. Controls should be simple and have a single purpose for which they are designed.
When you create a ViewModel for your UserControl, you also break the natural flow of data via the DataContext. This is where you will experience the most pain. To demonstrate, consider this simple example.
We have a ViewModel that contains People, each being an instance of the Person type.
public class ViewModel
{
public IEnumerable<Person> People { get; private set; }
public ViewModel()
{
People = PeopleService.StaticDependenciesSuckToo.GetPeople();
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
To show a list of people in our window is trivial.
<Window x:Class="YoureDoingItWrong.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:YoureDoingItWrong"
Title="Derp">
<Window.DataContext>
<l:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:Person}">
<l:PersonView />
</DataTemplate>
</Window.Resources>
<ListView ItemsSource="{Binding People}" />
</Window>
The list automatically picks up the correct item template for the Person and uses the PersonView to display the person's information to the user.
What is PersonView? It is a UserControl that is designed to display the person's information. It's a display control for a person, similarly to how the TextBlock is a display control for text. It is designed to bind against a Person, and as such works smoothly. Note in the window above how the ListView transfers each Person instance to a PersonView where it becomes the DataContext for this subtree of the visual.
<UserControl x:Class="YoureDoingItWrong.PersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Label>Name</Label>
<TextBlock Text="{Binding Name}" />
<Label>Age</Label>
<TextBlock Text="{Binding Age}" />
</StackPanel>
</UserControl>
For this to work smoothly, the ViewModel of the UserControl must be an instance of the Type it is designed for. When you break this by doing stupid stuff like
public PersonView()
{
InitializeComponent();
this.DataContext = this; // omfg
}
or
public PersonView()
{
InitializeComponent();
this.DataContext = new PersonViewViewModel();
}
you've broken the simplicity of the model. Usually in these instances you end up with abhorrent workarounds, the most common of which is creating a pseudo-DataContext property for what your DataContext should actually be. And now you can't bind one to the other, so you end up with awful hacks like
public partial class PersonView : UserControl
{
public PersonView()
{
InitializeComponent();
var vm = PersonViewViewModel();
// JUST KILL ME NOW, GET IT OVER WITH
vm.PropertyChanged = (o, e) =>
{
if(e.Name == "Age" && MyRealDataContext != null)
MyRealDataContext.Age = vm.PersonAge;
};
this.DataContext = vm;
}
public static readonly DependencyProperty MyRealDataContextProperty =
DependencyProperty.Register(
"MyRealDataContext",
typeof(Person),
typeof(PersonView),
new UIPropertyMetadata());
public Person MyRealDataContext
{
get { return (Person)GetValue(MyRealDataContextProperty); }
set { SetValue(MyRealDataContextProperty, value); }
}
}
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
MVVM doesn't stand for "No Codebehind". Put your UI logic for your user control in the codebehind. If it is so complex that you need business logic inside the user control, that suggests it is too encompassing. Simplify!
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
This is not a yes or no question. It depends on whether having extra view models affords you better maintainability or testability. There's no point adding view models if it doesn't gain you anything. You'll need to gauge whether the overhead is worth it to your particular use case.
[should] each user control have its own ViewModel or should the window as a whole have only one ViewModel?
Unfortunately, the highest-voted answer to this question is misleading, and based on comments I've exchanged in other questions, providing poor guidance to people trying to learn WPF. That answer replies:
Your UserControls should NOT have ViewModels designed specifically for them.
The problem is, that's not the question that was asked.
I would agree with the general sentiment that when you write a UserControl, the public API of the control should not involve creating also a view model type that is specifically designed to be used for that control. The client code must be able to use whatever view model it wants.
But, that does not preclude the idea that "each user control [might] have its own ViewMomdel". There are at least two obvious scenarios I can think of where the answer to that would be "yes, a view model for each user control":
The user control is part of a data template in an items presenter (e.g. ItemsControl). In this case, the view model will correspond to each individual data element, and there will be a one-to-one correspondence between the view model object and the user control that presents that view model object.In this scenario, the view model object is not "designed specifically for them" (so no contradiction with the questionable answer), but it certainly is the case that each user control has its own view model (making the answer to the actual question "yes, each user control may have its own view model").
The user control's implementation benefits from, or even requires, a view model data structure specifically designed for the user control. This view model data structure would not be exposed to the client code; it's an implementation detail, and as such would be hidden from the client code using the user control. But, that certainly still would be a view model data structure "designed specifically for" that user control.This scenario is clearly not problematic at all, which directly contradicts the claim that "Your UserControls should NOT have ViewModels designed specifically for them."
Now, I don't believe it was ever the intent of the author of that answer to rule out either of these scenarios. But the problem is, people who are trying to learn WPF may not have enough context to recognize the difference, and thus may incorrectly generalize with respect to user controls and view models, based on this emphatic, highly-upvoted, and misleading answer.
It is my hope that by presenting this alternative view point as a point of clarification, and answering the original question in a less narrow-viewed way, those who found this question while learning more about WPF will have better context, and a better idea and when a view model might be implemented specific to a user control and when it should not be.
I would say that each user control should have its own ViewModel, because that would allow you to reuse the ViewModel/UserControl pair in new constellations in the future.
As I understand it, your window is a Composite of user controls, so you can always create a ViewModel that composes all the separate ViewModels for each of the user controls. This will give you the best of both worlds.
I'd be inclined toward a viewmodel.
Bear in mind that all of these patterns are designed to fragment your code to make it more maintainable in the future. Including MVVM. The view has certain responsibilities, so too the viewmodel, so too the model. A fresh developer can come in, can recognise this pattern, will have a better idea where to find and maintain things. Better than if it were a heap of spaghetti.
So, within that, if you have logic which correctly pertains to the usercontrol, which correctly belongs in the vm, then why not?
But there is a caveat here. Bear in mind what a UserControl is. It's something that is a tiny snippet of UI that can be reused from place to place. Your vm should be the same - reusable. The last thing you want to end up with is a vm which behaves one way in one scenario, and a different way in another scenario.
Note, no technology talked. I'm just talking about the logical structure of the pattern.
I guess your application is doing some sort of view composition, so if you make your user controls to have its own view model, you'll have more freedom to embed them in other host windows without changing the window global view model.
As an added bonus, your application will be more suited to evolve to a more architecturally-sound composition model as that provided by Prism or Caliburn frameworks, if the application requirements arise.

Open File Dialog MVVM

Ok I really would like to know how expert MVVM developers handle an openfile dialog in WPF.
I don't really want to do this in my ViewModel(where 'Browse' is referenced via a DelegateCommand)
void Browse(object param)
{
//Add code here
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == true)
{
//Do stuff
}
}
Because I believe that goes against MVVM methodology.
What do I do?
Long story short:
The solution is to show user interactions from a class, that is part of the view component.
This means, such a class must be a class that is unknown to the view model and therefore can't be invoked by the view model.
The solution of course can involve code-behind implementations as code-behind is not relevant when evaluating whether a solution complies with MVVM or not.
Beside answering the original question, this answer also tries to provide an alternative view on the general problem why controlling a UI component like a dialog from the view model violates the MVVM design pattern and why workarounds like a dialog service don't solve the problem.
1 MVVM and dialogs
1.1 Critique of common suggestions
Almost all answers are following the misconception that MVVM is a pattern, that targets class level dependencies and also requires empty code-behind files. But it's an architectural pattern, that tries to solve a different problem - on application/component level: keeping the business domain decoupled from the UI.
Most people (here on SO) agree that the view model should not handle dialogs, but then propose to move the UI related logic to a helper class (doesn't matter if it's called helper or service), which is still controlled by the view model.
This (especially the service version) is also known as dependency hiding. Many patterns do this. Such patterns are considered anti-patterns. Service Locator is the most famous dependency hiding anti-pattern.
That is why I would call any pattern that involves extraction of the UI logic from the view model class to a separate class an anti-pattern too. It does not solve the original problem: how to change the application structure or class design in order to remove the UI related responsibilities from a view model (or model) class and move it back to a view related class.
In other words: the critical logic remains being a part of the view model component.
For this reason, I do not recommend to implement solutions, like the accepted one, that involve a dialog service (whether it is hidden behind an interface or not). If you are concerned to write code that complies with the MVVM design pattern, then simply don't handle dialog views or messaging inside the view model.
Introducing an interface to decouple class level dependencies, for example an IFileDialogService interface, is called Dependency Inversion principle (the D in SOLID) and has nothing to do with MVVM. When it has no relevance in terms of MVVM, it can't solve an MVVM related problem. When room temperature does not have any relevance whether a structure is a four story building or a skyscraper, then changing the room temperature can never turn any building into a skyscraper. MVVM is not a synonym for Dependency Inversion.
MVVM is an architectural pattern while Dependency Inversion is an OO language principle that has nothing to do with structuring an application (aka software architecture). It's not the interface (or the abstract type) that structures an application, but abstract objects or entities like components or modules e.g. Model - View - View Model. An interface can only help to "physically" decouple the components or modules. It doesn't remove component associations.
1.2 Why dialogs or handling Window in general feels so odd?
We have to keep in mind that the dialog controls like Microsoft.Win32.OpenFileDialog are "low level" native Windows controls. They don't have the necessary API to smoothly integrate them into a MVVM environment. Because of their true nature, they have some limitations the way they can integrate into a high level framework like WPF. Dialogs or native window hosts in general, are a known "weakness" of all high level frameworks like WPF.
Dialogs are commonly based on the Window or the abstract CommonDialog class. The Window class is a ContentControl and therefore allows styles and templates to target the content.
One big limitation is, that a Window must always be the root element. You can't add it as a child to the visual tree and e.g. show/launch it using triggers or host it in a DataTemplate.
In case of the CommonDialog, it can't be added to the visual tree, because it doesn't extend UIElement.
Therefore, Window or CommonDialog based types must always be shown from code-behind, which I guess is the reason for the big confusion about handling this kind of controls properly.
In addition, many developers, especially beginners that are new to MVVM, have the perception that code-behind violates MVVM.
For some irrational reason, they find it less violating to handle the dialog views in the view model component.
Due to it's API, a Window looks like a simple control (in fact, it extends ContentControl). But underneath, it hooks into to the low level of the OS. There is a lot of unmanaged code necessary to achieve this. Developers that are coming from low level C++ frameworks like MFC know exactly what's going on under the hoods.
The Window and CommonDialog class are both true hybrids: they are part of the WPF framework, but in order to behave like or actually to be a native OS window, they must be also part of the low level OS infrastructure.
The WPF Window, as well as the CommonDialog class, is basically a wrapper around the complex low level OS API. That's why this controls have sometimes a strange feel (from the developer point of view), when compared to common and pure framework controls.
That Window is sold as a simple ContentControl is quite deceptive. But since WPF is a high level framework, all low level details are hidden from the API by design.
We have to accept that we have to handle controls based on Window and CommonDialog using C# only - and that code-behind does not violate any design pattern at all.
If you are willing to waive the native look and feel and the general OS integration to get the native features like theming and task bar, you can improve the handling by creating a custom dialog e.g., by extending Control or Popup, that exposes relevant properties as DependencyProperty. You can then set up data bindings and XAML triggers to control the visibility, like you usually would.
1.3 Why MVVM?
Without a sophisticated design pattern or application structure, developers would e.g., directly load database data to a table control and mix UI logic with business logic. In such a scenario, changing to a different database would break the UI. But even worse, changing the UI would require to change the logic that deals with the database. And when changing the logic, you would also need to change the related unit tests.
The real application is the business logic and not the fancy GUI.
You want to write unit tests for the business logic - without being forced to include any UI.
You want to modify the UI without modifying the business logic and unit tests.
MVVM is a pattern that solves this problems and allows to decouple the UI from the business logic i.e. data from views. It does this more efficiently than the related design patterns MVC and MVP.
We don't want to have the UI bleed into the lower levels of the application. We want to separate data from data presentation and especially their rendering (data views). For example, we want to handle database access without having to care which libraries or controls are used to view the data. That's why we choose MVVM. For this sake, we can't allow to implement UI logic in components other than the view.
1.4 Why moving UI logic from a class named ViewModel to a separate class still violates MVVM
By applying MVVM, you effectively structuring the application into three components: model, view and view model. It is very important to understand that this partitioning or structure is not about classes. It's about application components.
You may follow the widely spread pattern to name or suffix a class ViewModel, but you must know that the view model component usually contains many classes of which some are not named or suffixed with ViewModel - View Model is an abstract component.
Example:
when you extract functionality, like creating a data source collection, from a big class named MainViewModel and you move this functionality to a new class named ItemCreator, then this class ItemCreator is logically still part of the view model component.
On class level the functionality is now outside the MainViewModel class (while MainViewModel now has a strong reference to the new class, in order to invoke the code). On application level (architecture level), the functionality is still in the same component.
You can project this example onto the often proposed dialog service: extracting the dialog logic from the view model to a dedicated class named DialogService doesn't move the logic outside the view model component: the view model still depends on this extracted functionality.
The view model still participates in the UI logic e.g by explicitly invoking the "service" to control when dialog is shown and to control the dialog type itself (e.g., file open, folder select, color picker etc.).
This all requires knowledge of the UI's business details. Knowledge, that per definition does not belong into the view model component. Of course, such knowlegde introduces a coupling/dependency from the view model component to the view component.
Responsibilities simply don't change because you name a class DialogService instead of e.g. DialogViewModel.
The DialogService is therefore an anti-pattern, which hides the real problem: having implemented view model classes, that depend on UI and execute UI logic.
1.5 Does writing code-behind violates the MVVM design pattern?
MVVM is a design pattern and design patterns are per definition library independent, framework independent and language or compiler independent. Therefore, code-behind is not a topic when talking about MVVM.
The code-behind file is absolutely a valid context to write UI code. It's just another file that contains C# code. Code-behind means "a file with a .xaml.cs extension". It's also the only place for event handlers. And you don't want to stay away from events.
Why does the mantra "No code in code-behind" exist?
For people that are new to WPF, UWP or Xamarin, like those skilled and experienced developers coming from frameworks like WinForms, we have to stress that using XAML should be the preferred way to write UI code. Implementing a Style or DataTemplate using C# (e.g. in the code-behind file) is too complicated and produces code that is very difficult to read => difficult to understand => difficult to maintain.
XAML is just perfect for such tasks. The visually verbose markup style perfectly expresses the UI's structure. It does this far better, than for example C# could ever do. Despite markup languages like XAML may feel inferior to some or not worth learning it, it's definitely the first choice when implementing GUI. We should strive to write as much GUI code as possible using XAML.
But such considerations are absolutely irrelevant in terms of the MVVM design pattern.
Code-behind is simply a compiler concept, realized by the partial directive (in C#). That's why code-behind has nothing to do with any design pattern. That's why neither XAML nor C# can't have anything to do with any design pattern.
2 Solution
Like the OP correctly concludes:
"I don't really want to do this [open a file picker dialog] in my
ViewModel(where 'Browse' is referenced via a DelegateCommand).
Because I believe that goes against MVVM methodology.
2.1 Some fundamental considerations
A dialog is a UI control: a view.
The handling of a dialog control or a control in general e.g. showing/hiding is UI logic.
MVVM requirement: the view model does not know about the existence of an UI or users. Because of this, a control flow that requires the view model to actively wait or call for user input, really requires some re-design: it is a critical violation and breaks the architectural boundaries dictated by MVVM.
Showing a dialog requires knowledge about when to show it and when to close it.
Showing the dialog requires to know about the UI and user, because the only reason to show a dialog is to interact with the user.
Showing the dialog requires knowledge about the current UI context (in order to choose the appropriate dialog type).
It is not the dependency on assemblies or classes like OpenFileDialog or UIElement that breaks the MVVM pattern, but the implementation or reference of UI logic in the view model component or model component (although such a dependency can be a valuable hint).
For the same reasons, it would be wrong to show the dialog from the model component too.
The only component responsible for UI logic is the view component.
From an MVVM point of view, there is nothing like C#, XAML, C++ or VB.NET. Which means, there is nothing like partial or the related infamous code-behind file (*.xaml.cs). The concept of code-behind exists to allow the compiler to merge the XAML part of a class with its C# part. After that merge, both files are treated as a single class: it's a pure compiler concept. partial is the magic that enables to write class code using XAML (the true compiler language is still C# or any other IL compliant language).
ICommand is an interface of the .NET library and therefore not a topic when talking about MVVM. It's wrong to believe that every action has to be triggered by an ICommand implementation in the view model.
Events are still a very useful concept that conform with MVVM, as long as the unidirectional dependency between the components is maintained. Always forcing the use of ICommand instead of using events leads to unnatural and smelly code like the code presented by the OP.
There is no such "rule" that ICommand must only be implemented by a view model class. It can be implemented by a view class too.
In fact, views commonly implement RoutedCommand (or RoutedUICommand), which both are implementions of ICommand, and can also be used to trigger the display of a dialog from e.g., a Window or any other control.
We have data binding to allow the UI to exchange data with the view model (anonymously, from the data source point of view). But since data binding can't invoke operations (at least in WPF - e.g., UWP allows this), we have ICommand and ICommandSource to realize this.
Interfaces in general are not a relevant concept of MVVM. Therefore, introducing an interface (e.g., IFileDialogService) can never solve a MVVM related problem.
Services or helper classes are not a concept of MVVM. Therefore, introducing services or helper classes can never solve a MVVM related problem.
Classes an their names or type names in general are not relevant in terms of MVVM. Moving view model code to a separate class, even if that class is not named or suffixed with ViewModel, can't solve a MVVM related problem.
2.2 Conclusion
The solution is to show user interactions from a class, that is part of the view component.
This means, such a class must be a class that is unknown to the view model and therefore can't be invoked by the view model.
This logic could be implemented directly in the code-behind file or inside any other class (file). The implementation can be a simple helper class or a more sophisticated (attached) behavior.
The point is: the dialog i.e. the UI component must be handled by the view component alone, as this is the only component that contains UI related logic. Since the view model does not have any knowledge of a view, it can't act actively to communicate with the view. Only passive communication is allowed (data binding, events).
We can always implement a certain flow using events raised by the view model that can be observed by the view in order to take actions like interacting with the user using a dialog.
There exist solutions using the view-model-first approach, which is does not violate MVVM in the first place. But still, badly designed responsibilities can turn this solution into an anti-pattern too.
3 How to fix the need for certain dialog requests
Most of the times, we can eliminate the need to show dialogs from within the application by fixing the application's design.
Since dialogs are a UI concept to enable interaction with the user, we must evaluate dialogs using UI design rules.
Maybe the most famous design rules for UI design are the 10 rules postulated by Nielsen and Molich in the 90's.
One important rule is about error prevention: it states that
a) we must prevent any kind of errors, especially input related, because
b) the user does not like his productivity to be interrupted by error messages and dialogs.
a) means: input data validation. Don't allow invalid data to enter the business logic.
b) means: avoid showing dialogs to the user, whenever possible. Never show a dialog from within the application and let the user trigger dialogs explicitly e.g., on mouse click (no unexpected interruption).
Following this simple rule certainly always eliminates the need to show a dialog triggered by the view model.
From the user's perspective, an application is a black box: it outputs data, accepts data and processes the input data. If we control the data input to guard against invalid data, we eliminate undefined or illegal states and ensure data integrity. This would mean that there is no need to ever show a dialog to the user from inside the application. Only those explicitly triggered by the user.
For example, a common scenario is that our model needs to persist data in a file. If the destination file already exists, we want to ask the user to confirm to overwrite this file.
Following the rule of error prevention, we always let the user pick files in the first place: whether it is a source file or a destination file, it's always the user who specifies this file by explicitly picking it via a file dialog. This means, the user must also explicitly trigger the file operation, for example by clicking on a "Save As" button.
This way, we can use a file picker or file save dialog to ensure only existing files are selected. As a bonus, we additionally eliminate the need to warn the user about overwriting existing files.
Following this approach, we have satisfied a) "[...]prevent any kind of errors, especially input related" and b) "[...]the user does not like to be interrupted by error messages and dialogs".
Update
Since people are questioning the fact that you don't need a view model to handle the dialog views, by coming up with extra "complicated" requirements like data validation to proof their point, I am forced to provide more complex examples to address these more complex scenarios (that were not initially requested by the OP).
4 Examples
4.1 Overview
The scenario is a simple input form to collect a user input like an album name and then use the OpenFileDialog to pick a destination file where the album name is saved to.
Three simple solutions:
Solution 1: Very simple and basic scenario, that meets the exact requirements of the question.
Solution 2: Solution that enables to use data validation in the view model. To keep the example simple, the implementation of INotifyDataErrorInfo is omitted.
Solution 3: Another, more elegant solution that uses an ICommand and the ICommandSource.CommandParameter to send the dialog result to the view model and execute the persistence operation.
Solution 1
The following example provides a simple and intuitive solution to show the OpenFileDialog in a MVVM compliant way.
The solution allows the view model to remain unaware of any UI components or logic.
You can even consider to pass a FileStream to the view model instead of the file path. This way, you can handle any errors, while creating the stream, directly in the UI e.g., by showing a dialog if needed.
View
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<StackPanel>
<!-- The data to persist -->
<TextBox Text="{Binding AlbumName}" />
<!-- Show the file dialog.
Let user explicitly trigger the file save operation.
This button will be disabled until the required input is valid -->
<Button Content="Save as"
Click="SaveAlbumNameToFile_OnClick" />
</StackPanel>
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
=> InitializeComponent();
private void SaveAlbumNameToFile_OnClick(object sender, EventArgs e)
{
var dialog = new OpenFileDialog();
if (dialog.ShowDialog() == true)
{
// Consider to create the FileStream here to handle errors
// related to the user's picked file in the view.
// If opening the FileStream succeeds, we can pass it over to the viewmodel.
string destinationFilePath = dialog.FileName;
(this.DataContext as MainViewModel)?.SaveAlbumName(destinationFilePath);
}
}
}
View Model
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
// Raises PropertyChanged
public string AlbumName { get; set; }
// A model class that is responsible to persist and load data
private DataRepository DataRepository { get; }
public MainViewModel() => this.DataRepository = new DataRepository();
// Since 'destinationFilePath' was picked using a file dialog,
// this method can't fail.
public void SaveAlbumName(string destinationFilePath)
=> this.DataRepository.SaveData(this.AlbumName, destinationFilePath);
}
Solution 2
A more realistic solution is to add a dedicated TextBox as input field to enable collection of the destination file path via copy&paste.
This TextBox is bound to the view model class, which ideally implements INotifyDataErrorInfo to validate the file path before it is used.
An additional button will open the optional file picker view to allow the user to alternatively browse the file system to pick a destination.
Finally, the persistence operation is triggered by a "Save As" button:
View
MainWindow.xaml
<Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<StackPanel>
<!-- The data to persist -->
<TextBox Text="{Binding AlbumName}" />
<!-- Alternative file path input, validated using INotifyDataErrorInfo validation
e.g. using File.Exists to validate the file path -->
<TextBox x:Name="FilePathTextBox"
Text="{Binding DestinationPath, ValidatesOnNotifyDataErrors=True}" />
<!-- Option to search a file using the file picker dialog -->
<Button Content="Browse" Click="PickFile_OnClick" />
<!-- Let user explicitly trigger the file save operation.
This button will be disabled until the required input is valid -->
<Button Content="Save as"
Command="{Binding SaveAlbumNameCommand}" />
</StackPanel>
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void PickFile_OnClick(object sender, EventArgs e)
{
var dialog = new OpenFileDialog();
if (dialog.ShowDialog() == true)
{
this.FilePathTextBox.Text = dialog.FileName;
// Since setting the property explicitly bypasses the data binding,
// we must explicitly update it by calling BindingExpression.UpdateSource()
this.FilePathTextBox
.GetBindingExpression(TextBox.TextProperty)
.UpdateSource();
}
}
}
View Model
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
private string albumName;
public string AlbumName
{
get => this.albumName;
set
{
this.albumName = value;
OnPropertyChanged();
}
}
private string destinationPath;
public string DestinationPath
{
get => this.destinationPath;
set
{
this.destinationPath = value;
OnPropertyChanged();
ValidateDestinationFilePath();
}
}
public ICommand SaveAlbumNameCommand => new RelayCommand(
commandParameter => ExecuteSaveAlbumName(this.TextValue),
commandParameter => true);
// A model class that is responsible to persist and load data
private DataRepository DataRepository { get; }
// Default constructor
public MainViewModel() => this.DataRepository = new DataRepository();
private void ExecuteSaveAlbumName(string destinationFilePath)
{
// Use a aggregated/composed model class to persist the data
this.DataRepository.SaveData(this.AlbumName, destinationFilePath);
}
}
Solution 3
The following solution is a more elegant version of the second scenario. It uses the ICommandSource.CommandParameter property to send the dialog result to the view model (instead of the data binding used in the previous example).
The validation of the optional user input (e.g. copy&paste) is validated using binding validation:
View
MainWindow.xaml
<Window x:Name="Window">
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<StackPanel>
<!-- The data to persist -->
<TextBox Text="{Binding AlbumName}" />
<!-- Alternative file path input, validated using binding validation
e.g. using File.Exists to validate the file path -->
<TextBox x:Name="FilePathTextBox">
<TextBox.Text>
<Binding ElementName="Window" Path="DestinationPath">
<Binding.ValidationRules>
<FilePathValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<!-- Option to search a file using the file picker dialog -->
<Button Content="Browse" Click="PickFile_OnClick" />
<!-- Let user explicitly trigger the file save operation.
This button will be disabled until the required input is valid -->
<Button Content="Save as"
CommandParameter="{Binding ElementName=Window, Path=DestinationPath}"
Command="{Binding SaveAlbumNameCommand}" />
</StackPanel>
</Window>
FilePathValidationRule.cs
class FilePathValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
=> value is string filePath && File.Exists(filePath)
? ValidationResult.ValidResult
: new ValidationResult(false, "File path does not exist.");
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public static readonly DependencyProperty DestinationPathProperty = DependencyProperty.Register(
"DestinationPath",
typeof(string),
typeof(MainWindow),
new PropertyMetadata(default(string)));
public string DestinationPath
{
get => (string)GetValue(MainWindow.DestinationPathProperty);
set => SetValue(MainWindow.DestinationPathProperty, value);
}
public MainWindow()
{
InitializeComponent();
}
private void PickFile_OnClick(object sender, EventArgs e)
{
var dialog = new OpenFileDialog();
if (dialog.ShowDialog() == true)
{
this.DestinationPath = dialog.FileName;
}
}
}
View Model
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
private string albumName;
public string AlbumName
{
get => this.albumName;
set
{
this.albumName = value;
OnPropertyChanged();
}
}
public ICommand SaveAlbumNameCommand => new RelayCommand(
commandParameter => ExecuteSaveAlbumName(commandParameter as string),
commandParameter => true);
// A model class that is responsible to persist and load data
private DataRepository DataRepository { get; }
// Default constructor
public MainViewModel() => this.DataRepository = new DataRepository();
private void ExecuteSaveAlbumName(string destinationFilePath)
{
// Use a aggregated/composed model class to persist the data
this.DataRepository.SaveData(this.AlbumName, destinationFilePath);
}
}
The best thing to do here is use a service.
A service is just a class that you access from a central repository of services, often an IOC container. The service then implements what you need like the OpenFileDialog.
So, assuming you have an IFileDialogService in a Unity container, you could do...
void Browse(object param)
{
var fileDialogService = container.Resolve<IFileDialogService>();
string path = fileDialogService.OpenFileDialog();
if (!string.IsNullOrEmpty(path))
{
//Do stuff
}
}
I would have liked to comment on one of the answers, but alas, my reputation is not high enough to do so.
Having a call such as OpenFileDialog() violates the MVVM pattern because it implies a view (dialog) in the view model. The view model can call something like GetFileName() (that is, if simple binding is not sufficient), but it should not care how the file name is obtained.
The ViewModel should not open dialogs or even know of their existence. If the VM is housed in a separate DLL, the project should not have a reference to PresentationFramework.
I like to use a helper class in the view for common dialogs.
The helper class exposes a command (not an event) which the window binds to in XAML. This implies the use of RelayCommand within the view. The helper class is a DepencyObject so it can bind to the view model.
class DialogHelper : DependencyObject
{
public ViewModel ViewModel
{
get { return (ViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(ViewModel), typeof(DialogHelper),
new UIPropertyMetadata(new PropertyChangedCallback(ViewModelProperty_Changed)));
private static void ViewModelProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (ViewModelProperty != null)
{
Binding myBinding = new Binding("FileName");
myBinding.Source = e.NewValue;
myBinding.Mode = BindingMode.OneWayToSource;
BindingOperations.SetBinding(d, FileNameProperty, myBinding);
}
}
private string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
private static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register("FileName", typeof(string), typeof(DialogHelper),
new UIPropertyMetadata(new PropertyChangedCallback(FileNameProperty_Changed)));
private static void FileNameProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("DialogHelper.FileName = {0}", e.NewValue);
}
public ICommand OpenFile { get; private set; }
public DialogHelper()
{
OpenFile = new RelayCommand(OpenFileAction);
}
private void OpenFileAction(object obj)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == true)
{
FileName = dlg.FileName;
}
}
}
The helper class needs a reference to the ViewModel instance. See the resource dictionary. Just after construction, the ViewModel property is set (in the same line of XAML). This is when the FileName property on the helper class is bound to the FileName property on the view model.
<Window x:Class="DialogExperiment.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DialogExperiment"
xmlns:vm="clr-namespace:DialogExperimentVM;assembly=DialogExperimentVM"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:ViewModel x:Key="viewModel" />
<local:DialogHelper x:Key="helper" ViewModel="{StaticResource viewModel}"/>
</Window.Resources>
<DockPanel DataContext="{StaticResource viewModel}">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Open" Command="{Binding Source={StaticResource helper}, Path=OpenFile}" />
</MenuItem>
</Menu>
</DockPanel>
</Window>
I use a service which i for example can pass into the constructor of my viewModel or resolve via dependency injection.
e.g.
public interface IOpenFileService
{
string FileName { get; }
bool OpenFileDialog()
}
and a class implementing it, using OpenFileDialog under the hood. In the viewModel, i only use the interface and thus can mock/replace it if needed.
I have solved it for me this way:
In ViewModel I have defined an interface and work with it in
ViewModel
In View I have implemented this interface.
CommandImpl is not implemented in code below.
ViewModel:
namespace ViewModels.Interfaces
{
using System.Collections.Generic;
public interface IDialogWindow
{
List<string> ExecuteFileDialog(object owner, string extFilter);
}
}
namespace ViewModels
{
using ViewModels.Interfaces;
public class MyViewModel
{
public ICommand DoSomeThingCmd { get; } = new CommandImpl((dialogType) =>
{
var dlgObj = Activator.CreateInstance(dialogType) as IDialogWindow;
var fileNames = dlgObj?.ExecuteFileDialog(null, "*.txt");
//Do something with fileNames..
});
}
}
View:
namespace Views
{
using ViewModels.Interfaces;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
public class OpenFilesDialog : IDialogWindow
{
public List<string> ExecuteFileDialog(object owner, string extFilter)
{
var fd = new OpenFileDialog();
fd.Multiselect = true;
if (!string.IsNullOrWhiteSpace(extFilter))
{
fd.Filter = extFilter;
}
fd.ShowDialog(owner as Window);
return fd.FileNames.ToList();
}
}
}
XAML:
<Window xmlns:views="clr-namespace:Views"
xmlns:viewModels="clr-namespace:ViewModels">
<Window.DataContext>
<viewModels:MyViewModel/>
</Window.DataContext>
<Grid>
<Button Content = "Open files.." Command="{Binding DoSomeThingCmd}"
CommandParameter="{x:Type views:OpenFilesDialog}"/>
</Grid>
</Window>
Having a service is like opening up a view from viewmodel.
I have a Dependency property in view, and on the chnage of the property, I open up FileDialog and read the path, update the property and consequently the bound property of the VM

Resources