How can I implement "View Model First" using Prism and Unity? - c#

Clarification
I am working with an MVVM solution. I have a 1 to 1 mapping between ViewModels and Views. All solutions I have seen follow a view first approach where the View type is resolved by an IoC container and has a ViewModel as a dependency. I need to reverse that somehow.
Original post:
I am currently trying to refactor a simple database viewing application from Caliburn Micro to Prism (which I am very new to). The application currently utilizes a ViewModel-First approach and the ShellViewModel maintains a list of ViewModels that is bound to a TabControl.
I can not find how to implement a similar approach in Prism. All solutions I have seen use a view first approach, but I have multiple states all mapping to one type of view and need to keep those states separate.
Is there a way I can configure prism to automatically inject a view when a viewmodel is assigned to a region?
Thank you.

Rachel pointed me to a solution in her comment to the original question.
Instead of trying to implement special prism functionality and prism regions, I have gone with a more straight forward MVVM implementation using DataTemplates.
ViewModel outline:
public abstract class ContainerViewModel : BindableBase
{
public ObservableCollection<ItemViewModel> Items { get; set; }
public ItemViewModel ActiveItem { get; set; }
protected virtual void Add(ItemViewModel item) { ... }
protected virtual void Remove(ItemViewModel item) { ... }
protected virtual void Activate(ItemViewModel item) { ... }
}
And XAML:
<TabControl Grid.Column="1" ItemsSource="{Binding Items}" SelectedItem="{Binding ActiveItem}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Table.TableName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type viewModels:QueryViewModel}">
<local:QueryView />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>

Have a look at this code project article (ignoring the part about child containers): http://www.codeproject.com/Articles/640573/ViewModel-st-Child-Container-PRISM-Navigation

Related

How to navigate between page and open window using MVVM in WPF?

