WPF / MVVM Design Suggestions - wpf

I'm pretty new to WPF, and am looking for some guidance here.
I'm working on an application that will be used to print out work orders for our fulfillment department.
Right now I have 2 windows: The first is the main screen, the second is a window with a gridview that will hold the work orders to print.
The first page will have several buttons on there. Every button will open up the second window; however, depending on which button is clicked, the parameters passed into the service that will load data will be different.
What would be the best practices way of doing this?
Is there way to define these parameters somewhere on the Button control, and then pass them through via ICommand/RelayCommand?
Should I create a UserControl/ServerControl that will let me build in these additional properties?
Something else I'm not thinking of?
Edit:
To give a rough example (and this is very oversimplified}, say i have 2 sets of criteria: OrderTypes: {Rush, Today, Future} and Locations {Warehouse 1, Warehouse 2, Warehouse 3}
The main window would have a 3x3 grid of buttons, one for each combination. I'd like to be able to specify on a single button "Expedite & Warehouse 1"; and then pass those parameters back to a single method, which would open the second window.

Lets say you have MainWindow and buttons are placed in it.
Create a MainWindowViewModel and set it as DataContext for MainWindow.
Have an ICommand on your ViewModel and bind button Command with this ICommand so that entry point for opening another window will be single. For ICommand you can use either RelayCommand or DelegateCommand whichever suits you best.
Now, comes the point where you need to open window and pass on parameter to it based on button type click. I would suggest to have Enum depicting action need to perform based on different buttons.
Enum
public enum ActionType
{
Action1,
Action2,
Action3 and so on...
}
And bind from button like this:
<Button Command="{Binding CommandInstance}"
CommandParameter="{x:Type local:ActionType.Action1}"/>
<Button Command="{Binding CommandInstance}"
CommandParameter="{x:Type local:ActionType.Action2}"/>
where local will be namespace where enum is declare.
And in command execute delegate pass the enum value to another window constructor:
private void CommandMethod(ActionType action)
{
AnotherWindow anotherWindow = new AnotherWindow(action);
anotherWindow.Show();
}
and from action passed in constructor, you can check what parameter need to pass to service responsible for loading data.
Also, in case creating window from ViewModel doesn't seems right you can have Service wrapper over window Controls which is responsible for showing/closing window.
UPDATE
Since you want to pass multiple parameters from Views so maintaining enum for this will be cumbersome. You can pass multiple values from View using IMultiValueConverter.
Let me explain with small example:
<Button Command="{Binding TestCommand}">
<Button.Resources>
<sys:String x:Key="Rush">Rush</sys:String>
<sys:String x:Key="Warehouse">Warehouse</sys:String>
</Button.Resources>
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiValuesReturnConverter}">
<Binding Source="{StaticResource Rush}"/>
<Binding Source="{StaticResource Warehouse}"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
where sys will be namespace for System in XAML:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
So, now you have liberty in XAML to pass many objects from XAML to your command parameter. All you have to do is to declare the resource under Button resources section and pass it as binding to converter.
Converter code to convert it into list of parameters which can be passed to command as a single parameter object:
public class MultiValuesReturnConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
return values.ToList<object>();
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
Command Method:
private void CommandMethod(object parameter)
{
// Now you have all the parameters here to take decision.
IList<object> values = parameter as List<object>;
AnotherWindow anotherWindow = new AnotherWindow(action);
anotherWindow.Show();
}

If you don't want to use some third party library, there really is no problem in simply passing the parameters through a click event into your other window's constructor. If your data is represented by a viewmodel you may also pass that viewmodel instead of the parameters themselves.
A point of MVVM is not "no code-behind". Many times you will end up without code-behind, but trying to develop applications this way leads you into convoluted anti-patterns that often are more work and more lines of code than simple click events and "the old way".
Treat your data as data, try to do all your work in testable viewmodels and never follow a pattern too rigidly lest you end up with mounds of unreadable abstractions.

