I have a WPF application which has a MainView.xaml Window which loads numerous page objects at runtime, loads them into ViewModels, and displays them dynamically in the menu.
My MainViewModel has an ObservableCollection of ViewModels and I bind these each to their appropriate Views in the MainView.xaml file.
However, is there a way to automate this so I don't have to make these manual entries each time I add a page?
<Window.Resources>
<DataTemplate DataType="{x:Type vm:PageItemManageCustomersViewModel}">
<v:PageItemManageCustomersView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:PageItemManageEmployeesViewModel}">
<v:PageItemManageEmployeesView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:PageItemReportsViewModel}">
<v:PageItemReportsView/>
</DataTemplate>
</Window.Resources>
Isn't this something that a "ServiceLocator" or "Container" should be doing, hooking up the Views to the ViewModels? I've read that the above is a common way to match up the ViewModels and Views in the MVVM pattern, but it comes across as a bit static to me. Would appreciate any thoughts.
Another option is to use a class for resolving ViewModels based on some key. You can then use this in your main view to resolve the correct viewmodel for your controls.
public static class ViewModelFactory
{
public ViewModelBase Create(string someKeyHere)
{
//Some logic to resolve a view model
}
}
Related
I have an app written in WPF (MVVM), which based on some conditions, will create instances of different UserControls, These UserControls are completely independent, used to display certain information. They have some custom logic inside, like timers and so on, so I can't use Templates.
Now I face the problem that I want to create a list of UserControls in the ViewModel, and bind the host UI to it. The problem is that I don't know how to bind and what to bind. In a non MVVM project, you would simply get the layout where you want to put your controls, and add them there as children. In MVVM app, I don't know how to do this. I imagine having a WrapPanel with ItemsSource, that will add all the controls and resize itself as needed, based on the UserControls.
Can someone suggest a solution?
EDIT:
My ViewModel exposes an ObservableCollection of IMyDriver right now. So that's what I thought, to break a little bit MVVM to get what I describe next:
Now, Each IMyDriver can be a different type of driver, and can implement different other interfaces. I need the UI to create specific UserControls that know how to get maximum from these Drivers, based on their capabilities. In short, the UserControls connect to the device through the driver for polling data. And each UserControl does it in a specific way.
You can do it quite simply and easily by declaring specific data type classes for the data in each UserControl and define DataTemplates that expose your UserControls in the App.xaml file:
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
<YourViewsPrefix:YourView />
</DataTemplate>
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourOtherViewModel">
<YourViewsPrefix:YourOtherView />
</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/UserControl. You can display them by having a property of the type of your view model using a ContentControl like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
...
public YourBaseViewModelClass YourViewModelProperty { get; set; }
Make sure that all of your view models extend this class:
public YourViewModel : YourBaseViewModelClass { }
...
public AnotherViewModel : YourBaseViewModelClass { }
Then you can swap each view model (and display each related view) like this:
YourViewModelProperty = new AnotherViewModel();
Based on what Will commented, and what Sheridan answered, I have found the solution to my problem.
So:
I don't break MVVM by leaving ViewModel types intact.
I create DataTemplates in my Window's Resources tag, and in each data template, I assign the DataTemplate to be my UserControl defined in another assembly (UICommons)
<DataTemplate x:Key="IMultiChannelMeasurementDCDataTemplate">
<uicommon:MeasurementMax8ChannelMonitoringUserControl/>
</DataTemplate>
I create a Template Selector in my application assembly, and based on the interfaces the DataTypes implement, I return the right DataTemplate, that I assign in the same Window's Resources tag
<!-- DataTemplate Selector -->
<local:DriverComponentDataTemplateSelector x:Key="templateSelector"
DefaultDCDataTemplate="{StaticResource DefaultDCDataTemplate}"
IIhcDCDataTemplate="{StaticResource IIhcDCDataTemplate}"
IMultiChannelMeasurementDCDataTemplate="{StaticResource IMultiChannelMeasurementDCDataTemplate}"
IProgrammablePowerSourceDCDataTemplate="{StaticResource IProgrammablePowerSourceDCDataTemplate}"
IEnvDCDataTemplate="{StaticResource IEnvDCDataTemplate}"/>
I create an ItemsControl in the Window, with the following XAML code, that binds itself to my ObservableCollection of items
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemTemplateSelector="{StaticResource templateSelector}" ItemsSource="{Binding DriverComponentsInfo}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" x:Name="ucWrapPanel">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
I enjoy dynamically created UserControls based on different Drivers!
P.S. I upvoted Will's comment and Sheridan's answer, because without these, I wouldn't be able to find the solution. Thx!
They have some custom logic inside, like timers and so on, so I can't use Templates.
This does not follow. I think you may have a misconception about the capabilities of WPF.
Also, as you want to use MVVM: Binding to a list of UserControls is breaking the pattern. View-models should only ever reference other view-models (and models); they do not know anything about the UI. Bind to a collection of view-models which have associated UserControls as their views (consider using implicit DataTemplates). To bind a WrapPanel you use an ItemsControl and set its ItemsPanel accordingly.
I am new to WPF and the MVVM pattern. I am trying to build a 'step by step' or 'wizard' like application.
So the user should first login then select some data and finally the selected data should be stored somewhere. (This flow never changes!)
However I decided to use DataTemplates and different ViewModels for each Template and a MainViewModel for the Window which populates the Templates. (Think this should be ok regarding to different Posts here)
But now my problems are starting. I know how I can change the current view from the MainViewModel BUT I want to change the current View from the inner ViewModel. And I want to be able to collect data from one inner ViewModel to another and I have no clue how this can work.
MainViewModel:
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentView {get; set;}
public MainWindowViewModel()
{
CurrentView = new ViewModelA;
}
}
XAML:
<Window x:Class="PUSEImporter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:PUSEImporter.ViewModel"
xmlns:V="clr-namespace:PUSEImporter.View">
<Window.DataContext>
<VM:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type VM:ViewModelA}">
<V:Detail />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ViewModelB}">
<V:Overview />
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding CurrentView}"/>
</Window>
So think about a button in ViewModelA (or the View from ViewModelA) and now I want to switch to ViewModelB when someone clicks on the button. And not enough the data which is collected by ViewModelA should also be available in ViewModelB.
Is this possible and the preferred way of using this techniques or do I misunderstand some concepts?
(And if this is true how should i handle things like that?)
Thanks in advance!
There are many ways to achieve what you want. In MVVM, there's one view model to each view... therefore, if your main view has a child view, then your main view model should have a property of the type of another view model. In this instance, you can add a delegate to the child view model and register a handler for it in the main view model.
This will enable you to effectively pass a signal to the main view model from the child view model, which you can react to in the parent view model in any way you want to. Rather than explain the whole story again here, please see my answers from the Passing parameters between viewmodels and How to call functions in a main view model from other view models? posts here on Stack Overflow for more information on how to achieve this.
I want to create a "Wizard" in my Silverlight MVVM application. The Wizard should contain multiple steps between which you can navigate using "next" and "previous".
The problem I am facing is the relationship between views and view models.
I want there to be a view and view model for the Wizard itself. My instinct tells me that there should be one view/view model pair for each step in the wizard.
What is a good approach for maintaining these kinds of relationships, where one view model holds several other view models and the view actually consists of several smaller views?
Are there any patterns or practices that I can use?
I know this question might be subjective, but give me the rough cuts of an approach and I'll award you an answer!
I'd propose main wizard viewModel which has a collection of steps view models and handles navigation between them. While navigating it should call validation methods in step viewModels:
WizardVM:
public class WizardVM
{
// this commands should support CanExecute
public ICommand GotoNextCommand { get; private set; } // should open next step VM
public ICommand GotoBackCommand { get; private set; } // should open previous step VM
// this prop should be set by 'GotoNext', 'GotoBack' commands
public object CurrentStep { get; private set; }
// probably internally you will have a list of all steps:
private ICollection<object> _stepViewModels = ...;
}
WizardView:
<StackPanel>
<ContentPresenter Content="{Binding CurrentStep}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding GotoBackCommand}">Back</Button>
<Button Command="{Binding GotoNextCommand}">Next</Button>
</StackPanel>
</StackPanel>
UPDATE:
Views can be coupled with ViewModels via Datatemplating. For example add this into resources in App.Xaml:
<DataTemplate DataType="{x:Type local:Step1ViewModel}">
<Step1View />
</DateTemplate>
<DataTemplate DataType="{x:Type local:Step2ViewModel}">
<Step2View />
</DateTemplate>
Your viewModels should know absolutely nothing about views. It means that WizardVM should
expose only other viewModels but not views. It's a rule of thumb for MVVM.
UPDATE2 Oops, I forgot that Silverlight doesn't have DataTemplating yet. In silverlight I would still expose ViewModels but bind them to ContentPresenters using a converter which will convert a viewModel into corresponding view.
We are folllowing mvvm approach for a wpf application.
We have are following view-model approach..I mean we create view-models and map them using
<DataTemplate DataType="{x:Type vm:CityViewModel}">
<vw:Cities/>
</DataTemplate>
In this city - view ..I have a user control...which I am using multiple times...
<view:UserControl1 Grid.Row="2" DataContext="{Binding UcViewModel}" Margin="291,5,291,-5"></view:UserControl1>
<view:UserControl1 Grid.Row="3" DataContext="{Binding Uc2ViewModel}" ></view:UserControl1>
We create multple instances of user control view model inside CityViewmodel.
Does this approach comply with mvvm ???
I would consider the MVVM pattern to be a loose guide.
Ideally what you are looking for is a testable application. Any code in the UI is harder to test.
If this works in you circumstances then go for it, but keep testability in mind.
In an application I am working on at the moment I have an ItemsControl with 6 instances of the same UserControl and ViewModel.
Edit:
public class InsuranceViewModel
{
public ObservableCollection<UnderwritingViewModel> Underwriting { get; set; }
}
In the view I have:
<ItemsControl ItemsSource="{Binding Path=Underwriting}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- this could be another UserControl -->
<views:UWView DataContext="{Binding}" />
<!-- or a full data template defined in this view -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Or you could put this in your resources:
So, in effect, all you have to do is create new ViewModel instances in your DataContext and the template will take care of the View creation.
I'm creating a wpf user control which is in mvvm pattern.
So we have : view(with no code in codebehind file), viewmodel,model,dataaccess files.
I have MainWindow.xaml as a view file, which I need to bind with MainWindowModel.cs.
Usually, in a a wpf application we can do this with onStartUp event in App.xaml file. But in user control, as we do not have App.xaml...How do I achieve it ?
Please help :(...Thanks in Advance !!!
You can use a ContentControl, with a DataTemplate to bind the UserControl (View) to the ViewModel :
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<v:MyUserControl />
</DataTemplate>
...
<ContentControl Content="{Binding Current}" />
WPF will pick the DataTemplate automatically based on the type of the Content
I know this is an old, answered question, but I have a different approach. I like to make implicit relationships in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
With this, there is no need to set a DataContext anywhere.
UPDATE >>>
In response to #Vignesh Natraj's request, here is a fuller explanation:
Once you have set up the DataTemplate in a Resources element, you can display the KioskView in this example by adding an instance of the KioskViewModel anywhere in your XAML. This could be filling the MainWindow, or just inside a particular section of the screen. You could also host multiple instances of the KioskViewModel in a ListBox and it will generate multiple KioskView instances.
You can add an instance of the KioskViewModel to your XAML in a couple of ways, depending on your requirements. One way is to declare the XML namespace for the project that contains the KioskViewModel.cs file and simply add an instance of it in a ContentControl to the page where you want your view to appear. For example, if you had a UserControl called MainView and the KioskViewModel.cs file was in a Kiosk.ViewModels namespace, you could use basic XAML like this:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<UserControl.Resources>
<ViewModels:KioskViewModel x:Key="KioskViewModel" />
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{StaticResource KioskViewModel}" />
</UserControl>
I prefer to use the MVVM design pattern with WPF, so I would have a base view model class providing useful functionality such as implementing the essential INotifyPropertyChanged interface. I then have a property called ViewModel in the main (top level) view model of type BaseViewModel. This provides me with a nice way to change the ViewModel property to any view model that has derived from BaseViewModel and therefore to be able to change the associated view from the view model.
For example, in the MainViewModel.cs class that is bound to MainView there is a field and relating property:
private BaseViewModel viewModel = new KioskViewModel();
public BaseViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; NotifyPropertyChanged("ViewModel"); }
}
As you can see, it starts off as a KioskViewModel instance, but can be changed to any other view at any time in response to user interaction. For this setup, the XAML is very similar, but instead of declaring an instance of the view model in the Resources element, we bind to the property in the MainViewModel:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<ContentControl Content="{Binding ViewModel}" />
</UserControl>
Note that for this example, we would need to declare two (or more to make this approach useful) DataTemplates in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
I've been using MVVM Light Toolkit which has a ViewModelLocator class that you can put properties to the viewmodels in. You then create a reference to the ViewModelLocator in your Mainwindow.xaml like so:
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/>
In the grid panel, or whatever you're using, you can then set the datacontext like this:
<Grid DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
...
</Grid>
You could also go with MEFedMVVM which potentially adds a bit more flexibility in terms of being able to swap different viewModel implementations into the view.
The flexibility in both of these libraries is that you don't have to use their ViewModel base classes if you don't want to - the ViewModelLocator and the MEFedMVVM can work with any class.
There are endless ways to do it, wich all fall in one of the two categories:"view first" or "model first".
In a "view first" mode the view (e.g. your mainwindow) is created first and then (e.g. in the codebehind) the View instantiates the ViewModel and sets it as its datacontext):
private void WindowLoaded(object sender, EventArgs args)
{
this.DataContext = ViewModelService.GetViewModelX();
}
In a "model first" mode the ViewModel is there first and then instanciated the View.
// method of the viewmodel
public void LoadView()
{
// in this example the view abstracted using an interface
this.View = ViewService.GetViewX();
this.View.SetDataContext(this);
this.View.Show();
}
The examples given here are just one way of many. You could look at the various MVVM frameworks and see how they do it.
We can use ObjectDataProvider to call a method inside an object ..as follows :
<ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
MethodName="ConvertTemp"
x:Key="convertTemp">
Is there anyway to do the same using DataTemplate
You can probably look at MSDN. I find it as a good resource, though it doesn't explain how to use usercontrols,you will find your way out.