I have read several article, tutorial, example. But I'm still unable to make navigation between page and windows happens.
> Visual Studio Community 2019
> .NET Framework: 4.7.2
To make it simple, I have 3 pages and 1 window ALL VIEW IS FULL SCREEN FOR KIOSK APPLICATION. Details page as below:
MVVMApps.sln
│
├── PageInitialize.xaml
├── PageHome.xaml
└──── PageSelectLanguage.xaml
└── WinMessage.xaml
I have try MvvmLight by GalaSoft but stuck on navigate from PageInitialize.xaml to PageHome.xaml. And I just found article from GalaSoft that WPF is coming soon for INavigationService. Most tutorial I found is sampling for Xamarin.
Plus I got undefined assembly using Microsoft.Practices.ServiceLocation; which I see it's available on Enterprise. On Nuget, installing MvvmLight will install CommonServiceLocator too.
public class ViewModelLocator
{
private static bool initialized;
public ViewModelLocator()
{
//Fix to keep blend happy
if (initialized) { return; }
initialized = true;
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<PageInitializeViewModel>();
SimpleIoc.Default.Register<PageHomeViewModel>();
SimpleIoc.Default.Register<PageSelectLanguageViewModel>();
SimpleIoc.Default.Register<WinMessageViewModel>();
SetupNavigation();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public PageInitializeViewModel PageInitializeViewModel => ServiceLocator.Current.GetInstance<PageInitializeViewModel>();
public PageHomeViewModel PageHomeViewModel => ServiceLocator.Current.GetInstance<PageHomeViewModel>();
public WinMessageViewModel WinMessageViewModel=> ServiceLocator.Current.GetInstance<WinMessageViewModel>();
public static void Cleanup()
{
// TODO Clear the ViewModels
}
private void SetupNavigation()
{
var navigationService = new Helpers.NavigationService<Helpers.NavigationPage>();
navigationService.ConfigurePages();
SimpleIoc.Default.Register<Helpers.INavigationService<Helpers.NavigationPage>>(() => navigationService);
}
}
I have go through some of tutorial without MvvmLight:-
Navigating between views in WPF / MVVM
Navigation with MVVM
MVVMTest
Each article use different approach and since I'm not familiar with it, once error line appear and no Intellisense suggestion, I cannot continue to find the solutions.
Is it hard to use MVVM in WPF if I have multiple Page and Window screen? Currently, I have a complete WPF App but it use code-behind. I want to move to MVVM since I've read that MVVM is better than code-behind somewhere. MVVM for single page is not a problem as I have done before and it is totally awesome when using MVVM.
Should I retain to use code-behind in WPF if navigation is almost impossible to have workable answer?
Start simple and set navigationservice, viewmodellocators and everything but the basics to one side for now.
I would avoid all those ....locator classes anyhow. They necessarily rely on an anti pattern IMO.
Just use viewmodel first and a single window app.
The basic pattern involves a MainWindow, the Datacontext of which is MainWindowViewModel.
You might want a menu or some such but the part you will switch out is the content of a contentcontrol.
Bind the Content property of your ContentControl to a public object property on mainwindowviewmodel. Call that CurrentViewModel for the sake of discussion.
Define a viewmodel and usercontrol per view you will switch between. Thus HomeView and HomeViewModel, LoginView and LoginViewModel. And so on.
In a resource dictionary, create a datatemplate for each view associating your usercontrol with the type of it's viewmodel.
Merge this resource dictionary in app.xaml.
To navigate.
Instantiate a new viewmodel of the sort you need.
Set CurrentViewModel to that instance.
It will then be templated into UI.
There are numerous variations of this - it's called viewmodel first and you should be able to easily google a few examples.
Here's one I wrote for a slightly different purpose:
https://social.technet.microsoft.com/wiki/contents/articles/52485.wpf-tips-and-tricks-using-contentcontrol-instead-of-frame-and-page-for-navigation.aspx
You can use a similar approach with pages if you really want pages:
A simplified view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:Page1ViewModel}">
<local:Page1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Page2ViewModel}">
<local:Page2/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ViewSelect"
ItemsSource="{Binding ViewChoices}"
SelectedItem="{Binding SelectedViewChoice, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Frame Grid.Column="1"
Content="{Binding SelectedViewChoice.VM}"
NavigationUIVisibility="Hidden"
/>
</Grid>
</Window>
The very much simplified viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<ViewChoice> ViewChoices { get; set; }
= new ObservableCollection<ViewChoice>
{
new ViewChoice{ Name="Page One", VM=new Page1ViewModel()},
new ViewChoice{ Name="Page Two", VM=new Page2ViewModel()},
};
private ViewChoice selectedViewChoice;
public ViewChoice SelectedViewChoice
{
get { return selectedViewChoice; }
set { selectedViewChoice = value; RaisePropertyChanged(); }
}
ps
If you decide to learn prism I would start with delegatecommand and stop there until you have written at least one wpf app.
There's a HUGE slew of functionality in PRISM and most apps don't actually benefit from regions and dynamic composition.
If you prefer mvvmlight ( I do ) then for core you're best getting the source code and using that. You want commandWPF namespace and this has a reliance on net old in the nuget package. The version that does not will not support command canexecute requery well.
I hope that's sufficient info without being overwhelming. Tricky to both be clear and not drown someone with info.

How to call a view from a view model using MVVM pattern?

I have two view models A and B. On a double click on view A I need to display view B.
How can I call a view B from a view Model A using the MVVM pattern?
I have looked around and I couldn't find a clear example that demonstrate this fundamental concept for the MVVM pattern.
c#
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Mvvm;
using System.Windows.Input;
namespace Example.ViewModels
{
public class ViewModelA : BindableBase
{
public ICommand ShowInfoCommand { get; private set; }
//Need to call view B
private void OnShowInfo(object obj)
{
//To Be Implemented
}
}
}
Well, here's an easy way to do this (assuming you have correctly implemented INotifyPropertyChanged):
Go to your App.xaml and declare some DataTemplates to connect the Views with the ViewModels:
<DataTemplate DataType="{x:Type ViewModels:ViewModelA}">
<Views:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:ViewModelB}">
<Views:ViewB />
</DataTemplate>
Now whenever your application uses ViewModelA or ViewModelB, these DataTemplates will set the correct views.
You can have a content presenter or content control to control which view model to display:
<ContentControl Content="{Binding ViewModel}" />
Then, you will set the ViewModel, whenever you wish to change views:
//Need to call view B
private void OnShowInfo(object obj)
{
ViewModel = new ViewModelB();
}
Well, that's it. Your ViewModel binding of the ContentControl together with the DataTemplates will do the job!
Of course, there are plenty of different approaches to do this. It will depend on your requirements. I'm currently using NavigationService to handle this in one of my projects.

Navigation with Frame and Combobox MVVM

