I want to make a reusable UserControl as:
A VIEW with its own VIEWMODEL where is the logic data recovery
This UserControl (or View ?) has a button 'OK' to target a RELAYCOMMAND in his viewmodel
I'm hosting this 'UserControl' in another VIEW ('MainPage'), who, herself, has its viewmodel
My question is:
How can I target the properties of VIEWMODEL of my 'MainPage' with the values outlined by my UC ?
As long as your user control is contained in your main page it will inherit the main pages view model. This is the default and it applies unless you explicitly change the data context through data binding or code.
If your user control binds to it own view model then you could let the main view model contain an instance of the child view model and expose it through a public property. Now you could set the data context of your user control by binding the DataContext property to the property on the main view model.
Finally if your child view model has a reference to the main view model then they will be able to communicate as needed.
Edit:
I'll try to setup a simple example:
First the view models:
public class MainPageViewModel
{
public MainPageViewModel()
{
ChildViewModel = new ChildViewModel(this);
}
public ChildViewModel {get; private set; }
public ICommand OkCommand { get { // return the command here }}
}
public class ChildViewModel
{
private MainPageViewModel _parentViewModel;
public ChildViewModel(MainPageViewModel parentViewModel)
{
_parentViewModel = parentViewModel;
}
// Returns the command from the main page view model
public ICommand OkCommand { get { return _parentViewModel.OkCommand; } }
// Other properties as well
}
Here we have the main view model that has the child view model as a property. The child view model exposes the OkCommand that returns the value from the main view model.
Now in your main page xaml you can do the following:
<uc:MyUserControl DataContext="{Binding ChildViewModel}" />
Here you insert your user control and set it's data context to the child user control view model.
Related
I have a MainWindowView that has a grid with 2 columns each having 1 UserControl View. MainWindowView constructor creates instance of MainWindowVM and sets the data context to this new instance.
this.DataContext = new MainWindowVM(this)
Now question is I am trying to set data context of each UserControlView to an instance of it's respective ViewModel inside MainWindowVM. How can I access the UserControlView inside MainWindowVM to do something like this
UserControl1View.DataContext= new UserControl1ViewModel()
If I can do this, it will allow me to use MainWindowVM as a common hub holding all kinds of event subscriptions from the 2 userControls.
ViewModel must not depend upon View, and both must have one-to-one relationship between them. So best is to use Binding to set DataContext and if this setting of DataContext depends upon some condition, then use Triggers.
Don't-Do-That.
A better approach is to have a ViewModel reference in he View.
Create an interface similar to this:
public interface IView<T> where T : class
{
T ViewModel;
}
Now, your Views must implement that interface
public partial class MainView : Window, IView<MainViewModel>
{
public MainViewModel ViewModel { get; set; }
And inject the ViewModel in the view constructor:
public MainView(MainViewModel vm)
{
this.ViewModel = vm;
this.DataContext = this.ViewModel;
// you can create the VMs you want for the another views
var vm1 = new UserControl1ViewModel();
// and pass it to the UserControl1View (UserControl1View implements IView<T>
var view1 = new UserControl1View(vm1);
Few words about my MVVM-based WPF app. I've defined MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
{
...
public ViewModelBase CurrentViewModel {get; set;}
...
}
This is data context for my MainWindow.xaml. This property has it's own template binding to specific View (depends on which 'inner' view model is currently included). This MainWindowViewModel is listening for Mvvm-Light Messenger message which changes current view model. Now my problem.
I'd like to assign there LoginViewModel with property SelectedUser. I'm getting property of SelectedUser from another ViewModel, where I'm executing this command:
this.UserSelectedCommand = new RelayCommand(() =>
{
LoginViewModel viewModel = new LoginViewModel();
viewModel.SelectedLogin = this.SelectedUserFromList;
MessageHelper.SendViewModel(viewModel); //change CurrentViewModel
viewModel.RaisePropertyChanged("SelectedLogin");
});
My LoginViewModel.cs has defined default value of SelectedLogin in constructor.
So in ViewModel which is responsible for displaying list of users I'm selecting one, I have this value in SelectedUserFromList property and I'd like to go back to login View with selected user. Which means I'd like to assign new LoginViewModel with SelectedLogin to CurrentViewModel property form MainWindowViewModel.
After executing UserSelectedCommand I can see proper View which is binded to LoginViewModel, but I have everywhere default values of properties.
I guess that because of this
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<views:LoginView/>
</DataTemplate>
I do have proper ViewModel in CurrentViewModel (with changed SelectedLogin) but template is loading LoginViewModel again (so constructor sets default values). Am I right? Because I can't see any other reasons.
EDIT
Ok, now I'm sure that DataTemplate is creating new instance of LoginViewModel (this one with default values). But what now? I fixed it with Singleton, but I'm not sure if this is good solution.
I am refactoring my application to utilize MVVM. I used to keep a List<Product> variable in the Application class that I was able to bind a ListView to. This List made up my Data layer. The Page with this ListView is a master/detail layout. With MVVM, I am thinking that the List should now hold instances of the ProductModel as it is the data layer. If I should be binding to ViewModels, do I need a separate List of ViewModels too?
You might need to take a different perspective on MVVM. Your View is the page with the controls (XAML) and your ViewModel is the glue between your data model and the page. The View's entire data context will be set to the ViewModel (done either in the XAML directly or in code-behind depending on which MVVM camp you subscribe to).
In your example, you would move List<Product> onto the ViewModel as ObservableCollection<Product> and make sure that your ViewModel implements the INotifyPropertyChanged interface. INotifyPropertyChanged is the contract the View uses to know when to update it's binding. You will use an ObservableCollection<T> instead of a list because ObservableCollection<T> implements INotifyPropertyChanged itself.
Your View's DataContext property will be set to an instance of the ViewModel. On the View, the ListBox control's ItemsSource property will be set to bind to the Product collection. You can then have methods inside of your ViewModel that will be responsible for communicating with your data store to populate the observable collection.
ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Product> _products = null;
public ObservableCollection<Product> Products
{
get { return _products; }
set
{
if( _products != value )
{
_products = value;
if( this.PropertyChanged != null )
{
this.PropertyChanged( this, new PropertyChangedEventArgs( "Products" ) );
}
}
}
}
// have code in here that loads the Products list from your data store (i.e. service call to database)
}
View Code-Behind
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
View
<ListBox
ItemsSource={Binding Path=Products, Mode=OneWay}
SelectedItem={Binding Path=SelectedProduct, Mode=TwoWay}
...
/>
I am working on my first WPF/MVVM application, and I have come across a limitation in my knowledge of commands!
Here is my scenario.
I have a window - Customer.xaml.
It houses 2 usercontrols
viewCustomerSearch.xaml
viewCustomerDetails.xaml
Each of THOSE has it's own view model.
So, the hierarchy looks like this:
... Customer.xaml
... ... viewCustomerSearch.xaml
... ... ... viewmodelCustomerSearch.xaml
... ... viewCustomerDetails.xaml
... ... ... viewmodelCustomerDetails.xaml
I understand this to be a 'not uncommon' scenario.
For what it is worth, the user selects a customer by double clicking on a listview line in the viewCustomerSearch.xaml control.
The problem is: I now need to tell the viewmodelCustomerDetails.xaml class which customer the user has just selected. I cannot work this out at all.
Does anyone have any help on where I declare the command I need, how it gets hooked up, where the implementation code fires, etc?
Any help gratefully appreciated,
DS
Typically, to do inter-viewmodel communication, you can either:
Use standard .NET events, and use the parent view model as the mediator - in your case the Customer view model would have references to the 2 child view models, and can subscribe to events, and call appropriate methods on the child view models when the events are published
Use an event aggregator pattern
Frameworks such as Caliburn.Micro and Prism provide an implementation of the event aggregator pattern.
Alternatively, if you don't need completely decoupled view-models, then your Customers.xaml could set its DataContext to an instance of CustomersViewModel. The search view would inherit this data context, would bind it's list view to the list of customers, and would set the SelectedItem property in response to a double click. The detail view DataContext would be bound to the SelectedItem property.
public class CustomersViewModel : ViewModelBase
{
public Customer SelectedItem
{
get { return _selectedItem; }
set { Set(() => SelectedItem, ref _selectedItem, value); }
}
private Customer _selectedItem;
public IEnumerable<Customer> Customers { get; private set; }
}
public class Customer : ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); }
}
...
}
I have a parent View that is xaml-binded to a ViewModel (the viewmodel is declared in the xaml).
This parent view can then display a child View (via NavigationService, aka navigation:Frame).
The parent view never goes out of scope, but I want the new child View to share the parent's ViewModel.
How can i do this? Because by declaring the same viewmodel in the child View's xaml would mean the child view gets it's own viewmodel instance (i.e. it is not the same viewmodel instance as it's parent view).
Thanks!
Sounds like a perfect opportunity to either (a) use MEF. Export the view model, then import it in both the parent and child views. By default, they will share the same object. Or (b) create a locator class that keeps track of the view model instances, exposed via a static property, and use that static property to retrieve the view model in the parent and the child:
public static class Locator
{
private static readonly MyViewModel _instance = new MyViewModel();
public static MyViewModel Instance
{
get { return _instance; }
}
}
public partial class MyView
{
public MyView()
{
InitializeComponent();
LayoutRoot.DataContext = Locator.Instance;
}
}
Well until somebody gives me a good answer i'll be using the following solution (if it works as I havent really tested it yet).
My hack solution:
The ViewModel will have a public static reference to itself.
Then child view(s) will set its DataContext to the ViewModel's static reference.
Cheers.
The child view inherits its DataContext from its parent view, there is no need to declare or assign it a second time.