How can I switch views between viewmodels? - c#

My Main window defines the markup for the application, for this specific scenario lets say I have a grid with 2 columns.
First column will have navigation links, and second column will display the different views.
There are 2 views (and 2 viewmodels) defined in mainwindow xaml:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:Window1ViewModel}">
<vw:Window1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Window2ViewModel}">
<vw:Window2View/>
</DataTemplate>
</Window.Resources>
And in second grid column that displays the views i got :
<ContentControl Content="{Binding Path=ViewModel}" HorizontalAlignment="Left">
</ContentControl>
Where ViewModel is a property that I set accordingly to a view(viewmodel) that i want to display.
Like :
ViewModel = new Window1ViewModel();
(datacontext of the mainwindowview is set to MainWindowViewModel)
So there is no problem to switch between views from the MainWindowViewModel.
My problem is how to switch within Window1ViewModel into Window1ViewMode2?
The various ViewModels don't "know" about other ViewModels.
Only MainWindowViewModel knos about others...
How can I solve this?
Maybe I should define a custom Event (with parameter), MainWindowViewModel will subscribe and other viewmodels will trigger it and then MainWindowViewModel will switch to the needed view?

the solution you describe is one possibility. One other I can think of is using some kind of Navigation-Service (static class or interface you pass to all your child-Viewmodels) that do this kind of work.
If your MainWindowViewModel creates all the others I would stick to the interface solution. You can for example let the MainWindowVM implement such a interface and inject it into all the child-vm on creation. This is much the same as your event-approach but instead of the childs-providing and the main having to subscribe you have the main give something ... IMHO the better approach.

Ok, may be I understood your point. You want that controller actually be modelview which notifies to mainmodelview about the fact that it have to be swapped with someone else.
Considering that we are talking about WPF, create DependecyProperty on mainmodelview , and set it from childview, which in code behind will trigger modelviews swap.

Related

MVVM and dynamic generation of controls

i've written a tool that generates sql queries using GUI, i want to rewrite the tool using MVVM and WPF, every sql column type has a different control as you can see in the following image
i add a column filter control based on the sql column type, and i generate the controls using code, just like i used to do in windows forms.
in MVVM i've read that the view is writtien enteirly using XAML,
does MVVM suite such application where i have to add different user
controls dynamically to a stack panel?
The controls won't exist in the view unless some column is double clicked, that means the control won't be available in the xaml and won't be hidden or collapsed.
is there any way that i can avoid the bindings in the code behind?
should i create a user control for each column type?
in general what is the best approach to devlop such application with complex and dynamic ui using mvvm?
Guess I know how to achieve that, but it is very complex stuff. First you should comprehend MVVM basic concepts.
Main ViewModel should be a class with ObservableCollection of ViewModels, each of them represents a column with its data and properties.
interface IViewModel : INotifyPropertyChanged,IDisposable
{
}
interface IColumnViewModel : IViewModel
{
}
class ViewModelBase : IViewModel
{
// ... MVVM basics, PropertyChanged etc. ...
}
class MainViewModel : ViewModelBase
{
ObservableCollection<IColumnViewModel> Columns {get; set}
}
In View I suppose something like ItemsControl with ItemTemplate, that should embed ContentControl with DataTemplate, that shall be automatically selected by WPF according to binded DataContext of list item. StackPanel itself is not suitable for that, but it can be invoked as ItemsPanelTemplate
<Window
xmlns:v="clr-namespace:WpfApplication.Views"
xmlns:vm="clr-namespace:WpfApplication.ViewModels">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=vm:TextColumnViewModel}">
<v:TextColumnView/>
</DataTemplate>
</Window.Resources>
<ItemsControl
ItemsSource="{Binding Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
So, you should build View/ViewModel pair for every column type.
Hope, my example will help. Good luck with your girlfriend and MVVM :)
If I've understood your scenario correctly :
You can use Data Templates & Items Templates
For example I've written an application which loads Data into Collection and then shows each item of that collection in a Wrap Panel [ Or stack panel ] based on defined data template.
And Wrap penel items are in sync by the collection itself within two way binding
You should consider using Observable Collections to achieve this goal
Then you can fill the collection and see the results on a view
I hope this helps
To write something like this in MVVM, you would have one view that is say, your content area. That view would have a view model, one of the properties of that view model would be a view, or several properties of that view model would be a view. It takes a bit to wrap your head around at times, but if you use Inversion of Control and Dependency Injection properly a view of views is very manageable in an MVVM pattern.
Well, your view isn't written entirely in XAML - you generate controls in C#.
I don't think you'll gain something from rewriting this and fitting it into an MVVM mold. Just keep the code as it is now and enjoy.