I'm trying to get my head around MVVM, i'm currently stuck on how to handle navigation.
Currently I have a page and within that page is a frame, that frame is responsible for framing in various other pages. Navigation was previously handled with a drop down box and on selection changed it would navigate that way.
I'm not sure how I could do this without touching the frame from the model view which would end up breaking mvvm.
In the end what I am trying to accomplish is, clicking on the combobox, selecting an item and then having then frame below navigate to the correct view.
I'm not using Prism or any other framework with MVVM, just trying to do it all manually.
The ComboBox would display an ObservableCollection of frame items exposed by your main viewmodel, and the viewmodel will have another property for the selected item.
Your main viewmodel and the frame item viewmodels all inherit from a ViewModelBase class which implements INotifyPropertyChanged, and maybe some other stuff.
So, C#:
public ObservableCollection<ViewModelBase> FrameItems { get; protected set; }
private ViewModelBase _selectedFrameItem;
public ViewModelBase SelectedFrameItem {
get { return _selectedFrameItem; }
set {
value = _selectedFrameItem;
// Defined in ViewModelBase
OnPropertyChanged();
}
}
Your main viewmodel will populate FrameItems in its constructor:
public MainViewModel()
{
FrameItems = new ObservableCollection<ViewModelbase> {
new IceCreamMenu(),
new SmurfOptions(),
new MagicSparklePonyFourierTransformConfiguration()
};
}
Every frame item is a subclass of ViewModelBase. It exposes properties with notifications, including ObservableCollections of any set of child things it may have. And we'll display it by writing a datatemplate for it in just a bit.
Let's assume that you've given your ViewModelBase class a String Title { get; set; } property. Or maybe you'll want to write a subclass of ViewModelBase that introduces Title; your call. For now let's put it in ViewModelBase for simplicity.
XAML -- this leaves out all the layout, but you don't need that here.
<ComboBox
ItemsSource="{Binding FrameItems}"
SelectedItem="{Binding SelectedFrameItem}"
DisplayMemberPath="Title"
/>
<Frame Content={Binding SelectedFrameItem}" />
OK, but how on earth does it know what to do with SelectedFrameItem?!
Easy! Write a resource dictionary called, say, ViewModelDataTemplates.xaml, and merge it into App.xaml so its contents are "visible" in any XAML in your application.
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Source is a relative path from project root directory -->
<ResourceDictionary Source="ViewModelDataTemplates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
...plus whatever theme stuff or whatever.
In ViewModelDataTemplates.xaml, define data templates for your frame item classes.
Say you've got an IceCreamMenu viewmodel, with a collection of Flavors
public ObservableCollection<IceCreamFlavor> Flavors { get; protected set; }
...and a SelectedFlavor. You'd define the namespace vm appropriately with an xmlns:vm attribute on the resource dictionary.
ViewModelDataTemplates.xaml
<DataTemplate DataType="{x:Type vm:IceCreamMenu}">
<Grid>
<ListBox
ItemsSource="{Binding Flavors}"
SelectedItem="{Binding SelectedFlavor}"
/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:IceCreamFlavor}">
<StackPanel Orientation="Horizontal">
<Border
Height="20"
Width="20"
Margin="4"
Background={Binding Color, Converter={StaticResource ColorToBrushConverter}}"
/>
<Label Content="Name" />
</StackPanel>
</DataTemplate>
If you've got existing UserControls that you want to use via datatemplates, that's easy: Say you've got a NotesTabView UserControl that's a view for your NotesTabViewModel, you could define a DataTemplate like this:
<DataTemplate DataType="{x:Type vm:NotesTabViewModel}">
<vw:NotesTabView />
</DataTemplate>
#EdPlunkett: As an alternative to DataTemplate for each view, you can bind your frame to selected page viewmodel using ViewModelToViewConverter like I did here: https://stackoverflow.com/a/31721236/475727
Implicit DataTemplates and DataTemplateSelectors are unique to WPF and XAML, so people think it's recommended solution, but I think it's not suitable for navigation. It feels hackish and it smells with violation of DRY principle.

How should communicate child ViewModel and main ViewModel according to MVVM?

