Passing Parameters from one view model to another in wpf - wpf

As I continue to adapt to the new world of wpf and mvvm I find myself with another problem which is proving to be a little vexing.
In windows forms if I needed a new form to take a parameter I simply passed the information that I needed to that form's constructor. As Winforms is essentially a code behind model anyway this wasn't an issue.
I now find myself with a MainWindow (controlled by MainWindowViewModel) and a Login (controlled by LoginViewModel) which is shown as a dialog first. I have sorted the issue of the missing dialog result and I can get the main window to show after closing the login dialog.
The login dialog is shown and responded to in the Application startup with the following code;
dim dlg As New Login
If dlg.ShowDialog() = True Then
Else
Current.Shutdown(0)
End If
and I use the use the following in the code behind of the login window to take care of showing the main window when the login dialog closes;
Public Sub New
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Application.Current.ShutdownMode =ShutdownMode.OnExplicitShutdown
End Sub
Protected Overrides Sub OnClosed(e As EventArgs)
MyBase.OnClosed(e)
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose
End Sub
Now the Mainwindow already knows that it's datacontext is MainWindoViewModel because I set that up when designing it with blend, so how do I pass any parameters that I want to pass from the LoginViewModel to the MainWindowViewModel?
I can read c# but would prefer vb if possible. I know that this is something that I will want to be doing often so if there is a generic way to do this then that's the way I'd like to go.
Thanks

Mvvm frameworks generally implement some kind of messaging system to allow data to be moved between viewmodels or sometimes between viewmodel and view.
You can look at the way messaging is handled in some of the more popular frameworks such as MvvmLight or MvvmCross. It's usually just like an event that uses a weak reference to prevent tight coupling, so you can register for a type of message and then from another viewmodel you publish a message to any registered listeners.
So you will either need to implement your own kind of messenger, or now might be a time to look at starting to use a third party framework, unless you have specific reasons for avoiding this.

Related

Possible to pass WPF form by interface?

I know I have done this with Winforms and thought I had done it with WPF forms but it doesn't seem to be working for me now.
AKA...I love MVVM but sometimes I just want to throw up a prompt in the middle of my Viewmodel logic. In those cases I ask the View to pass me a prompt just in case I need it. The Prompt Implements an interface with the simple methods and properties I need (ex: ShowDialog, DialogResult etc...). I have also used Events to throw the duty back to the UI to prompt but sometimes it just seems cleaner for the VM to prompt without knowing what the view is via an interface.
So, did something change with WPF forms or I am just mistaken. I get a "Value of type 'PTO_DayDetail()' cannot be converted to 'iBasicPrompt'" build error when trying to pass a Form that Implements iBasicPrompt to a method expecting iBasicPrompt.
Public Class PTO_DayDetail
Implements MBS.Core.Interfaces.iBasicPrompt
End Class
Public Sub Add(... Prompt As MBS.Core.Interfaces.iBasicPrompt)
Yeah, I did something wrong (not sure what as I went a different route). I built a test app to post here and it all worked fine. Hmmmm. Weird.

A combination of winforms' controls as a prefab?

In our winforms application we often have a situation where the same panel is used in different forms. Right now I simply copy the code and the designer elements from form to form but that is obviously a terrible practice.
So I thought of making a class that could be easily added to a different form as a "component". The only problem is to be able to prototype and maintain this panel in the visual designer in such a way that if I want to change the panel's appearance in the future it gets changed for every class instance I created.
For instance I have a panel that provides search functionality:
Alongside with the code for click/textChanged events. I want to encapsulate it in a class, which I would be able to instantiate and initialize in any form's constructor to instantly add these controls (alongside events) into that form. Now it is not necessary for me to see the controls in the designer of the recepient form, however, I need to be able to see them in the designer somewhere in order to modify them if I would ever require that.
And if I do modify the appearance somehow (for instance add an extra button) these controls instantly change across the entire project, everywhere I instantiated the class.
I do know that all this can easily be done just creating a new form and encapsulating everything in it, I just wonder if it can be done for a group of controls instead.
You just described the perfect use of a User Control. It's easy to use and direct.
First Add a user COntrol to the project:
Then add the desired controls on the user control:
Build the project and you will see the UserControl on the toolbox:
Add them to the form as a standard control:
If you change the code for the user control (in this case adding a button click handler) uit will affect all the intances of that user control:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TextBox1.Text = "Button Clicked"
End Sub
Note: If you have the Control in another project on the same solution, make sure you build that project too if you make any change.

