I am simplifying a little here:
I have a tab control, and should like for individual tabs to have the power to create further tabs; siblings if you will. So I am calling the tab control the parent, and its tab pages the children.
Using MVVM, my tab control view model is something like this:
class ParentViewModel
{
ObservableCollection<ChildViewModel> _pages;
public ObservableCollection<ChildViewModel> Pages
{
get
{
if (_pages == null)
_pages = new ObservableCollection<ChildViewModel>();
return _pages;
}
}
public ParentViewModel()
{
Pages.Add(new ChildViewModel());
}
}
So I have a collection of ChildViewModel objects on my ParentViewModel.
This works a treat, and from inside the ParentViewModel I can of course easily add all the extra ChildViewModel objects I want to my collection and have it all nicely reflected in my Views.
What I want to do is allow a button press (for example) on a ChildView to result in the addition of another ChildViewModel to the collection on the ParentViewModel object.
I have read a lot about relay commands, routed commands, relativesource bindings, the dependancy injection pattern and so forth, but could someone tell me please the 'proper' (in an MVVM sense) way to achieve this, and exactly how it is best done. Thank you!
One of the ways I like to handle a situation like this is with Event Aggregating.
It is an ability added with Unity (if you aren't already using it)
Basically you add the Event Aggregator to your Dependancy Injections and then your Parent would subscribe as a listener to the event and your children would publish the event.
The nice part about this is that the children have no concept of who is listening and the parent just knows it has an event request to handle. For more information you can go HERE!
You can use MVVM Lite Messenger class (or write you own):
And send a Message from a childe class to Parent class. See an example here.
create a message class:
public class AddNewChildMessage
{
public string Data {get;set;} //any data you need to pass
}
In a ParrentViewModel's constructor:
Messenger.Default.Register<AddNewChildMessage>
(
this,
( message ) => AddNewChild(message )
);
private void AddNewChild(AddNewChildMessage message)
{
//do staf with message.Data if any
Pages.Add(new ChildViewModel());
}
In a child view model:
Messenger.Default.Send<AddNewChildMessage>( new AddNewChildMessage() );
Adding childs is an example - you can add any logic you want.
Related
I have the following views and viewModels View: Staff, VM: StaffViewModel and View: Notes, VM: NotesViewModel.
My StaffViewModel has a SelectedStaffMember property of type SelectedEmployee.
The Staff view has a button that launches another view (Notes).
When the user enters a note I need to save it against the SelectedEmployee, which means the NotesViewModel needs to know the currently selected employee ID.
Right now Im doing this via my ViewModelLocator but this seems wrong, what is the corret way to pass data to a VM???
I'm using MVVM Light.
Relevant code - StaffViewModel
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
if (value == _selectedEmployee) return;
_selectedEmployee = value;
HolidayAllowance = _staffDataService.GetEmployeeHolidayAllowance(_selectedEmployee.Id);
RaisePropertyChanged();
RaisePropertyChanged(nameof(HolidayAllowance));
}
}
NoteViewModel
public RelayCommand SaveNoteCommand { get; private set; }
private void SaveNote()
{
var note = new Note
{
NoteContent = NoteContent,
EmployeeId = ViewModelLocator.Staff.SelectedEmployee.Id,
NoteDate = NoteDate
};
_dataService.SaveNote(note);
}
I'm using MahApps Flyouts to show the view for add note:
This is where the view is shown, it is launched from MainView.xaml NOT Staff.xaml, which I think is going to be another issue of getting SelectedEmployee ID:
MainView.xaml
<controls:Flyout Name="AddNoteFlyout"
Header="Add Note"
IsModal="True"
IsOpen="{Binding IsAddNoteOpen}"
Opacity="85"
Position="Right"
Width="450">
<views:AddNote VerticalAlignment="Top" Margin="0,30,0,0"/>
</controls:Flyout>
Im considering firing a message on the button click that launches the View, which my staff view would register against. The message would contain the selectedEmployeeId. Would that be a better way?
The simple way
The simple way is what you are doing, but maybe a bit better solution is to create a static or singleton class like a NavigationParameterContainer and store the selected StaffMember in a public property. Then you can retrieve it in your NotesViewModel
The best practice
The better solution for passing data between ViewModels is using a custom navigation service, and navigation aware ViewModels.
MVVMLight don't support this, so either you use a different framework like Prism or write yourself an architecture that you can use for making parameterized navigationt.
The base idea is that you create an INavigationAware interface that support navigation lifecycle callbacks like OnNavigatedTo, which receives an object representing the NavigationParamter (the selected StaffMember).
Then you create some kind of NavigationService with a Navigate method, that accepts some parameter to determine the Page you want to navigate to, and an object wich is the NavigationParamter.
When you navigate you call the Navigate method on your Service and pass the selected item as parameter. Then you need to make the actual navigation inside your service, and after the navigation is finished, you call the OnNavigatedTo callback on your ViewModel if it is implementing the INavigationAware interface. (You can retreive the VM from the Page.DataContext and cast it to INavigationAware if it is not null you can call the OnNavigatedTo).
Finally in your NotesViewModel you just need to implement the INavigationAware interface, and handle the parameter you received in the OnNavigatedTo method.
This is just the basic idea but I strongly recommend you to see some MVVM framework that already implements this. (like PrismLibrary).
I'm learning UWP at the moment in an attempt to port an old Win32 to the new platform. I'm using Template10 and everything runs fine so far, except I'm bit confused on how to implement the problem below.
Problem: In a page, I have to constantly remove and insert user controls depending on a view model property. The user controls are fairly complex and they all look and behave differently. Imagine a wizard with back and next buttons. On every click I have to remove the old content and insert a new one, with completely different view model.
Question: What would be the recommended way of implementing this in a MVVM way?
At the moment, my only idea is to send a message from the page's view model and subscribe for the message in page's code behind where I can create the required component and insert it dynamically in the page (after removing the old one).
In MyPageViewModel:
public IComponentViewModel CurrentComponent {get; set;}
...
public DelegateCommand NextItemCommand = new DelegateCommand(() =>
{
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Publish(CurrentComponent);
});
In MyPage.xaml.cs code behind
public MyPage()
{
InitializeComponent();
var evt = App.EventAggregator.GetEvent<ItemChangedMessage>();
evt.Subscribe(OnItemChanged);
}
private void OnItemChanged(IComponentViewModel viewModel)
{
switch (viewModel.Type)
{
case 1:
// create the new user control and insert it in the container
var component = new TypeOneComponent();
component.DataContext = (TypeOneCompoentViewModel)viewModel;
// ...
case 2:
...
}
}
Not sure this is the best approach tho.
I've been thinking about a Wizard approach lately myself. It seems to me that a FlipView with re-templated left/right buttons is the easiest approach. My WizardViewModel would have several children view-models; something like Page1ViewModel, Page2ViewModel, and so on. I strongly feel that each page view-model would have a dedicated UserControl so the UI can be unique but not dynamic - I think it makes sense to design against dynamic UI, while embracing an adaptive UI - which is a different concept altogether.
The pseudo code might look like this:
public interface IWizardPage { }
public class Page1ViewModel : ViewModelBase, IWizardPage { }
public class Page2ViewModel : ViewModelBase, IWizardPage { }
public class Page3ViewModel : ViewModelBase, IWizardPage { }
public class MainPageViewModel : ViewModelBase
{
public IWizardPage CurrentPage { get; set; }
public IWizardPage Page1ViewModel { get; set; }
public IWizardPage Page2ViewModel { get; set; }
public IWizardPage Page3ViewModel { get; set; }
}
And this:
<FlipView Template="{StaticResource WizardFlipView}"
SelectedItem="{Binding CurrentPage, Mode=TwoWay}">
<Page1UserControl DataContext="{Binding Page1ViewModel}" />
<Page2UserControl DataContext="{Binding Page2ViewModel}" />
<Page3UserControl DataContext="{Binding Page3ViewModel}" />
</FlipView>
This is just a recommendation. But to answer your question, this would be very amenable to the MVVM pattern. I also think this would allow for you to be very flexible without getting so dynamic that maintenance is affordably time consuming. There are lots of ways to do wizards. I think this would be a fine solution, good with the MVVM pattern and fine with Template 10.
Best of luck.
I typically use an ItemsControl. This allows you to have a generic item template and a item specific template if you want and you can add / remove items at will by binding to the ItemsSource.
In your example of a wizard, you might make the main wizard container an ItemsControl that only shows one item at a time and a page would be an "item". The distinction with MVVM is that you don't add child controls, you add data and then specify a template to render it. So your items are going to be simple databound poco objects.
For your actual example, I guess you can add child controls to the ItemsControl and they would render automatically without even using a template since a ContentPresenter knows how to render controls. I would still use data only classes though since one of the tenants of MVVM is to separate the data from the UI. So your child item would be a model and your item specific template would be the UI layout bound to the item data.
I have been strugglin whit this problem for quite some time now. I'm building my first WPF MVVM application. In this App i have a AppView (with it's corresponding viewmodel). Child views are contained into tabs and represented by separated views (UserControl) and have one viewmodel for each view. So far so good.
In one view, a have a list of costumers, and a Delete button. I also have a correspondig command on the viewmodel to actualy delete the record, and this work fine. Now I want the delete button to create a new view with two buttons, one for confirmation and the other for cancel, and then if user click the "Confirm" button execute the delete.
The problem here is that each view, and its correspondig viewmodel are isolated from the other (as long as I understand) so i cannot access the second view viewmodel to see if the confirm button is clicked.
The only posible solution that i found so far is to add an event on one view and subscribe the other view to that event. But this technic is quite complex for such a trivial task. Is there other alternatives? Can't the two views share the same datacontext or viewmodel?
Thanks!
var dialog = new DialogViewModel();// could be a DialogService if you wish
with in this DialogViewModel or DialogService again your choice how you actually do it.
dialog.Result which in this case would return your confirmation either true or false
var settings = new Dictionary<string, object>();
settings["Owner"] = this;
settings["WindowStartupLocation"] = WindowStartupLocation.CenterParent;
windowManager.ShowDialog(dialog, null, settings);
if(dialog.Result == true)
do the delete on the parent viewmodel.
Or you can do it all with IEventAggregator and a message package. I personally use the first for a lot of things. Sometimes a combination depending on situation.
Most will favor the IDialogService method of things for SoC, and do DI with it to bring it into the viewmodel using it. Then each viewmodel will be responsible its own dialogs. From there you can call ShowDialog since its part of the WindowManager, which you click Yes or No, or what ever you setup for you dialogview. Numerous ways to skin the cat but in the end you want KISS methodology and something that won't break the patterns you are trying to adhere too.. Hell for all it matters you could add it to a viewmodelbase base class for all of your viewmodels to inherit to access globally. All a function how you want your app to behave in the end anyway.
--update--
public class YourViewModel(IWindowManager winMan)
{
private readonly IWindowManager _winMan;
public YourViewModel()
{
_winMan = winMan;
}
public void DeleteCustomer()
{
var dialog= new DialogViewModel(); // not best way but...
var settings = new Dictionary<string, object>();
settings["Owner"] = this; //<< Parent
settings["StartupLocation"] = WindowStartupLocation.CenterParent;
_winMan.ShowDialog(dialog, null, settings);
if(dialog.Result)
//do delete
else
//do nothing
}
}
I am trying to close a window from its ViewModel. I am using the MVVM pattern. I have tired to get the window using;
Window parentWindow = Window.GetWindow(this);
But I cannot do this, how do I get the window of the ViewModel so I am able to close the window. I want to be able to do this in code.
Can you find the parent window in code?
ViewModels should not be referencing the View in any way, including closing windows, in MVVM.
Instead, communication between the View and ViewModel is typically done through some kind of Event or Messaging System, such as Microsoft Prism's EventAggregator, or MVVM Light's Messenger
For example, the View should subscribe to listen for event messages of type CloseWindow, and when it receives one of those message it should close itself. Then the ViewModel simply has to broadcast a CloseWindow message anytime it wants to tell the View to close.
There's a brief overview of event systems in MVVM, and some examples, on my blog post about Communication between ViewModels if you're interested
yes referencing view in viewmodel isn't best practice. WHY? because when you unit test your viewmodel it is require you to instantiate view, for small view will not difficult to do that, but for a complex view with complex tree of dependency? that wont be good.
for me, the easiest way to do communication with view is by passing IInputElement on viewmodel constructor. the bennefit of IInputElement is Routed Event backbone, it has RaiseEvent and AddHandler method required for routed event. thus you can bubble/tunnel/direct event to any view or viewmodel on your application freely without any additional library.
here is my the simplified code on viewmodel but remember this technique only work for view first approach
public class MyViewModel : INotifyPropertyChanged
{
public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel));
private IInputElement dispatcher;
public MyViewModel(IInputElement dispatcher)
{
this.dispatcher = dispatcher;
}
public void CloseApplication()
{
dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent));
}
}
on your View simply
DataContext = new MyViewModel(this)
//notice "this" on the constructor
and the root view (Window) of your application simply
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose));
}
private void onRequestClose(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Close();
}
}
}
and because IInputElement is interface rather than class, you easily create a mock class for your unit test
var target = new MyViewModel(new DispatcherMock)
or you can use mock library like RhinoMocks
for further reading, you can learn more about how to use Routed Event
Let the ViewModel do this, if really in need.
The Models says for example, that there are no longer valid data
pass that information to the ViewModel
the ViewModel recognizes, that it can no longer display anything
and then closes the window.
An empty view is the normal way of expressing that there are no more data
You can define an action in your ViewModel
public Action CloseAction { get; set; }
then, in your window (for example in the DataContextChanged) you can set this action :
((IClosable)viewModel.Content).CloseAction = () => System.Windows.Application.Current.Dispatcher.Invoke(Close());
Well, all this is part of a bigger dependency injection pattern, but basic principle is here...
Next, you juste need to call the action from the VM.
There is a useful behavior for this task which doesn't break MVVM, a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel.
This behavior demonstrates a simple technique for allowing the
ViewModel to manage the closing events of the View in a
Model-View-ViewModel application.
This allows you to hook up a behavior in your View (UserControl) which
will provide control over the control's Window, allowing the ViewModel
to control whether the window can be closed via standard ICommands.
Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
http://gallery.expression.microsoft.com/WindowCloseBehavior/
In a MVVM WPF application.
How do you set a second windows parent from the ViewModel?
example:
view1 -- viewModel1
viewModel1's command calls:
var view2 = new view2
view2.Owner = <----This is the problem area. How do I get view1 as the owner here from the viewModel?
view2.Show()
EDIT:
See accepted answer below, then read the following edit.
I'am using MVVM light -> http://mvvmlight.codeplex.com/ (awesome btw)
The baked-in messaging system is great. I am now sending a message from the viewmodel to my view telling it to show another window.
For the message I'am currently using a string with a switch statement in the main view to determine what view to open; however I may tinker with the tokens that also are part of MVVM light toolkit.
Thank you!
In my opinion, opening a new window is the responsibility of the View, not of the ViewModel. Personally, I would use the same approach as used for displaying a dialog box (this was discussed in this forum already):
Have the ViewModel send a Message to the View requesting that it opens a new Window.
(alternatively) use an IDialogService or whatever you want to call it which you pass to the ViewModel's constructor. This service will be in charge of opening the Window (or of delegating this task to the View).
This way, you keep a clean separation of concerns and your VM remains testable (you can unit test that the request to open the new WIndow has been sent, but you couldn't test that the window has been, indeed, open).
Does that make sense?
Cheers,
Laurent
From your viewmodel call
Messenger.Default.Send<NotificationMessage>(new NotificationMessage("Open Window"));
And from your view's codebehind (a view that call the second
view) easily write this in the constructor:
Messenger.Default.Register<NotificationMessage>(this, ReplyToMessage);
And also write this method in the view's codebehind:
private void ReplyToMessage(NotificationMessage msg)
{
if (msg.Notification == "Open Window")
{
SecondWindow win = new SecondWindow();
win.ShowDialog();
}
}
I don't have an answer of my own but here's a few links to things I've been looking at lately that might help. I'll also be interested in anything others suggest.
As I understand it, the key thing is, you shouldn't be creating Views from within a View Model if possible, so you need a means of communicating what you need in a loosely coupled fashion.
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
http://www.codeproject.com/KB/architecture/MVVM_Dialogs.aspx
Handling Dialogs in WPF with MVVM
You can do in this way like you need to create some events and register those in view and call these in view model.and open that pop up window.
Like This example
public class Mainclass : MainView
{
public delegate abc RegisterPopUp(abc A);
public RegisterPopUp POpUpEvent;
public RelayCommand ShowCommand { private set; get; }
public void ShowCommand()
{
ShowCommand("Your parameter");
}
}
inside the view
MainView mn = new MainView();
Register the event here like mn.POpUpEvent += then click on tab button double time and in registers popup method write the code for opening the pop up window.
Prism-Event Aggrigator is good approach, where we can create independent module without dependency. first viewmodel will publish event and then another view or view or viewmodel can subscribe that event from event aggrigator.
in this case Unity container can also use to inject one viewmodel in to another with dependency injection.