Reverting an object is a user clicks "Cancel" in WPF - 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.

Related

WPF / MVVM Design Suggestions

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.

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.

WPF/XAML - binding control commands to parent window's viewmodel?

I have an application, and I have an assembly.
In the application, I have a window, and in the assembly I have a user control.
There is an instance of the user control in the window.
Both the user control and the window are backed by separate viewmodels.
In the user control, there is a button. The button should be enabled/disabled based on the state of the user control's viewmodel. When the button is clicked, processing needs to be done, based on the information in the user control's viewmodel, but it needs to be done by the window's viewmodel. (There are aspects of what needs to be done that are, and should be, outside of the scope of the user control.)
And here's the twist - this user control won't be used exclusively in this window, it might be used in another, or in a control that is used in a third. The user control can't be allowed to know what kind of window or control contains it, or is handling the process when its button is clicked.
So, what to do?
Define a command in the assembly, and bind the user control's button to it, passing the user control's viewmodel as the command parameter? How, then, do I bind the command to the window's viewmodel?
Or should I define the command in the user control's viewmodel, then raise an event to tell the parent window that the appropriate action needs to be taken?
It's not clear to me which is cleaner.
If you always know that the parent's property is going to be exposed the same with the same name, you can do something like this that has worked for me plenty of times:
Command={Binding Parent.DataContext.SomeCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}
This gets the usercontrol, then goes to the parent and gets that datacontext and binds it to that command. This works when the user control will be encompassed by many windows / controls that expose the same command (you could implement an interface here).
You could then pass the user control's viewmodel to the command (again, implement some interface) like so:
CommandParaemter={Binding }
You could use a Messenger structure to communicate between ViewModels.
MVVMLight contains one that you could use or you could write your own.
Before doing this make sure you did separate the responsibilities correctly or you'll end up with spaghetti-messages-code.
There should be hierarchy with your view models, just like you have with your controls. The main window has a child user-control. The Main View Model should be able to get connected with User Control View Model (and assign it if needed). Here is how I would do it:
public class MainVM:NotificationObject
{
// Make this a Notify Property
public UserVM userVM { get{return _userVM;}; set {_userVM = value; RaisePropertyChanged("userVM");}
public MainVM
{
userVM = new UserVM();
userVM.ExecuteCmd = new DelegateCommand (yourAction);
}
}
public class UserVM:NotificationObject
{
public DelegateCommand ExecuteCmd {get{return _executeCmd;} set{_executeCmd = value; RaisePropertyChanged("ExecuteCmd");
}
}
XAML:
<local:urUserCtrl DataContext={Binding userVM}/>
This is of course psuedocode
Sounds like a case for the Strategy pattern. http://en.wikipedia.org/wiki/Strategy_pattern
Define an interface for a strategy object that can be assigned to the UserControl's viewmodel, (or used to initialise it). The interface defines whatever properties/methods/events are required to enable the strategy object to retrieve from the UserControl viewmodel the data needed for the processing, plus a means of returning the result of the processing back to the UserControl viewmodel.
Then create a concrete implementation of that strategy object that collaberates with the Window's viewmodel to perform whatever task it needs to. In this case the Window's viewmodel might even implement the strategy interface itself.
Other instances of the UserControl in other scenarios can then be initialised with other concrete implementations of the strategy object that perform the same required task, but possibly in very different ways.

User controls communicating via Commands - how?

I'm working on my first project in WPF/XAML, and there's a lot I've not figured out.
My problem is simple - I need a window that has a bunch of fields at the top, with which the user will enter his selection criteria, a retrieve button, and a data grid. When the user clicks on the button, a query is run, and the results are used to populate the grid.
Now the simple and obvious and wrong way to implement this is to have a single module containing a single window, and have everything contained within it - entry fields, data grid, the works. That kind of mangling of responsibilities makes for an unmaintainable mess.
So what I have is a window that is responsible for little more than layout, that contains two user controls - a criteria control that contains the entry fields and the retrieve button, and a data display control that contains the data grid.
The question is how to get the two talking to each other.
Years back, I would have added a function pointer to the criteria control. The window would have set it to point to a function in the display control, and when the button was clicked, it would have called into the display control, passing the selection criteria.
More recently, I would have added an event to the criteria control. I would have had the window set a handler in the display control to listen to the event, and when the button was clicked, it would have raised the event.
Both of these mechanisms would work, in WPF. But neither is very XAMLish. It looks to me like WPF has provided the ICommand interface specifically to accommodate these kinds of connection issues, but I've not yet really figured out how they are intended to work. And none of the examples I've seen seem to fit my simple scenario.
Can anyone give me some advice on how to fit ICommand to this problem? Or direct me to a decent explanation online?
Thanks!
MVVM is the prevalent pattern used with WPF and Silverlight development. You should have a read up on it.
Essentially, you would have a view model that exposes a command to perform the search. That same view model would also expose properties for each of your criteria fields. The view(s) would then bind to the various properties on the view model:
<TextBox Text="{Binding NameCriteria}"/>
...
<Button Command="{Binding SearchCommand}".../>
...
<DataGrid ItemsSource="{Binding Results}"/>
Where your view model would look something like:
public class MyViewModel : ViewModel
{
private readonly ICommand searchCommand;
private string nameCriteria;
public MyViewModel()
{
this.searchCommand = new DelegateCommand(this.OnSearch, this.CanSearch);
}
public ICommand SearchCommand
{
get { return this.searchCommand; }
}
public string NameCriteria
{
get { return this.nameCriteria; }
set
{
if (this.nameCriteria != value)
{
this.nameCriteria = value;
this.OnPropertyChanged(() => this.NameCriteria);
}
}
}
private void OnSearch()
{
// search logic, do in background with BackgroundWorker or TPL, then set Results property when done (omitted for brevity)
}
private bool CanSearch()
{
// whatever pre-conditions to searching you want here
return !string.IsEmpty(this.NameCriteria);
}
}

wpf mvvm passing parameters between viewmodels using commands

This is my first attempt at MVVM. My application's core is loosely based Josh Smith's msdn article. And I am also using the mvvm light framework.
I have a main window containing a command list area and a workspace area which shows usercontrols/views as tabitems, each usercontrol has a corresponding viewmodel. The mainWindow also has a viewmodel containing my command list, and the workspace viewmodels have a base workspace viewmodel.
My default view has a master datagrid, of MappingSets, that can have one selected item. The commands launch new tabitems with views that handle MappingSet detail based on that selected item. I have a View/ViewModel that, depending on the command used should return either a tabitem for creating a new MappingSet with no existing data, or a tabitem containing the detail of the selected item for editing, or a tabitem containing detail the selected item as the base for a new MappingSet.
Having Set the scene, what I have not managed to work out is command dependent way to pass parameters, such as the identifier of the selected MappingSet object, to instantiate my viewmodel in one of the three states mentioned above? For instance would the mvvmlight messenger be appropriate for this task?
This is a perfect scenario for the messenger/eventaggregator. However, your message chain might be a bit convoluted. From what I'm understanding, your Main window holds a list of commands (like a menu or a ribbon). Here is how I see the chain of events.
You select a MappingSet from the datagrid, this causes a MappingSetSelected message to be fired (with a payload of the selected MappingSet)
The main window listens for that message and stores the currently selected MappingSet
When the user clicks the button a "EditMappingSet" or "CreateNewMappingSet" message is fired (or if the Window is responsible for creating the new views, it creates them itself).
If there are only three options, you could have them binding to three different commands and within the commands do the passing of your self-defined variable.
private RelayCommand _openMappingSetCommand;
//Command that one of your options is bound to
public ICommand ViewMappingSetOption1
{
get
{
if (_openMappingSetCommand == null)
{
_openMappingSetCommand = new RelayCommand(param => this.DoTabRequest("your parameter");
}
return _openMappingSetCommand ;
}
}
// Method that creates your viewmodel
private void DoTabRequest(parameterType parameter)
{
WorkspaceViewModel viewModel = null;
if (viewModel == null)
{
viewModel = (WorkspaceViewModel)Activator.CreateInstance(typeof (viewModelType), parameter);
this.Workspaces.Add(viewModel);
}
this.ActiveWorkspace = viewModel;
}
Then allow for that parameter on the constructor of your viewmodel and do whatever else you need based on that.

Resources