Before detailing any thing, I would advice you to use the third party library MVVMLight, it has many helpful features such as Messenger, its own RelayCommands etc ...
For passing parameters from a button to consume them in Commands, you can use the Tag property if you want to pass a parameter regardless of the type of the event, if you want to pass a parameter that is related to a certain Command(event) then CommandParameter is what you need :
Tag : Gets or sets an arbitrary object value that can be used to store custom information about this element. (Inherited from FrameworkElement.)
CommandParameter :
<Button Content="Parameterized Command"
Command="{Binding ParameterizedCommand}" CommandParameter={Binding SomeObject} />
I don't think at the level of your question that you need to create a UserControl unless you have more complicated scenarios.
You can use the Messenger class to pass information from ViewModel to another (this just a helper it's independent from the MVVM Pattern).
The MVVMLight has code templates that help you create ViewModels with ease.
The MVVMLight has many helpful snippets which you will find helpful.
Beware of Commands because they are not originally supported with all UIElements, they are only available with ButtonBase and its children and they work only to replace the Click event, to use Commands and CommandParameters with other UIElements and with other kinds of events you should use a sort of EventToCommand behaviours, MVVMLight has got that already ready for you
Hope I covered most important parts you may need.

The most easy and intuitive way (Using INotifyPropertyChanged to update the UI instead of DependencyProperty):
You create a property that'll be your DataContext for your OrderViewModel in MainWindowViewModel
class MainWindowViewModel : ViewModelBase // ViewModelBase should implement INotifyPropertychanged, unless you're using dependency properties
{
private OrderViewModel _OrderViewModelInstance;
public OrderViewModel OrderViewModelInstance
{
get{ return _OrderViewModel;}
set { _OrderViewModel = value;
OnPropertyChanged("OrderViewModel")} // Method from ViewModelBase
}
Now, whichever way You're creating Your Order View:
You instantiate OrderViewModel in MainWindowViewModel (Let's say when a button gets clicked) with desired parameters.
you bind the Order view's DataContext to OrderViewModelInstance in XAML. You might want to create an additional variable that tells you when the window is visible.

Related

Create object from View input and pass it to ViewModel (in MVVM)

Recently I'm developing my first project using MVVM concept (I use WPF). I've read many tutorials (ia. famous J.Smith's one) about MVVM before I started writing a code. Everything I've read had been clear until I started to code...
The problem is simple: in View layer I have a form with TextBoxes. Let's say a few TextBoxes, ie.: Name, Surname, Phone number. When user fills all of them and clicks OK-button, I want to add new person (with specified personal details) to my local Database (I use Entity Framework as a ORM).
To do this I need to write something like this:
<Button Name="MyButton" Command="MyRelayCommandWhichNeedsAllOfTheTextboxes" Content="OK" />
Using CommandParameter I can pass one object from View to ViewModel. There are many textboxes so it's probably not a good idea.
From XAML I can assign to CommandParameter whole form which needs to be filled by user. But then, inside ViewModel, I need to know all textboxes' names. The main assumption in MVVM is that all the layers (View, ViewModel and Model) have to be independent.
What's the best solution? How can I pass input data from form to ViewModel?
I would suggest having the relay command as part of your View Model -- that way, when the command gets triggered, you'll have access to all the properties you need.
XAML:
<Button Name="SurnameTextBox" Text="{Binding Surname,Mode=TwoWay}" />
<Button Name="MyButton" Command="{Binding MyRelayCommand}" Content="OK" />
View Model:
public class MyViewModel : INotifyPropertyChanged
{
// (note, raise property changed on the setter
public string Surname { get; set; }
public ICommand MyRelayCommand { get; set; }
public MyViewModel
{
// set the command callback here
MyRelayCommand = new RelayCommand(OKCommandHandler);
}
private void OKCommandHandler(object parameter)
{
// save the record here using Surname, etc
// (note that you don't even need to use parameter, so you can just ignore it)
}
}
you dont need to pass data from form to viewmodel because your viewmodel has allready all data.
your viewmodel expose all data through properties to your view and you bind to it. look at dbaseman answer.

Reverting an object is a user clicks "Cancel" in WPF

I have a Window that serves as a dialog in a WPF application. This dialog has an "OK" and a "Cancel" button. I am setting the DataContext of the Window to an instance of an object in my application. The user can change the values of the properties of the object within the Window. If a user clicks "Cancel", I want to revert the property values back to their original values. Is there an easy way to do this in WPF?
For instance, I know with RIA data services there is RejectChanges. Is there something similar on the client-side with WPF?
Thanks!
In object which is set to DataContext (ideally it should be ViewModel in MVVM approach) expose two commands
public ICommand CancelCommand { get; set; }
public ICommand OkCommand { get; set; }
Then for the buttons assign these commands like shown below
<Button Command="{Binding CancelCommand}" ... />
You've to keep two copies of object, a copy should be created by Deep Copy or if an object has a few editable fields you can keep those as class fields. Basically on initialization stage do backup editable object properties, then bind to DataContext editable version of object. In Cancel Command handler - restore from a backup copy...
When the object is simple (just a few properties of basic types such as string, int, etc.) DeepCopy or IEditableObject is a very good option.
When the object is a node in a more complex hierarchy this might prove to be too difficult and going back to the server/model and reloading the original data is much easier.

WPF: Proper configuration for Window with a child UserControl (MVVM)

I am trying to properly accomplish the following. I have a UserControl (ProgramView). It has a viewmodel (ProgramViewViewModel). ProgramView is consumed as a child control within a Window (ProgramWindow). ProgramWindow has a public property ProgramId, so the consumer of the window can specify the desired Program (data entity) to show. ProgramView has a property ProgramId, as it's primary job is to display this data. ProgramWindow is little more than a wrapper window for this user control.
ProgramViewViewModel also has a property ProgramId. Changes to this property drive out the operation of the view model, which are surfaced out of the view model using other properties, which ProgramView can bind to.
I am trying to hide the operation of the view model from the consumer of the ProgramView and ProgramWindow.
This ProgramId should be bound through all of these layers. Changes to ProgramWindow.ProgramId should flow to ProgramView.ProgramId and then to ProgramViewViewModel.ProgramId. I cannot figure out how to properly implement this.
My current approach is to surface ProgramId in all three classes as a DP. Within the Window, I would imagine ProgramView being instantiated thusly:
<local:ProgramView ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramWindow}}, Path=ProgramId}" />
This appears to actually work. Within ProgramView, I do obtain changed events for the property, and they do appear to have the correct value. FindAncestor seems to operate properly.
How then should I synchronize the ProgramViewViewModel.ProgramId property? I see two ways. One way would be to set a Binding on the ProgramViewViewModel instance itself, to also use FindAncestor, and find the ProgramId on the ProgramViewViewModel This has two downsides. It requires ProgramViewViewModel to surface ProgramId as a dependency property. I'd rather like to avoid this, but it might be acceptable. Either way, I cannot accomplish it in XAML.
<local:View.DataContext>
<local:ProgramViewViewModel
ProgramId="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ProgramView}}, Path=ProgramId}" />
</local:View.DataContext>
This does not work. It appears that I cannot introduce a binding expression within the instantiation of the instance. FindAncestor reports that it cannot find ProgramView. My theory here is that the instance is outside of the logical tree, and thus cannot traverse to it's parent.
The second option, which makes more sense, is to bind the ProgramView.ProgramId property to "ProgramId" (in the DataContext). I cannot accomplish this because I cannot figure out how to specify a binding expression on a property defined in the code-behind. is required in the XAML, but the type ProgramId exists on is actually . I cannot figure out how to specify this property.
If I manually (in code-behind of ProgramView) create a Binding instance and call SetBinding(ProgramIdProperty, binding), the value no longer flows into the View itself. I believe this is because I just replaced the binding on ProgramView.ProgramId, which was previously set by ProgramWindow. One binding per-property?
My remaining ideas are to provide TWO ProgramId properties in ProgramView. One bound to the DataContext, the other publicly available to be bound by the consumer (ProgramWindow), and then write OnValueChanged handlers that synchronize the two. This feels like a hack. The other is to manually watch for changes on ProgramView.ProgramId and ProgramView.DataContext within the code-behind of ProgramView, and propagate the value myself. Neither of these ideas feel ideal.
I'm looking for other suggestions.
Your description seems detailed but I'm having trouble understanding why you need to implement this design. I can't help but think DRY.
If you need to expose a dependency property in two such-related view models, I would suggest that you make the child view model (for the user control view) a property of the first (for the program window view). Something like:
public class MainViewModel : ViewModelBase
{
public ProgramViewModel ChildViewModel { get; private set; }
}
public class ProgramViewModel : ViewModelBase
{
private int _ProgramId;
public int ProgramId
{
get { return _ProgramId; }
set
{
if (value != _ProgramId)
{
// set and raise propery changed notification
}
}
}
}
The MainView can get the property using ChildViewModel.ProgramId (data context set to MainViewModel). The ProgramView accesses it by ProgramId (data context set to MainViewModel.ChildViewModel).

WPF+MVVM: How to display VM via resource dictionary

At the moment I am creating new instance of both View and ViewModel and assign view.DataContext = vm in the application in the handler of 'Startup' application event
In the http://msdn.microsoft.com/en-us/magazine/dd419663.aspx in the "Applying a View to a ViewModel" chapter I've read that it would be a good idea to bind ViewModel object to view via resources.
Am I correctly understand that using suggested approach I should:
Create a resource in the "MainPage" that will have a "DataTemplate" section for each pair of View/ViewModel;
Bind instance of the ViewModel object to the MainPage?
Am I right?
P.S. Actually, I've tried to do that but got few issues and want to know if I am going by the proper way. If no, please point me how that should be done in the right way.
How this technique works is that instead of finding and creating views directly let wpf find the view through data templates. so when you have the following in your application resources. This drives the UI based on what ViewModel you want to display and don't have to worry about coding up the view.
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<ui:MyView />
</DataTemplate>
Note: vm: and ui: are just xml namespaces declared in the top element of the resource file.
you can then just create a generic window that will 'find' the view via a ContentControl
<Window ...>
<ContentControl Content="{Binding}" />
</Window>
var window = new Window();
window.DataContext = new MyViewModel();
window.Show();
This will display the window embedding MyView as the content of the window. Provided you have your bindings set in your view pointing to properties in your viewmodel the wire up succeed. No need to 'new' up a view. The main window can be reused simply by reassigning a different view model to the data context.
Also if you let us know what specific issues you are having we will be able to provide a more specific answer if the above is not what you are looking for.
HTH
I used to do a key/value pair for all of my ViewModel/View like aqwert suggests, but once you get a couple dozen,or more than one :), ViewModels it starts getting pretty tedious and prone to typos.
I personally like an IValueConverter doing the work for me and using Convention for the location of the View.
For example let's say I have my view models in namespace MyApp.ViewModels
and all of my Views in namespace MyApp.Views
and I have a suffix of ViewModel behind all of my VMs and a suffix of View behind all of my Views
All I have to do is:
1) Have all of my ViewModels inherit from a base class ViewModelBase
2) Put this in my application resource dictionary
<m:ViewModelConverter x:Key="ViewModelConverter"/>
<DataTemplate DataType="{x:Type vm:ViewModelBase}">
<ContentControl Content="{Binding Converter={StaticResource ViewModelConverter}}"/>
</DataTemplate>
3) Create my converter, the following is just an example, you can modify to meet your convention.
public class ViewModelConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
Type ViewModelType = value.GetType();
string ViewNameSpace = ViewModelType.Namespace.Replace("ViewModel", "View");
string ClassName = ViewModelType.Name.Replace("Model", string.Empty);
Type ViewType = Type.GetType(string.Format("{0}.{1}", ViewNameSpace, ClassName));
if (ViewType != null)
return Activator.CreateInstance(ViewType);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
The above will try and find the view, if it doesn't it will just return the ViewModel that it was trying to convert (which WPF will just call .ToString() on)
You don't have to worry about actual wiring of ViewModel to the View's DataContext because WPF does that automatically.
And then I'm done. I don't have to touch my resource file any more. :)

