Setting datacontext inside ViewModel - wpf

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);

Related

Bind Control to Two properties

Using MVVM pattern and WPF, I would like to bind my controls to two properties. Let's say I have a label that is bind to property on VM1 and I would like to bind it as well to the property on VM2 to send received value from VM1 to VM2.
I could use messenger and for multiple items Tuple Class, but I was wondering if there is another solution for that. Multibinding? but then I need ValueConverter.
Thank you for any suggestions.
Since The View-Model is an abstraction of the view that exposes public properties and commands, it doesn't make a lot of sense for a view to have two view-models the way you explained. It'll be more rational if there is a VM class as the view-model of your view that has two properties of type VM1 and VM2. Then the binding would be on VM.VM1.YourText and you can notify VM2 via events like this:
in VM1:
public event EventHandler<EventArgs> ValueChanged;
string _yourText;
public string YourText
{
get
{
return _yourText;
}
set
{
_yourText= value;
if (ValueChanged != null)
ValueChanged(_yourText, new EventArgs());
}
}
In VM:
public VM1 Vm1 {get; set;}
public VM2 Vm2 {get; set;}
public VM()
{
InitializeComponent();
Vm1 = new VM1();
Vm2 = new VM2();
Vm1.ValueChanged += Item_ValueChanged;
DataContext = this;
}
void Item_ValueChanged(object sender, EventArgs e)
{
VM2.YourAnotherText = sender.ToString();
}
If 2 properties are connected, usually INotifyPropertyChanged can be utilized to notify about a change of 2 or more properties if on the same ViewModel.
What I understand is that you want also to notify a View attached to a ViewModel about a change of a property on another ViewModel. This is usually done by letting ViewModels exchange information.
If that's a one rare case, using message bus for that might be an overkill. Usually keeping a reference to each view model and changing properties from outside should be all right. To keep separation of concerns you can create an interface on one or both viewmodels and reference this interface instead of a concrete type.
Overall keeping a single binding between a control and property keeps it simple and easy to understand and you should worry about making sure that this property handles all changes to/from other VMs.

MVVM - data layer of models and viewmodels

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}
...
/>

How do I load a DataGrid with data from a database using WPF and MVVM?

I am a complete newbie to WPF and MVVM so I apologise in advance if this query is quite simple. I've searched online and havent been able to find anything which fits my requirements. Hense why I'm here!
I am currently trying to implement a table of data queried from a database using LINQ. This is the query I run:
DataContext connection = new DataContext();
var getTripInformation = from m in connection.tblTrips
where m.TripDate > DateTime.Today
select new { m.TripID, m.TripName, m.TripDate, m.ClosingDate, m.PricePerAdult, m.PricePerChild, m.Status };
Which fills my var with the relevant information which I expect.
Now, what I want to be able to do is diplay this in my View using a DataGrid. Can anyone assist me with this?
In a nutshell, you will have your View and ViewModel. The ViewModel will need to implement the INotifyPropertyChanged interface to facilitate view binding. This just provides an event that is raised when you change a property on your ViewModel. Your View will then bind to the ViewModel's properties. This works as long as the DataContext of the view is set to a ViewModel instance. Below, this is done in code-behind, but many purists do this directly in XAML. Once these relationships are defined, run your LINQ query to populate the ObservableCollection (which also implements INotifyPropertyChanged for when items are added/removed internally) and your grid will show the data.
ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<MyRecord> _records = null;
public ObservableCollection<MyRecord> Records
{
get { return _records; }
set
{
if( _records != value )
{
_records = value;
if( this.PropertyChanged != null )
{
this.PropertyChanged( this, new PropertyChangedEventArgs( "Records" ) );
}
}
}
}
public MyViewModel()
{
this.Records = new ObservableCollection<MyRecord>();
this.LoadData();
}
private void LoadData()
{
// this populates Records using your LINQ query
}
View (Code-Behind)
public class MyView : UserControl
{
public MyView()
{
InitializeControl();
// setup datacontext - this can be done directly in XAML as well
this.DataContext = new MyViewModel();
}
}
View (XAML)
<DataGrid
ItemsSource="{Binding Path=Records, Mode=OneWay}"
...
/>
If you set AutoGenerateColumns = 'True' on your DataGrid, it will create a row for each public property of the bound item type. If you set this value to false, you will need to explicitly list the columns and what property they will map to.
If you are developing the application using MVVM then you need to do-
ViewModel Class - that will have UI logic and will implement INotifyPropertyChanged interface. You need to create Property of type collection which will gets binded with DataGrid. And on setter of this proeprty you need to call PropertyChangedEventHandler.
You need to set the DataContext of View to your ViewModel on XAML, Codebehind, ViewModel or on some mediator class.