I have View with TabControl and Button. My ViewModel began to grow fast, so I decided to split it into a couple of ViewModels for each TabItem. I want my Button IsEnabled property depends on if any checkbox checked in first TabItem.
So my question is how should child ViewModel communicate with main ViewModel. Here is my code for View:
<TabControl>
<TabItem DataContext="{Binding TabItem1ViewModel}">
<ListBox ItemSource="{Binding Items}>
<ListBoxt.ItemTemplate>
<DataTemplate>
<Checkbox IsChecked="{Binding IsChecked}" Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding SelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</TabItem>
//...... Another TabItems
</TabControl>
<Button IsEnabled="{Binding IsAnythingChecked}" Content=""/>
for ViewModel:
MainViewModel:BaseViewModel
{
public TabItem1ViewModel TabItem1ViewModel {get;set;}
public bool IsAnythingChecked
{
get
{
return TabItem1ViewModel.Items.Any(x=>x.IsChecked);
}
}
}
TabItem1ViewModel:BaseViewModel
{
public TabItem1ViewModel()
{
SelectionChangedCommand = new RelayCommand(selectionChanged);
}
public Model Items {get;set;}
public ICommand SelectionChangedCommand {get;set;}
private void selectionChanged()
{
//...
//Some logic here
//...
//And I want to update IsAnythingChecked property of MainViewModel what is the best way to do it?
}
}
for Model:
class Model:INotifyPropertyChanged
{
public bool IsChecked { get;set; }
public string Text { get; set; }
}
Also, is it a good practice to split ViewModel into smaller ones or fat ViewModel is also ok?
The whole point of view model is to separate concerns.
To answer your questions:
1. How should communicate child ViewModel and main ViewModel according to MVVM?
The most common approach that I use and feel at home is with a common message bus. Where you take an instance of e.g. IMessenger in the constructor and then can use it to push events that happen in your view model, and any other interested view model (in your case, your main view model) can subscribe to those events.
Depending what library you are using there probably already is a basic pub/sub messaging bus already implemented (e.g. PRISM or MVVMLight). I will post some links shortly.
2. Is it a good practice to split ViewModel into smaller ones or fat ViewModel is also ok
Definitely, go for smaller view models. If you are testing at all, it is much easier to test smaller view models, and easier for next person to come along and understand what a smaller view model is supposed to do, rather than a mother of all view models! Part of SOLID is Single Responsibility Principal, which really helps when it comes to maintenance and/or testing.

UI design using MVVM pattern