WPF MVVM: Convention over Configuration for ResourceDictionary?

Update
In the wiki spirit of StackOverflow, here's an update:
I spiked Joe White's IValueConverter suggestion below. It works like a charm.
I've written a "quickstart" example of this that automates the mapping of ViewModels->Views using some cheap string replacement. If no View is found to represent the ViewModel, it defaults to an "Under Construction" page. I'm dubbing this approach "WPF MVVM White" since it was Joe White's idea. Here are a couple screenshots.
The first image is a case of "[SomeControlName]ViewModel" has a corresponding "[SomeControlName]View", based on pure naming convention. The second is a case where the ModelView doesn't have any views to represent it. No more ResourceDictionaries with long ViewModel to View mappings. It's pure naming convention now.
I posted a download of the project here:
Mvvm.White.Quickstart.zip
Original Post
I read Josh Smith's fantastic MSDN article on WPF MVVM over the weekend. It's destined to be a cult classic.
It took me a while to wrap my head around the magic of asking WPF to render the ViewModel.
It's like saying "Here's a class, WPF. Go figure out which UI to use to present it."
For those who missed this magic, WPF can do this by looking up the View for ModelView in the ResourceDictionary mapping and pulling out the corresponding View. (Scroll down to Figure 10 Supplying a View ).
The first thing that jumps out at me immediately is that there's already a strong naming convention of:
classNameView ("View" suffix)
classNameViewModel ("ViewModel" suffix)
My question is:
Since the ResourceDictionary can be manipulated programatically, I"m wondering if anyone has managed to Regex.Replace the whole thing away, so the lookup is automatic, and any new View/ViewModels get resolved by virtue of their naming convention?
[Edit] What I'm imagining is a hook/interception into ResourceDictionary.
... Also considering a method at startup that uses interop to pull out *View$ and *ViewModel$ class names to build the DataTemplate dictionary in code:
//build list
foreach ....
String.Format("<DataTemplate DataType=\"{x:Type vm:{0} }\"><v:{1} /></DataTemplate>", ...)
Rather than writing code to explicitly add things to the ResourceDictionary, how about just generating the right view on demand? You can do this with a ValueConverter.
Your resources would look like this:
<Views:ConventionOverConfigurationConverter x:Key="MyConverter"/>
<DataTemplate DataType="{x:Type ViewModels:ViewModelBase}">
<ContentControl Content="{Binding Converter={StaticResource MyConverter}}"/>
</DataTemplate>
You still need a DataTemplate resource, but as long as your ViewModels all have a common base class, you'll only need one DataTemplate to take care of all of them.
Then define the value converter class:
public class ConventionOverConfigurationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
// value is the ViewModel. Based on its GetType(), build a string
// with the namespace-qualified name of the view class, then:
return Activator.CreateInstance(Type.GetType(viewName));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
All you'd need to do is write the logic inside Convert, which will depend on things like whether your Views and ViewModels are in the same namespace or not.
I decided to do pretty much hthe same thing so I load my DataTemplates directly into the ResourceDictionary using
private void RegisterResources()
{
ResourceDictionary dictionary = new ResourceDictionary();
dictionary.Source = new Uri("pack://application:,,,/StartupModule;Component/UIResources.xaml");
Application.Current.Resources.MergedDictionaries.Add(dictionary);
}
where the UIResources file is a ResourceDictionary xamls file containing all of our DataTemplates

Resources