How to reuse entity framework datasource for multiple views WPF EF MVVM

Hi I am designing an application using WPF4, EF and MVVM. I want to be able to create reusable UserControls that I can use in multiple windows in the application, and have them draw data from the same source.
Lets say I have a GraphView component and a TableView component that can appear on the same page or in different places in the application, and I want them to both reflect the same collection of filtered EF entities. MVVM common practice seems to require that each view has its own viewmodel. But should I be be using a joint viewmodel and bind both to it, so if you change the data or filter, both would update simultaneously? If not how should I handle this?
Thanks for any advice!
One approach could be to have two ViewModels, one for each of your Views/UserControls, and then nest them into some top or higher level ViewModel. If, for example, both Views reside in a MainWindow View, it could look like this:
public class MainWindowViewModel
{
public MainWindowViewModel(IRepository repository)
{
SharedUserControlData sharedData = new SharedUserControlData()
{
MyCollection = new ObservableCollection<MyEntity>(
repository.GetMyEntities()),
// instantiate other shared data properties
}
UserControl1ViewModel = new UserControl1ViewModel(sharedData);
UserControl2ViewModel = new UserControl2ViewModel(sharedData);
}
public UserControl1ViewModel UserControl1ViewModel { get; private set; }
public UserControl2ViewModel UserControl2ViewModel { get; private set; }
// more stuff...
}
You have a SharedUserControlData class which contains properties both views can bind to:
public class SharedUserControlData : INotifyPropertyChanged
{
public ObservableCollection<MyEntity> MyCollection { get; set; }
// other properties
// INotifyPropertyChanged implementation
}
And the ViewModels of the UserControls get those data injected:
public class UserControl1ViewModel
{
public UserControl1ViewModel(SharedUserControlData data)
{
SharedUserControlData = data;
}
public SharedUserControlData SharedUserControlData { get; private set; }
// more stuff
}
// and the same for UserControl2ViewModel
Your UserControl Views are bound to the ViewModels by a DataTemplate:
<DataTemplate DataType="{x:Type vm:UserControl1ViewModel}" >
<v:UserControl1View />
</DataTemplate>
// and the same for UserControl2ViewModel
And some controls inside of the UserControls are bound then to SharedUserControlData.MyCollection and other properties of the UserControlViewModels. The DataContext of the MainWindow is the MainWindowViewModel:
IRepository repository = new MyRepository(); // or use Dependency Injection
MainWindow window = new MainWindow();
MainWindowViewModel viewModel = new MainWindowViewModel(repository);
window.DataContext = viewModel;
In the XAML of your MainWindow we bind the UserControls to the nested ViewModels of the MainWindow's DataContext (which is the MainWindowViewModel):
<StackPanel>
<v:UserControl1View DataContext="{Binding UserControl1ViewModel}" />
<v:UserControl2View DataContext="{Binding UserControl2ViewModel}" />
</StackPanel>
This way both UserControls would have different ViewModels but both share the same SharedData instance which comes from the higher level ViewModel containing both UserControl's ViewModels. The Repository then has access to the EF data context. (Having repositories here is only an example, you could also inject instances of Service classes or something.)
Your EF classes, near as I've been able to tell after only four days using EF, reside at the project level. My first instinct would be to implement a singleton containing references to the entities you want to hold common across your viewmodels. That will create a class dependency on your singleton, of course.
This actually sounds like a design problem addressed by Unity, MEF, or something else that will do dependency injection. You'd have your EF classes in a module of one of those frameworks and use their protocols to coordinate between EF and your VM's. Then a change in your filter or your data in EF would also trigger a message your VM's could register to receive, in order to trigger UI changes or VM state changes or whatever.
I agree wholeheartedly with the one ViewModel per View approach. For shared data you can either pass references around (tedious and error prone), you can use DI (depending on your comfort level but doesn't play well with design time data), or you can create static properties in your App.xaml.cs which are then shared and accessible throughout the application. In the long run, DI will probably get the most support from other folks.
You might have a look at the BookLibrary sample application of the WPF Application Framework (WAF). It contains two different Views (BookListView [Master], BookView [Detail]) for the same data source which is provided by the Entity Framework.

Silverlight UserControl and his own Viewmodel hosted in a View

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.

Resources