I'm trying to choose the best way to implement this UI in MVVM manner. I'm new to WPF (like 2 month's) but I have huge WinForms experience.
The ListBox here act's like a TabControl (so it switches the view to the right), and contains basically the Type of item's displayed in tables. All UI is dynamic (ListBox items, TabItems and Columns are determined during run-time). The application is targeting WPF and Silverlight.
Classes we need for ViewModel:
public abstract class ViewModel : INotifyPropertyChanged {}
public abstract class ContainerViewModel : ViewModel
{
public IList<ViewModel> Workspaces {get;set;}
public ViewModel ActiveWorkspace {get;set;}
}
public class ListViewModel<TItem> where TItem : class
{
public IList<TItem> ItemList { get; set; }
public TItem ActiveItem { get; set; }
public IList<TItem> SelectedItems { get; set; }
}
public class TableViewModel<TItem> : ListViewModel<TItem> where TItem : class
{
public Ilist<ColumnDescription> ColumnList { get; set; }
}
Now the question is how to wire this to View.
There are 2 base approaches I can see here:
With XAML: due to lack of Generics support in XAML, I will lose strong typing.
Without XAML: I can reuse same ListView<T> : UserControl.
Next, how to wire data, I see 3 methods here (with XAML or without doesn't matter here). As there is no simple DataBinding to DataGrid's Columns or TabControl's TabItems the methods I see, are:
Use DataBinding with IValueConverter: I think this will not work with WPF|Silverlight out of the box control's, as some properties I need are read-only or unbindable in duplex way. (I'm not sure about this, but I feel like it will not work).
Use manual logic by subscribing to INotifyPropertyChanged in View: ViewModel.PropertyChanged+= ....ViewModel.ColumnList.CollectionChanged+= ....
Use custom controll's that support this binding: Code by myself or find 3d party controls that support this binding's (I don't like this option, my WPF skill is too low to code this myself, and I doubt I will find free controls)
Update: 28.02.2011
Things get worser and worser, I decided to use TreeView instead of ListBox, and it was a nightmare. As you probably guess TreeView.SelectedItems is a readonly property so no data binding for it. Ummm all right, let's do it the old way and subscribe to event's to sync view with viewmodel. At this point a suddenly discovered that DisplayMemberPath does nothing for TreeView (ummmm all right let's make it old way ToString()). Then in View's method I try to sync ViewModel.SelectedItem with TreeView's:
private void UpdateTreeViewSelectedItem()
{
//uiCategorySelector.SelectedItem = ReadOnly....
//((TreeViewItem) uiCategorySelector.Items[uiCategorySelector.Items.IndexOf(Model.ActiveCategory)]).IsSelected = true;
// Will not work Items's are not TreeViewItem but Category object......
//((TreeViewItem) uiCategorySelector.ItemContainerGenerator.ContainerFromItem(Model.ActiveCategory)).IsSelected = true;
//Doesn't work too.... NULL // Changind DataContext=Model and Model = new MainViewModel line order doesn't matter.
//Allright.. figure this out later...
}
And none of methods I was able to think of worked....
And here is the link to my sample project demonstrating Control Library Hell with MVVM: http://cid-b73623db14413608.office.live.com/self.aspx/.Public/MVVMDemo.zip
Maciek's answer is actually even more complicated than it needs to be. You don't need template selectors at all. To create a heterogeneous tab control:
Create a view model class for each type of view that you want to appear as tab items. Make sure each class implements a Text property that contains the text that you want to appear in the tab for its item.
Create a DataTemplate for each view model class, with DataType set to the class's type, and put the template in the resource dictionary.
Populate a collection with instances of your view models.
Create a TabControl and bind its ItemsSource to this collection, and add an ItemTemplate that displays the Text property for each item.
Here's an example that doesn't use view models (and that doesn't implement a Text property either, because the objects I'm binding to are simple CLR types), but shows how template selection works in this context:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:coll="clr-namespace:System.Collections;assembly=mscorlib">
<DockPanel>
<DockPanel.Resources>
<coll:ArrayList x:Key="Data">
<sys:String>This is a string.</sys:String>
<sys:Int32>12345</sys:Int32>
<sys:Decimal>23456.78</sys:Decimal>
</coll:ArrayList>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Int32}">
<StackPanel Orientation="Horizontal">
<TextBlock>This is an Int32:</TextBlock>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Decimal}">
<StackPanel Orientation="Horizontal">
<TextBlock>This is a Decimal: </TextBlock>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</DockPanel.Resources>
<TabControl ItemsSource="{StaticResource Data}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</Page>
Of course in a real MVVM application those DataTemplates would use UserControls to map each type to its view:
<DataTemplate DataType="{x:Type my:ViewModel}">
<my:View DataContext="{Binding}"/>
</DataTemplate>
Maciek and Robert already gave you some ideas on how to implement this.
For the specifics of binding the columns of the DataGrid I strongly recommend Meleak's answer to that question.
Similar to that you can use attached properties (or Behaviors) and still maintain a clean ViewModel in MVVM.
I know the learning curve for WPF is quite steep and you're struggling already. I also know that the following suggestion doesn't help that and even makes that curve steeper. But your scenario is complex enough that I'd recommend to use PRISM.
I wrote an article and a sample application with source code available, where I discuss and show the problems I have mentioned here and how to solve them.
http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/
In order to connect your ViewModel to your View you need to assign the View's DataContext. This is normally done in the View's Constructor.
public View()
{
DataContext = new ViewModel();
}
If you'd like to see your view model's effect at design time, you need to declare it in XAML, in the View's resources, assign a key to it, and then set the target's DataContext via a StaticResource.
<UserControl
xmlns:vm="clr-namespace:MyViewModels
>
<UserControl.Resources>
<vm:MyViewModel x:Key="MyVM"/>
</UserControl.Resources>
<MyControl DataContext={StaticResource MyVM}/>
</UserControl>
(The above is to demonstrate the design-time trick works)
Since you're dealing with a scenario that includes a container such as the TabControl I'd advocate considering the following things :
Hold your child ViewModels in a Property of type ObservableCollection
Bind the TabControls ItemsSource to that property
Create a new View that derives from TabItem
Use a template selector to automatically pick the type of the view based on the type of the view model.
Add IDisposable to yoour child ViewModels and create functionality to close the views.
Hope that helps a bit, if you have further questions let me know.

Categories

Resources