How to use default CommandButtonsVisibility with Dataform, WCF RIA and POCO Entites?

I have a Silverlight project (in VB), which uses a (WCF RIA) Domain Service, which uses POCO Entities. I'm displaying the data with a DataForm. My Domain Service has InsertUser, UpdateUser, DeleteUser-methods (correctly) specified. CommandButtonsVisibility is not set (so I guess it has default values).
However, the Commit-Button is not visible (it's not only disabled, it's not even there). When I set CommandButtonsVisibility=All I can use the edit button and it is working (changes are saved in db). I would like to use the default behavior though.
I recognized the DataForm automatically shows or hides the buttons if the CRUD-methods are written or not written in the Domain Service (I tested it with my Delete-methods).
What am I doing wrong, are my Update-methods not defined correctly? This is how they look like:
Public Sub UpdateUser(ByVal user As User)
'... update the user
End Sub
For the C#-users:
public void UpdateUser(User user)
//... update the user
End Sub
First thing is first. The DataForm is the buggiest piece of code I've ever encountered in my life. It was mainly designed to be used as a Child view to a DataGrid, in a Master/Child type of a view. And I guess they only tested it with the unmaintainable drap & drop code that's generate, which you see a lot in the intro videos.
Let's get to your problem now:
The most common reason for this is that you're not binding your DataForm to the correct backing data store. If you're binding to a single item, then you're pretty much out of luck. Just implement your own OK and Cancel buttons. But if you're working against a collection, than make sure it implements ICollectionView. (You may get lucky with ObservableCollection as well.)
If you need more specific help, please post the relevant parts of your XAML and code behind.

How do I open a WPF window in AddNew mode before any data has been loaded?