Create view object in ViewModel

I have the following code in my C# WPF MVVM application.
public RelayCommand PolishCommand
{
get
{
polishcommand = new RelayCommand(e =>
{
PolishedWeightCalculatorViewModel model = new PolishedWeightCalculatorViewModel(outcomeIndex, OutcomeSelectedItem.RoughCarats);
PolishedWeightCalculatorView polish = new PolishedWeightCalculatorView(model);
bool? result = polish.ShowDialog();
if (result.HasValue)
{
But i came to know that, calling a window from viewmodel is wrong one in MVVM pattern.
Also stated in the below link.
M-V-VM Design Question. Calling View from ViewModel
Please help me anybody by providing an alternate solution.
Thanks in advance.
You are right that generally you should never access views from view models. Instead in WPF, we set the DataContext property of the view to be an instance of the relating view model. There are a number of ways to do that. The simplest but least correct is to create a new WPF project and put this into the constructor of MainWindow.xaml.cs:
DataContext = this;
In this instance the 'view model' would actually be the code behind for the MainWindow 'view'... but then the view and view model are tied together and this is what we try to avoid by using MVVM.
A better way is to set the relationship in a DataTemplate in the Resources section (I prefer to use App.Resources in App.xaml:
<DataTemplate DataType="{x:Type ViewModels:YourViewModel}">
<Views:YourView />
</DataTemplate>
Now wherever you 'display' a view model in the UI, the relating view will automatically be shown instead.
<ContentControl Content="{Binding ViewModel}" />
A third way is to create an instance of the view model in the Resources section like so:
<Window.Resources>
<ViewModels:YourViewModel x:Key="ViewModel" />
</Window.Resources>
You can then refer to it like so:
<ContentControl Content="{Binding Source={StaticResource ViewModel}}" />
I have answered a very similar question previously, which details how you can open a new window from your view model, whilst maintaining the separation of concerns that the MVVM pattern promotes. I hope this helps: Open a new Window in MVVM
You are allowed to break the rule. You don't have to follow MVVM completely.
I am always using commands to create a new view. You could even create an event (Amagosh, did he just say that!?) for when you click on a button.
I mean, this is just my opinion, I guess it depends on the style programming you're into.

Bind multiple views to multiple viewmodels

I have a WPF window displaying different self-defined Views. So far I was able to use everything I learned about MVVM :)
Now I got to a new "problem": I have 10 entities of the same view in a bigger view. These ten view-entities contain a set of controls (textbox, combobox etc.) but are all consistent.
So how do I bind these Views to a ViewModel?
I thought about having 10 instances of the ViewModel in the "higher-level" ViewModel and give the views fix-defined the instances of the VM as datacontext.
My question is now --> Is there a easier (or more convienient) way to bind many (identical) views to their viewmodels?
Code-Example:
View Model:
private PanelViewModel _panelViewModel1 = new PanelViewModel();
public PanelViewModel PanelVM1
{
get { return _panelViewModel1; }
}
View-Example:
<myControls:vwPanel HorizontalAlignment="Left" x:Name="vwPanel1"
VerticalAlignment="Top" DataContext="{Binding Path=PanelVM1}"/>
What bothers me is that I would need this logic ten times for ten views?
UPDATE:
To answer some questions: I want to show one view 10 times (in my example) I defined my own view by inheriting from UserControl. So my vwPanel inherits from UserControl. The 10 vwPanels are just placed inside a StackPanel inside a Grid.
It's not about displaying data, as you pointed out, there would be a listview or a datagrid a better place to start. It's a special case where I need this much input-controls :/
UPDATE2: What I hoped for was more like defining a List of ViewModels and Bind my 10 Views to one of this List. But this will not work will it? At least I wouldn't know how to refernce one "special" entitiy in the list out of XAML...
Typically I use implicit DataTemplates for mapping Views to ViewModels. They can go in <Application.Resources>, <Window.Resources> or even in under specific elements only such as <TabControl.Resources>
<DataTemplate DataType="{x:Type local:PanelViewModel}">
<myControls:vwPanel />
</DataTemplate>
This means that anytime WPF encounters an object in the VisualTree of type PanelViewModel, it will draw it using vwPanel
Objects typically get placed in the VisualTree through an ItemsSource property
<ItemsControl ItemsSource="{Binding CollectionOfAllPanels}" />
or by using a ContentControl
<ContentControl Content="{Binding PanelVM1}" />
If I understand your question correctly, you have a collection of something that you what to represent visually. That is, you have several viewmodels that you want to define a single view for, but show X number of times. Your example shows you using a panel as your view for the "PanelViewModel"...what is the parent item's control for the vwPanel? Assuming you're using something like a ListBox, you can define a custom DataTemplate that contains your vwPanel and assign that DataTemplate to your ListBox.ItemTemplate.
For example:
<Window.Resources>
<DataTemplate x:Key="myVMTemplate" TargetType="{x:Type myViewModels:PanelViewModel}">
<myControls:vwPanel HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Path=MyCollectionOfPanelVMs}"
ItemTemplate="{StaticResource myVMTemplate}" />
I haven't verified that this works.

How to define the usercontrols in mvvm pattern?

I using from mvvm in my application. I want know how to define my user control in mvvm pattern.
Must I define it by using from mvvm, or I can define it generally?
Let's just call the control that embeds the user control MainWindow, and the user control UserControl. Since you are in MVVM pattern, you have at least one View Model for the outer view - I usually use the name MainVm.
You have two choices for the user control: They can share the same View Model, or you could have a sub view model, just for the UserControl, i.e. UserVm.
For your first choice, you do nothing. You define UserControl (Visual Studio 'add new item' -> User Control is a pretty good start). Then, you simply embed it in Main Window.
<Window
x:Class="SO.MainWindow"
...
xmlns:src="clr-namespace:SO"
...
>
...
<src:UserControl />
...
</Window>
UserControl will inherit the same DataContext from MainWindow, and do all the {Binding} as you would do in the MainWindow.
If you want to have a sub view model (UserVm) - it would typically be a public property of the MainVm (say, userVm). In that case, you'll set the DataContext of the UserControl when you reference it.
<src:UserControl DataContext="{Binding Path=userVm}" />
Another popular paradigm would be to declare the DataTemplate instead of the UserControl. If you do that, you just need to put the UserVm (either instantiate it in the XAML, or through binding):
<Window x:Class="MainWindow" ...>
<Window.Resources>
<DataTemplate x:Key="UserDt"> <!-- or user TargetType instead of x:Key -->
...
</DataTemplate>
</Window.Resources>
...
<!-- You can put in a ContentControl like here: -->
<ContentControl Content="{Binding Path=userVm}"
ContentTemplate="{StaticResource UserDt}" />
<!-- or, if you defined TargetType for the DT, you can simply instantiate
the sub VM here. I don't like this apporach but it exists. -->
<src:UserVm />
</Window>
I think that depends on the user control. The user control can be just a view, in which case you would compose a larger control or page which has this user control as part of the whole. The larger control or page would provide the view and the view model parts for this view.
Or you could create a self contained user control which has all of mvvm and use events to interact with the larger user control that it is a part of.
I suspect you'll get better reuse and modularisation with the second approach.
In short: it depends.

Is there an MVVM-friendly way to swap views without value converters firing unnecessarily?

I thought what I was doing was right out of the Josh Smith MVVM handbook, but I seem to be having a lot of problems with value converters firing when no data in the view-model has changed.
So, I have a ContentControl defined in XAML like this:
<ContentControl Grid.Row="0" Content="{Binding CurrentViewModel}" />
The Window containing this ContentControl references a resource dictionary that looks something like this:
<ResourceDictionary ...>
<DataTemplate DataType="{x:Type lib_vm:SetupPanelViewModel}">
<lib_v:SetupPanel />
</DataTemplate>
<DataTemplate DataType="{x:Type lib_vm:InstructionsPanelViewModel}">
<lib_v:InstructionsPanel />
</DataTemplate>
</ResourceDictionary>
So, basically, the two data templates specify which view to show with which view-model.
This switches the views as expected whenever the CurrentViewModel property on my window's view-model changes, but it also seems to cause value converters on the views to fire even when no data has changed. It's a particular problem with IMultiValueConverter classes, because the values in the value array get set to DependencyProperty.UnsetValue, which causes exceptions unless I specifically check for that. But I'm getting other weird side effects too.
This has me wondering if I shouldn't just do everything manually, like this:
Instantiate each view.
Set the DataContext of each view to the appropriate view-model.
Give the ContentControl a name and make it public.
Handle the PropertyChanged event for the window.
In the event handler, manually set the Content property of the ContentControl to the appropriate view, based the CurrentViewModel (using if statements).
This seems to work, but it also seems very inelegant. I'm hoping there's a better way.
Could you please advise me the best way to handle view switching so that value converters don't fire unnecessarily?
You should look at PRISM or any other composite UI framework. Prism will give you a great mechanism for this type of thing.
I solved this by getting rid of all IValueConverter and IMultiValueConverter classes and just using the ViewModel to provide all data. It turns out, this requires less code and hassle, and doesn't sacrifice anything that I'm aware of.

Categories