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.
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'm making an application for DB migrations. I made a multithreaded framework with WPF GUI. I put someting like this in my namespace/folder:
class Something : Migrator {
public override Run(){
//I would need this
string valueOfMyCustomFieldOnForm = xyz.Text; //example
int count = 500;
for(int i = 0; i < 500; i++){
//do something here
OnProgressChanged(...); //call event, GUI is updated
}
OnCompleted(...); //migration completed
}
}
Then using reflection I put all classes in that namespace onto dropdown list. When I choose one in a list and click Start, the Thread with code in Run method is started.
DB Host: TEXTBOX
DB Username: TEXTBOX
DB Password: TEXTBOX
--
Migrator custom field 1: TEXTBOX
Migrator custom field 2: TEXTBOX
...
--
List with migrated items - irrelevant
There are few commong field on GUI (like database host, username etc...). But for some of those migrators I would need custom fields on GUI (for example 3 extra textbox fields).
What is the best way to do this in WPF? I need part of the GUI to be dynamic.
There's a lot of seemingly-irrelevant information in your question, which - I think - is really about mechanisms for creating metadata-driven UIs in WPF. Here's a way to approach that problem:
Suppose that you want to build a property-sheet-like UI: a grid that displays a row for each property, with a prompt and an input control of some kind. To do this, you're going to need a collection of objects, with each item in the collection including properties that describe the property and its value. A simple design would be a class that exposes a Prompt property and a Value property and that implements change notification.
Once you have created and populated this collection, you can implement an ItemsControl that displays it in a grid:
<ItemsControl ItemsSource="{Binding Properties}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="PropertyViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Prompt"/>
<ColumnDefinition SharedSizeGroup="Value"/>
</Grid.ColumnDefinition>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
<Label Content="{Binding Prompt}"/>
<TextBox Grid.Column="1" Text="{Binding Value, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is pretty simple - the most complicated thing about it is using Grid.IsSharedSizeScope so that all of the grids that this control creates use the same column widths. You could also use a ListView instead of an ItemsControl, though using a ListView for this introduces a bunch of issues surrounding focus and selection that you may not want to deal with.
Note that because of the magic that is WPF template matching, you could conceivably implement the Value property as an object, and create different templates to handle the different possible types of the Value property - just like a real property sheet does. To do this, you'd create a template for each type, e.g.:
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Value, Mode=TwoWay}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:DateTime}">
<DatePicker Value="{Binding Value, Mode=TwoWay}"/>
</DataTemplate>
etc. Then you'd change the template for the PropertyViewModel so that instead of showing the Value in a TextBox, it uses a ContentPresenter, e.g.:
<ContentPresenter Grid.Column="1" Content="{Binding}"/>
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.
I have a XAML code that should load my UserControl inside the TabControl.
If I put this XAML code:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Gui}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
I have absolutly nothing appear in the windows (Gui property is inside the ViewModel class and return a UserControl).
But if I put his XAML code instead of the previous one:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
I have the ViewModel Object loading:
(source: clip2net.com)
Here is a piece of code of the TextBoxInputViewModel that has the property Gui that should be binded to be able to get the Visual (usercontrol):
private UserControl gui;
public UserControl Gui
{
get
{
if (this.gui == null)
{
this.gui = new SimpleTextBoxInputControl();//Xaml User Control
this.gui.DataContext = this;//Bind the Visual and ViewModel
}
return this.gui;
}
}
Any idea how that I can get the UserControl instead of this object reference text?
The problem is that ItemSource is a collection, where as you're binding it to a property that is just one value. The error in the Output window that you're seeing is likely related to this.
Instead of returning a UserControl directly from your View Model, it would be better to return another View Model that represents the contents of the tab, and use templates to display that content. If you need it to be more dynamic than choosing the template based on the Type of the View Model, look into setting TabControl.ContentTemplateSelector. This needs to be set to a class that derives from DataTemplateSelector. You can use this class to decide which template to load based on the object bound to that tab.
you should create a template for your viewmodel in your app.xaml file like this
<DataTemplate DataType="{x:Type simpleModel:TextBoxInputViewModel}">
<myView:TextBoxInputControl />
</DataTemplate>
where simpleModel is the namespace of TextBoxInputViewModel, and TextBoxInputControl is the user control you want to show and myView is the namespace of that user control.
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
}
}