I want to open a WPF4/EF4 form in AddNew mode so the user can start entering data in bound controls before any data has been selected from the database. I already have an "Add New Record" button but it only works with a populated DataContext (my CollectionViewSource). Here is the code so far:
private void btnAddNewRecord_Click(object sender, RoutedEventArgs e)
{
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesListCollectionView.AddNewItem(newEntity);
}
Background: This is a basic WPF app with bound controls. I started with an Entity Framework model that appears in my DataSources window. I dragged my LabSample entity from the DataSources window and let it create my CollectionViewSource (labSamplesViewSource) in the XAML's Windows.Resources section. The DataContext for all my controls is the labSamplesViewSource. I create a new LabEntities object called _labEntitiesContext as the window is instantiated. I use _labEntitiesContext to build my filtered ObjectQuery(of LabSample) and to SaveChanges, but I'm a little confused as to how this _labEntitiesContext is hooked up to my CollectionViewSource. If you could clarify this along with answering my question that would be helpful. Note: I'm not ready to use MVVM.
When the window loads I use this.FindResource to grab a reference to the CollectionViewSource in a class level variable named _labSamplesCollectionViewSource. I allow the user to enter search fields to populate the screen with data. My LoadData routine looks something like this:
System.Data.Objects.ObjectQuery<LabSample> labSamplesObjectQuery = this.GetLabSamplesFiltered_Query(_labEntitiesContext, sampleID_LIKE, xxx_LIKE, yyy_LIKE);
System.Data.Objects.ObjectResult<LabSample> labSamplesObjectResult = labSamplesObjectQuery.Execute(System.Data.Objects.MergeOption.AppendOnly);
_labSamplesCollectionViewSource.Source = new System.Collections.ObjectModel.ObservableCollection<LabSample>(labSamplesObjectResult);
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
The _labSamplesListCollectionView class level variable set above is used in my btnAddNewRecord_Click code. Before LoadData is called the _labSamplesListCollectionView is null causing my AddNew code to fail with "Object reference not set to an instance of an object".
How can I make this work? I'm wondering if I should be making use of _labSamplesListCollectionView.AddNew instead of my current technique but I couldn't get that work either. Your help will be greatly appreciated.
I am writing an app that does something similar. I am however using MVVM pattern, which allows me to do some neat things. In mine, I am working with Shipments. On the ShipmentsView, I can click an "Add New" button which fires off a bound command property which is housed in the associated ViewModel class. That command methods looks like the following: Note: Views in this context are not CollectionView but refer to MVVM View classes.
Dim NewShipment = New Shipment()
_Context.AddToShipments(NewShipment)
Dim ShipVM = New ShipmentViewModel(NewShipment)
ShipmentVMCollection.Add(ShipVM)
Dim NewShipmentView as ShipmentView(ShipVM)
My ShipmentView handles it's placement and visiblility, and my Shipment object has it's property values initialized so that it does not immediately present errors via it's validation handlers. This way the user can create a new shipment and if they get sidetracked they can save it and come back to it without having a bunch of mandatory fields.
When I use a CollectionViewSource, I populate it with an ObservableCollection of my entities, and then add the entities to that observable collection when I create them. ObservableCollection implements INotifyPropertyChanged and INotifyCollectionChanged events and notifies the UI when something happens, and it all works through the CollectionViewSource.
You might take a look at the MVVM pattern which is really good for moving data and keeping it in the proper scope, and there are some good MVVM frameworks out there that will help you make a nice application with MVVM.
MVVM may be a bit of overkill for your app if it is small. But if it gets over more than just a few Views it is going to get unwieldy and hard to move data back and forth and keep it current, and maintainable.
Wiki Article for MVVM - a pretty good place to start and get links
This is my Constructor for one of my ViewModels. I realize you don't want to implement in MVVM right now, but a code behind would be similar. In this instance, I am using a background worker to get my entity records, (the constructor call be for that and the View setting immediately afterward can be disregarded), then I link up my CVS, Populate it with my ObservableCollection, and set it's View to a field so I can filter on it later.
Public Sub New(ByRef MyView As NTMsView)
Me.New(ViewManagerService.CreateInstance, ViewModelUIService.CreateInstance)
NTMsBackgroundWorker.RunWorkerAsync()
_View = MyView
_NTMCollectionViewSource = _View.FindResource("NTMCollectionViewSource")
_NTMCollectionViewSource.Source = NTMs
_NTMCollectionView = _NTMCollectionViewSource.View
End Sub
This is an example of my AddRecord method. Then I instance a new object, add it to the appropriate collection in the Context, Save it, execute a stored procedure, then refresh the context since the stored procedure did a few things to the record. Then I add the object to my Observable.
Private Sub AddNTM()
'Create an NTM Object.
Dim NewNTM As New NTMShipment()
'Add it to the context
_context.AddToNTMShipments(NewNTM)
_context.SaveChanges()
_context.MakeNewSecurityID(NewNTM.NTMShipID)
_context.Refresh(RefreshMode.StoreWins, NewNTM)
'Wrap it in a ViewModel and Add it to the NTMs collection
NTMs.Add(New NTMViewModel(NewNTM))
End Sub
As for creating a new entity before your CollectionViewSource is created, a couple of questions. Is your edit forms datacontext related to the CVS? In my forms, the CVS is only used in conjunction with ItemsControls since it is displaying a collection of items. If your edit forms controls are dissociated from the CVS, you should not have much trouble populating them with a new entity and when it comes time to save, check to see if CVS is null and if so, create it then populate it.
If that is not a good answer, could you expand on how your application is structured?
Instead of opening the Window in AddNew mode I disable all data entry controls when the window loads or the when a search returns no records. When the "Add New Record" button is clicked I ALWAYS start over with a new data context that contains just one new entity. This means I have to prompt to save changes if any dirty (modified) records exist. The prompt allows the user to save changes, discard changes or continue editing (never entering AddNew mode). Here is the AddNew code:
MessageBoxResult response = PromptToSaveChanges(ReasonForPromptToSave.LoadingData);
if (response == MessageBoxResult.Cancel) return;
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesCollectionViewSource.Source = new ObservableCollection<LabSample>();
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
_labSamplesListCollectionView.AddNewItem(newEntity);
_labSamplesListCollectionView.CommitNew();
_labSamplesListCollectionView.Refresh();
Here are my steps to put the window in AddNew mode:
1) Prompt to save changes.
2) Create a new entity and add it to my data context.
3) Create a new ObservableCollection of my entity type and assign it to the .Source of my CollectionViewSource. Note the _labSamplesCollectionViewSource is a reference to the XAML's CollectionViewSource that was auto-generated by dragging a table from the data sources window.
4) Assign the .View of the CollectionViewSource to a class level ListCollectionView variable.
5) Add the new entity to the ListCollectionView that was just created.
6) Call CommitNew and Refresh on the ListCollectionView
I have answered my own question here, but keep in mind that the answer is the result of trial and error and may not be ideal. Regarding my confusion as to how the _labEntitiesContext is hooked up to the CollectionViewSource I believe the answer is in the line that reads _labSamplesListCollectionView.AddNewItem(newEntity), but I'd like to see an explanation of how all of the objects reference each other.
My final comment/question is that I'm disappointed at how hard it is to find a standard reference application or document that teaches non-MVVM WPF/Entity Framework databinding in detail. Microsoft promotes drag-and-drop binding but leaves us without a reference on how to build a complete application. I'll move on to MVVM soon, meanwhile if anyone can direct me to a GREAT resource or feature complete application that is WPF, non-MVVM and Entity Framework I would greatly appreciate it.

