I need to create a dynamic forms in WPF. For example, say I need to allow the user to create a family tree with information about each of the people and each person will have infomation about their occupation.
class Person
{
int age;
int dateOfBirth;
List<Job> jobList = new List<Job>();
}
class Job
{
string title;
int salary;
}
This program needs to be able to have fields that will allow data entry of all the members in the class, including multiple jobs. I would like the forms data to (possibly) coded in XAML, where if they click the button "Add Person" it will expand another person entry box to allow the user to add information about the person. This is the same as "Add Job" but the jobs are only added under that person.
(Note that the end result of this will be a tree data structure that contains all the people and their children)
How would I go about doing this in WPF using the MVVM pattern? Before I learned the MVVM pattern, I had used code behind and created dynamic view using c# to code the XAML view and added it dynamically as child elements. But I don't think that is the best way to do this since it seems too tedious.
I'm new to the MVVM pattern so some small code snippets of how I would do this (using databinding?) would be very helpful.
I made a quick example coded in XAML of how the form might look like:
This problem is solved in WPF using DataTemplates. In any place you need a "repeated" form, you will set up something like this:
<ItemsControl ItemsSource="{Binding Jobs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<...>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You use ListView to get a collection control, that will display a instance of the DataTemplate for every item in the bound collection. Inside of the DataTemplate, your DataContext is the bound item inside the collection, ie. an instance of the Job class.
If Jobs was an ObservableCollection<T> then the control would automatically update when items were added or removed from the bound collection.
Related
I have a list of objects (a custom class) that I want to display inside a ListBox, with each object drawing inside a custom User Control. Imagine a list of contacts (with custom Contact class) that should show up as a list of ContactUserControls (the XAML designed to present a Contact)
I know how to databind a list of Contact objects to a ListBox. I can databind a single Contact to a single ContactUserControl. I'm trying to understand the pattern/implementation of a databound list of objects that uses my custom UserControl to draw each object.
Do I bind the ListBox to my list of Contact objects, and (inside the Contact class) set up a connection to the ContactUserControl ("This is how you draw")? Do I bind the ListBox to a list of ContactUserControls, and bind each User Control to one of theses Contact objects before they go into the list? If so, do I have to do it manually via "ForEach" binding, or is there a "semi-magical" way in which it can be done purely via XAML?
Ideally, everything is correctly databound. Thanks! Not expecting somebody to present a turnkey solution of the entire thing, pointers to the applicable pattern/tutorials would be a great start.
You can use <ListBox.ItemTemplate>. Something like this:
<ListBox ItemsSource="{Binding contacts}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ContactUserControls DataContext="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
See https://msdn.microsoft.com/en-us/library/cc265158(v=vs.95).aspx the section about To format items in a ListBox or see https://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemtemplate(v=vs.110).aspx the Examples section
I have a large project coded with VB6 which I've been trying to upgrade to new technologies for a few months. My project consist on 6 administrative modules reunited under a client-server application. Coming from VB, my logical choice was to upgrade to .NET. After a lot of research I decide to use C#, WPF and the MVVM pattern (with Caliburn Micro).
At the beggining I had some problems, but I managed to resolve them. But now I've come to a point where I need (like every complex application) to communicate with different views and their corresponding viewModel through modal popups (or some other technique). And in this matter the MVVM pattern seems to be very restrictive or complex. A simple "Are you sure you want to delete this record (yes/no)" is a very complex task. So I'm looking for advice as how communicate views without complex artifacts as EventAgregators.
So far the only possible alternative I've found is to use the ModalContentPresenter class from this blog. The problems with this solution are:
I need to write the father view XAML and modal XAML on the same view.
I cannot have multiple popus from same view.
Some examples of where I'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with all posible clients and let the user chose one.
Add a product popup to a customer order.
Any ideas or suggestions? Some sample code would be appreciated? Thanks!
I am the author of the linked ModalContentPresenter control so I will attempt to address some of your questions and concerns.
I need to write the father view XAML and modal XAML on the same view.
You can actually write both views in separate files. The views can then be loaded dynamically using DataTemplates which will depend on the ViewModel that is bound to either the Content or ModalContent properties.
See this which describes the general way in which this view switching can be achieved.
You could have a MainViewModel which has two properties, PrimaryViewModel and SecondaryViewModel which return appropriate view models which provide the properties and commands for the main and modal content.
You could have the following setup in XAML:
<DataTemplate DataType="{x:Type FooViewModel}">
<Controls:FooView />
</DataTemplate>
<DataTemplate DataType="{x:Type BarViewModel}">
<Controls:BarView />
</DataTemplate>
<controls:ModalContentPresenter
Name="modalPresenter"
Content={Binding DataContext.PrimaryViewModel}
ModalContent={Binding DataContext.SecondaryViewModel} />
When the IsModalproperty is false, only your PrimaryView will be displayed. As soon as you set the IsModal property to true the ModalContentPresenter will display your SecondaryView.
I cannot have multiple popus from same view.
I take it you mean you want to be able to display different modal content at different times from the same main view.
Using the above technique this is as simple as switching the ViewModel that is bound to the ModalContent property (before displaying it by setting IsModal to true). As long as you have a DataTemplate for the ViewModel that is bound (and your MainViewModel implements INotifyPropertyChanged correctly), the correct content will be displayed.
Some example on where i'd like to use modal popups is:
Put a button on a view to select a Client. It should open a popup with
all possible clients and let the user chose one.
Add a product popup to a customer order.
Once you understand the technique described above you should be able to see that the as long as you have a View and ViewModel pair you can cover any scenario you can think of.
As an example, consider viewModels that have the following interfaces:
public interface SelectCustomerViewModel : INotifyPropertyChanged {
event EventHandler CustomerSelected;
public ObservableCollection<Customer> Customers { get; }
public Customer Customer { get; set; }
public Command CustomerSelectedCommand { get; }
}
public interface MainViewModel : INotifyPropertyChanged {
public SelectCustomerViewModel ModalContent { get; }
public Command SelectCustomerCommand { get; }
public bool IsSelectingCustomer { get; }
}
You could have XAML that looks something like this:
<Window x:Class="ModalContentTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Select a customer">
<DataContext>
<vm:MainViewModel />
</DataContext>
<DataTemplate DataType="{x:Type SelectCustomerViewModel}">
<Controls:SelectCustomerView />
</DataTemplate>
<c:ModalContentPresenter Name="modalPresenter"
ModalContent={Binding ModalContent}
IsModal={Binding IsSelectingCustomer}>
<!-- This is the primary content! -->
<Grid>
<Button Content="Select a customer"
Command={Binding SelectCustomerCommand} />
</Grid>
</c:ModalContentPresenter>
</Window>
Here's how it works:
The IsSelectingCustomer property of the MainViewModel would start off as false.
Clicking the button in the main view would invoke the SelectCustomerCommand object. The command would then tell the MainViewModel to change the IsSelectingCustomer property to true.
The ModalContentPresenter would display the view specified by the data template. The user can now only interact with the 'select customer view'.
Once a customer has been selected, a button can be clicked (which is bound to the CustomerSelectedCommand of the SelectCustomerViewModel) which in turn would raise the CustomerSelected event.
The MainViewModel would have an event handler that would respond to the CustomerSelected event. The handler would then read the SelectedCustomer property from the SelectCustomerViewModel and finally, it would set the IsSelectingCustomer property back to false, causing the modal content to be closed.
Is there a simple way to do this?
My problem is mostly that the user triggers the element creation via a context menu on on control, and then wants to create a copy, or a new element nearby and the like.
I cannot seem to find a way to pass the appropriate information to the Execute function.
I know about CommandParameter but my gut feeling is to pass the entirety of the source control (or its parent in the case of the right click menu) and that seems wrong.
What is the idiomatic way to do this?
What level of duplication are you talking about?
The easiest case:
1) IEumerable<object> in your ViewModel, with different ViewModels(IFunnyControlViewModel, ISadCotrolViewModel), etc..
2) Create ItemsControl in View and bind against that collection. You can use DataTemplates to "map" different viewmodel to different view control.
3) Receive ViewModel Execute() with underlying ViewModel, just "re-add" it to the collection, thus "referencing" the same object, if you want to keep them synchroized, or clone it.
public YourViewModel {
public IEnumerable<YourBaseControlViewModel> Controls {get; set;}
public YourViewModel()
{
Controls = new List<YourBaseControlViewModel();
Controls.Add(new YourFunnyControlViewModel());
}
// Called from View by Command.
public void DuplicateControl(YourBaseControlViewModel Control)
{
// either duplicate it using cloning, or add the same reference.
Controls.Add(Control);
}
}
And within ItemsControl, something like:
<ItemsControl ItemsSource="{Binding Controls}">
<ItemsControl.Resources>
<DataTemplate x:Type="{x:Type blah:YourFunnyControlViewModel}">
custom stuff here
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Introduction
I have an application that imports lab instrument data while it is running. This data is imported and then displayed in a ListView at an interval set by the end-user as per his or her testing requirements. When a value of interest appears in this ListView that they watch, they then press a Start button and the application begins performing calculations on that datum and subsequent data until a Stop button is pressed. So on the left side of the screen is a View for displaying the imported data and on the right side is another View for watching the values and statistics as they are calculated and displayed.
The Current Code
The View that displays the ListView where data is imported to is the ImportProcessView.xaml and it sets its DataContext to the ImportProcessViewModel.cs. The VM I've just introduced has a property ObservableCollection<IrData> that the ListView, I've also just described, binds to. Now to the interesting part...
The ImportProcessView has a ContentControl that sets it's content dynamically a UserControl representing the controls and fields specific to the type of Phase that is chosen by the end-user.
<StackPanel Background="White" Margin="5">
<ContentControl Content="{Binding CurrentPhaseView}"/>
</StackPanel>
There are three PhaseViews, each in its own User Control and each sets it's DataContext to the ImportProcessViewModel. As a result I am getting some severe VM bloat to the tune of 2000 lines. Ridiculous. I know. The reason for the bloat is because the ImporProcessViewModel is maintaining state through properties for each of the three PhaseViews and not only that but contains methods for performing calculations whose data is stored and displayed in these "PhaseViews".
What I am trying to achieve
Obviously before the ImportProcessViewModel becomes more unwieldy, I need to break it up so that each PhaseView has its own ViewModel, but also such that each ViewModel maintains a relationship back to the ImportProcessViewModel for sake of the dependency imposed by the ObservableCollection of IrData.
R&D
I've done my research on ViewModels communicating with each other, but most of the results involve applications that were written with a specific MVVM framework. I am not using a framework, and at this point in the project it would be too late to refactor it to start using one.
I did, however, find this article and the answer offered by 'hbarck' suggests something simple like composition to achieve the result I want, but since I don't have much experience with DataTemplates I don't understand what is meant when he/she suggests exposing "the UserControl's ViewModel as a property on the main ViewModel, and bind a ContentControl to this property, which would then instantiate the View (i.e. the UserControl) through a DataTemplate"
Specifically, I don't understand what is meant by "bind a ContentControl to this property which would then instantiate the View through a DataTemplate".
Can someone clarify by way of an code example what is meant by instantiating a view through a DataTemplate in the context of this example?
Additionally, is this a good approach (as suggested by 'hbarck')?
As one can see, I am already setting the Content property of a ContentControl to the Phase View that is to be instantiated. I just don't know know what involving a DataTemplate would look like.
I don't understand what is meant when he/she suggests exposing "the
UserControl's ViewModel as a property on the main ViewModel, and bind
a ContentControl to this property, which would then instantiate the
View (i.e. the UserControl) through a DataTemplate"
A DataTemplate allows you to specify a relationship between a view (such as a user control) and a view model.
<DataTemplate DataType="{x:Type myApp:MyViewModel}">
<myApp:MyUserControl />
</DataTemplate>
This tells a ContentPresenter to display MyUserControl whenever its content property is set to an instance of MyViewModel. The view model will be used as the user controls DataContext. Typically, the DataTemplate is added to your application resources.
What the author of that answer is saying is that you could have a viewModel that has a property of another viewModel type which is bound to the Content property of the ContentPresenter.
<ContentPresenter Content="{Binding ParentViewModel.ChildViewModelProperty}"/>
Providing you have a DataTemplate that specifies a relationship between your ChildViewModel and your user control, WPF will automatically load the user control into your view.
This answer I provided to another question might also provide you with some help.
I need to break it up so that each PhaseView has its own ViewModel,
but also such that each ViewModel maintains a relationship back to the
ImportProcessViewModel.
This will allow you to break your viewModels into smaller, more manageable viewModels that look after themselves. This will leave you with the problem of communicating between the viewModels.
If you nest your viewModels as suggested, then your child viewModels could expose events that the parent viewModel can bind to so it is notified when something changes. Something like this:
public class ParentViewModel // Derive from some viewModel base that implements INPC
{
public ParentViewModel()
{
childViewModel = new ChildViewModel();
childViewModel.SomeEvent += someEventHandler;
// Don't forget to un-subscribe from the event at some point...
}
private void SomeEventHandler(object sender, MyArgs args)
{
// Update your calculations from here...
}
}
This is simple and doesn't require any additional frameworks. Some might argue against this method but it is a valid solution that works. The downside is that the viewModels have to know about each others existence in order to subscribe to the events so can end up being tightly-coupled. You can use standard object-oriented design principles to get around this though (I.E. derive your child viewModel from an interface so that the parent only knows about the interface and not the implementation).
If you really want to go for loosely-coupled communication then you need to use some sort of event aggregation or message bus system. This is similar to the above method except there is an object that sits between the view models and acts as a mediator so that the viewModels do not have to know of each others existence. My answer here provides some more information.
There are pre-existing solutions available but this would involve taking on an additional framework. I would advise using Josh Smiths MVVM foundation as it is very simple and you would only need to use a single class anyway.
While Benjamin's answer is really elaborate and very helpful, I'd like to clarify how what I wrote in the other post would apply to your problem:
You'd have three different PhaseViewModel-Classes for your different phases, probably derived from one common base class, let's say PhaseVMBase.
Instead of a CurrentPhaseView property, you'd probably have a CurrentPhaseVM property. This would be of type Object or PhaseVMBase, and return one of the three PhaseViewModel classes, depending on what the user chose in the main ViewModel.
PhaseVMBase would have an UpdateData method, which would be called by the main ViewModel whenever it received new data that should be processed by the phase view. The main ViewModel would call this method on whatever happened to be the CurrentPhaseVM at the moment. The PhaseViewModels would implement INotifyPropertyChanged, so that changes as a result of UpdateData would be visible to bound controls.
Your DataTemplates would be declared in the resources of the main view, e.g. the main window,
like this:
<DataTemplate DataType="{x:Type my:Phase1VM}">
<my:Phase1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase2VM}">
<my:Phase2View/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:Phase3VM}">
<my:Phase3View/>
</DataTemplate>
Notice that there is no x:Key, only the DataType value. If declared like this, WPF would choose the appropriate DataTemplate when asked to display an object of type Phase1VM, Phase2VM or Phase3VM, respectively. Phase1View, Phase2View and Phase3View would be UserControls which would know how to display the different ViewModels. They wouldn't instantiate their ViewModels themselves, but expect that their DataContext is set to an instance of their respective ViewModel from outside.
Under the assumption that the ContentControl which should show the phase view is declared in the main view, and that the DataContext there would be the main ViewModel, you'd declare the ContentControl like this:
<ContentControl Content="{Binding CurrentPhaseVM}"/>
Depending on the actual type of CurrentPhaseVM, this will choose one of the three DataTemplates, and display the appropriate UserControl. The UserControl's DataContext would automatically be the ContentControl's Content, since that would the object which caused the DataTemplate to be chosen.
EDIT: Lists and code formatting don't go together, it seems...
I'm working on my first true WPF MVVM pattern application.
Currently I have a number of ComboBoxes on various screens that are bound to Collection classes and properties of the relevant ViewModel class.
They always have an entry with the text <Add>, which is really an empty object class and I currently use it to trigger an AddNewObject event if the Property bound to the SelectedItem has <Add> in its ToString() output. This strikes me as cumbersome and it ties the View too closely to the View model for my liking.
e.g.
<ComboBox ItemsSource="{Binding AllObjects}" SelectedItem="{Binding SelectedObject}" />
then in ViewModel code:
public SomeObjectType SelectedObject
{
get{return this.fieldSomeObjectType;}
set
{
if(null==value)
return;
if(value.ToString().Contains(#"<Add>"))
{
if(null!=this.AddNewObject)
{
this.AddNewObject;
}
}
}
}
Is there a way in XAML of adding this extra line into the ComboBox drop down list and binding it to an AddNewObject Command?
The code you've written in your view has nothing to do with your business logic. Its fine.
MVVM doesn't say that you shouldn't have anything in the codebehind of the view. Showing 'Add' is a requirement on the view and can be handled by the code behind of view.
In ASP.NET I've been doing this that I databinded the list control to some data but also specified some items in the html and it would merge them. Have you tried that?
use CompositeCollection for merging a default item with a itemsource. http://msdn.microsoft.com/en-us/library/ms742405.aspx