Reputation: 3653
We already have an existing UI for our WPF app, where separate button presses would show elements based on bindings to Visibility (MVVM), where an ICommand would set these Visibility properties on or off. These worked well, but recently we were given a requirement for a button press to show/hide elements like before, but another type of button press to show some other elements but WITHIN a TabControl.
The way I thought to do it was to:
a) Create separate UserControls containing those elements
b) These UserControls are instantiated through the ViewModel during runtime depending on the button press, and bound to the View as ItemsSource through an ObservableCollection:
ViewModel:
private ObservableCollection<Object> centerView;
public ObservableCollection<Object> CenterView
{
get { return centerView; }
}
...
UserControlOfReportRelatedElements reportsView = new UserControlOfReportRelatedElements();
CenterView.Clear();
Grid.SetRow(reportsView, 0);
CenterView.Add(reportsView);
XAML:
<ItemsControl ItemsSource="{Binding CenterView}" Grid.Row="2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
So depending on whether or not the UserControl is wrapped in a TabControl, we would simply slip it in the ObservableCollection (during runtime).
This works, and it's convenient. But does this break MVVM? Is there anything else we can do?
Upvotes: 1
Views: 670
Reputation: 387547
Yes, having references to UI elements in the view model breaks MVVM. The view model is supposed to be completely isolated and unaware of the view.
A way to solve this is to just expose the actual data content as a property on the view model. You can then use various mechanisms to show controls based on that content. For example, you if you expose a list of objects, you could then use data templates to show different controls depending on what type an object has.
So you would just expose the data on the view model, and expect the view to provide the necessary means to display that data correctly.
For example instead of exposing UserControlOfReportRelatedElements
controls, you would expose ReportData
objects which contain the data. Then you would register a data template on the ReportData
type that then displays a UserControlOfReportRelatedElements
element and shows the data from the ReportData
object.
Upvotes: 3
Reputation: 9384
I would say, yes that breaks the MVVM-Pattern. One of the ideas behinde MVVM is to keep the View (UI) and the ViewModel (business-logic) seperated from each other.
A solution for you could be to create a Base-ViewModel-Class where the ViewModels for the different views (your tab-pages) are inherited. In your ObserableCollection you have your Base-ViewModel-Instanced.
To display the correct views to your viewmodels you can do something like the following in your App.xaml
<DataTemplate DataType="{x:Type viewModel:CustomViewModel1}">
<view:CustomView1/>
</DataTemplate>
With this definition you tell your application that whenever a CustomViewModel1
should be shown the CustomView1
should be displayed. With this the DataContext is also set.
A really good example for this is this
Upvotes: 3