Open dialog in WPF MVVM

I have an application that need to open a dialog from a button where the user enters some information.
At the moment I do it like this (which works fine)
The button click generates a command in the ViewModel.
The ViewModel raises an event which the Controller listens to.
The Controller works out the details of the new window (i.e. View, ViewModel & model) and opens it (ShowDialog)
When the window is closed the Controller adds the result to the eventargs and returns to the ViewModel
The ViewModel passes the information to the Model.
There are a lot of steps but they all make sense and there is not much typing.
The code looks like this (the window asks for the user's name)
ViewModel:
AskUserNameCommand = DelegateCommand(AskUserNameExecute);
...
public event EventHandler<AskUserEventArgs> AskUserName;
void AskUserNameExecute(object arg) {
var e = new AskUserNameEventArgs();
AskUserName(this, e);
mModel.SetUserName(e.UserName);
}
Controller:
mViewModel.AskUserName += (sender,e) => {
var view = container.Resolve<IAskUserNameView>();
var model = container.Resolve<IAskUserNameModel>();
var viewmodel = container.Resolve<IAskUserNameViewModel>(view, model);
if (dlg.ShowDialog() ?? false)
e.UserName = model.UserName;
}
My question is how the horizontal communication works in the MVVM pattern.
Somehow it seems wrong to let the controller be involved in the data transfer between the models.
I have looked at the mediator pattern to let the models communicate directly. Don't like that idea since it makes the model depending on implemetations details of the GUI. (i.e. if the dialog is replaced with a textbox, the model need to change)
I don't like most of the current suggestions for one reason or another, so I thought I would link to a nearly identical question with answers I do like:
Open File Dialog MVVM
Specifically the answer by Cameron MacFarland is exactly what I do. A service provided via an interface to provide IO and/or user interaction is the way to go here, for the following reasons:
It is testable
It abstracts away the implementation of any dialogs so that your strategy for handling these types of things can be changed without affecting constituent code
Does not rely on any communication patterns. A lot of suggestions you see out there rely on a mediator, like the Event Aggregator. These solutions rely on implementing two-way communication with partners on the other side of the mediator, which is both hard to implement and a very loose contract.
ViewModels remain autonomous. I, like you, don't feel right given communication between the controller and the ViewModel. The ViewModel should remain autonomous if for no other reason that this eases testability.
Hope this helps.
i use this approach for dialogs with mvvm.
all i have do do now is call the following from my viewmodel to work with a dialog.
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
I have come across similar problems. Here is how I have solved them, and why I have done what I have done.
My solution:
My MainWindowViewModel has a property of type ModalViewModelBase called Modal.
If my code needs a certain view to be modal, it puts a reference to it in this property. The MainWindowView watches this property through the INotifyPropertyChanged mechanism. If Modal is set to some VM, the MainWindowView class will take the VM and put it in a ModalView window where the appropriate UserControl will be shown through the magic of DataTemplates, the window is shown using ShowDialog. ModalViewModelBase has a property for DialogResult and a property called IsFinished. When IsFinished is set to true by the modal VM, the view closes.
I also have some special tricks for doing interactive things like this from backgroundworker threads that want to ask the user for input.
My reasoning:
The principle of modal views is that other views are disabled, while the modal is shown. This is a part of the logic of the View that is essentially lookless. That's why I have a property for it in the MainWindowViewModel. It I were to take it further, I should make every other property or command for all other VM's in the Main VM throw exceptions, while in modal mode, but I feel this to be excessive.
The View mechanism of actually denying the user any other actions, does not have to be performed with a popup window and showdialog, it could be that you put the modal view in the existing window, but disable all others, or some other thing. This view-related logic belongs in the view itself. (That a typical designer can't code for this logic, seems a secondary concern. We all need help some times.)
So that's how I have done it. I offer it only as a suggestion, there is probably other ways of thinking about it, and I hope you get more replies too.
I've used EventAggregator from Prism v2 in similar scenarios. Good thing about prims is that, you don't have to use entire framework in your MVVM application. You can extract EventAggregator functionality and use it along with your current setup.
You might have a look at this MVVM article. It describes how a controller can communicate with the ViewModel:
http://waf.codeplex.com/wikipage?title=Model-View-ViewModel%20Pattern&ProjectName=waf

Resources