Sharing context between view models - silverlight

I am building my first silverlight application. This application has several forms that allow user to save Customers, Vendors, Staff etc. Each page has one parent usercontrol (with a corresponding viewmodel) and one or more child usercontrols (each with a viewmodel of its own). For example customer form would have Customer usercontrol as the parent and Address Usercontrol, Phone Numbers UserControl as Child usercontrols on the form. Now the parent "Customer" is responsible for ensuring the data is saved for Customer, Address and Phone when the user clicks the Save button. Behind the scenes I could share a datacontext instance between customer, address and phone number viewmodels. So when the Save button is clicked, the customer usercontrol could save data for all three (since its datacontext would have the Address and PhoneNumber entities as well).
What I would like to know how to pass this datacontext from Customer to Address and Phone Number? One way could be to pass datacontext to Address and Phone number view model in their respective constructors but then I would need to ensure the Address and PhoneNumber constructors are called only after Customer viewodel has been created. can there be a better solution? Does MEF offer any better solution?

You have the power of Silverlight client-side. Meaning: you have statics :-)
The way I did this in our most recent app was to create a ContextCache. Basically, it's a static class that holds all of my RIA contexts for use in any of my ViewModels. Like so:
public static class ContextCache
{
private static TicketContext _ticketContext;
public static TicketContext TicketContext
{
get
{
if (_ticketContext == null)
_ticketContext = new TicketContext();
return _ticketContext;
}
private set { _ticketContext = value; }
}
}
So, in a ViewModel, you just make a call to ContextCache.TicketContext and you can save changes. This way you can take advantage of all of the state change tracking in RIA and let it handle all the details for you about object persistance (if you've coded your services correctly that is, but that's another story).

Perhaps with your architecture a bad solution can be with IsolatedStorageFile which you may simulate ASP.NET's session!
From my understanding, you are trying to overuse the MV-VM pattern in your app! One of the important reason ViewModel came in our life is because of unit testable! If I were you, I would have had a Customer ViewModel and use it in all my child-usercontrols!
Depending on the complexity and isolation if child controls requires it own separate ViewModel, you may consider a parent ViewModel and have properties of the child ViewModel.
public class ParentViewModel : FrameworkElement {
Child1ViewModel Child1 { get; set; } //Must be a dependency property
Child1ViewModel Child2 { get; set; } //Must be a dependency property
ICommand SaveButtonCommand; //Your delegate command of your save button
public ParentViewModel() {
Child1 = new Child1ViewModel();
Child2 = new Child2ViewModel();
}
}
And you can use this parent view on your Parent XAML
<UserControl.DataContext>
<ViewModels:ParentViewModels />
</UserControl.DataContext>
<Controls:Child1 DataContext="{Binding Child1}" />
<Controls:Child2 DataContext="{Binding Child2}" />
You also have ICommand SaveButtonCommand in your ParentViewModel and manipulate with the properties in your child ViewModel when clicked!
Hope it helped!

Related

Share State Between MainWindow and UserControl

I have a WPF application and want to share data between my MainWindow and one or more UserConrols, but for simplicity lets assume I have only one MainWindow.xaml and one UserControl.xaml. From what I've gathered so far, this can be done with Bindings and Properties. So I tried this with no success.
The Object I want to share between the controls looks like this:
SharedObject {
prop string Name;
prop List<Product> Products;
}
Product {
prop string ItemName;
prop double Price;
prop bool Available;
}
So I load the SharedData in the MainWindow and want to be able to edit this in the UserControl, but in TwoWay mode, to get the modified product list updates also in the MainWindow. I also want to access and modify the Name Property of the SharedObject in the UserControl.
How can I achieve this? Is the Property/Binding the way to go? Can this state management also be done in a more elegant way? (in dotnet Core 3.1)
You should set the DataContext of the window to an instance of your view model (SharedObject) and then let the UserControl inherit the DataContext from its parent window (which it does by default).
You can then bind directly to any public property of the view model from the an element in UserControl.

What constitutes a viewmodel?

I'm still not totally sure what constitutes a view model. I have a class that I use to wrap my model and alter the data slightly, but I am not sure whether it constitutes a view model. What is necessary to be considered a view model? Is it merely not supposed to have direct dependencies on the view, so that the viewmodel does not know how the view uses its properties and the view does not know what is in the viewmodel? and when the view wants to update anything it just gives some abstract command that the viewmodel takes and uses to update the model?
As I understand it in MVVM, I'm supposed to use properties on the view that bind to properties on the viewmodel that bind to properties on the model.
and in the reverse direction, I'm supposed to use commands from the view to the viewmodel, which then can either uses Icommand to command the model, or can just call public functions in the model to make changes to it.
One confusing thing is that in the example of MVVM that I saw made it seem like in MVVM the view should have no code behind other than perhaps creating commands, but I don't see how I could do that in my current project. I'm making a custom control using lots of controls that are interacting on events.
how would I make one treeview expand on another treeview's expand without using events?
Often, View Models end up being very similar to your domain models. One of the main goals of having View Models is to separate the GUI development from the business logic. For example, let's say you have a "User" domain model that has an IsAdmin property which you don't want exposed to the View. You create a View Model called "UserViewModel" that still has the ID, Username, and Password (see example code below), but doesn't have an IsAdmin property. Another approach is to use the domain model inside your view model, see the "AlternateUserViewModel" class below. There are pros and cons to any View Model solution. Creating the UserViewModel class with properties means you are essentially duplicating the objects you created for the domain model, since often times your domain models will be very similar to your view models. With the AlternateUserViewModel approach, there's not clear separation between your business logic layer and your GUI layer because the view model still needs to "know" about the domain model. What approach you decide on really depends on the environment you're working in. For personal projects, I like using the 2nd approach because separating the business logic from the design layer isn't such a major concern that I wouldn't want to let the view model layer "see" the domain model layer, but for a large corporation where you have separate teams working on the design layer and the back-end, the first approach may be preferred.
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool IsAdmin { get; set; }
}
public class UserViewModel
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public class AlternateUserViewModel
{
public User User { get; set; }
public User ToDomainModel()
{
if (User == null)
return null;
// if this is an existing user, retrieve it from the database so you're not overwriting the IsAdmin property
if (User.ID != default(int))
{
User existingUser = UserService.GetUserByID(User.ID);
existingUser.Username = User.Username;
existingUser.Password = User.Password;
// IsAdmin is not set because you don't want that property exposed in the View Model
return existingUser;
}
else
{
return new User
{
Username = User.Username,
Password = User.Password,
IsAdmin = false
};
}
}
}
You have a number of questions here (consider breaking it into multiple posts). I'll try to answer some.
I think of ViewModel as THE app, in other words, this is where most of the app's logic happens.
In other words, ViewModel receives an input of:
Commands from the View
Changes in bound properties from the View
Events from a background service (e.g. when data is received from the Web)
Other events from the system or from domain models
and generates an output of:
changing properties that the View would bind to (e.g. IsBusy that could cause the View to display the waiting indicator)
showing/hiding stuff in the View (indirectly, of course, for example using various bool Properties)
causing navigation to other Views (also indirectly, since it doesn't have direct access to NavigatonService available to the View).
Another way to think about the ViewModel is like so: the ViewModel is the complete user-facing state of the system. In other words, this state and nothing else would be used by the View to show/present this state in some user-understandable way.
About Commands vs. Events:
Unfortunately, not everything in WPF is exposed as a Command. Buttons generate Commands, but not all controls do.
Luckily, you can use Behaviors to translate Events to Commands and some frameworks provide the implementation. Here's an example of how MVVM Light does this. Blend also provides this functionality.
Also, why do you need to generate Commands in the code-behind? Frameworks like MVVMLight provide an implementation of a RelayCommand (or DelegateCommand) that removes the need to create different ICommand implementations. You can just as easily implement it yourself.

passing data to a mvvm usercontrol

I'm writting a form in WPF/c# with the MVVM pattern and trying to share data with a user control. (Well, the User Controls View Model)
I either need to:
Create a View model in the parents and bind it to the User Control
Bind certain classes with the View Model in the Xaml
Be told that User Controls arn't the way to go with MVVM and be pushed in the correct direction. (I've seen data templates but they didn't seem ideal)
The usercontrol is only being used to make large forms more manageable so I'm not sure if this is the way to go with MVVM, it's just how I would of done it in the past.
I would like to pass a class the VM contruct in the Xaml.
<TabItem Header="Applicants">
<Views:ApplicantTabView>
<UserControl.DataContext>
<ViewModels:ApplicantTabViewModel Client="{Binding Client} />
</UserControl.DataContext>
</Views:ApplicantTabView>
</TabItem>
public ClientComp Client
{
get { return (ClientComp)GetValue(ClientProperty); }
set { SetValue(ClientProperty, value); }
}
public static readonly DependencyProperty ClientProperty = DependencyProperty.Register("Client", typeof(ClientComp),
typeof(ApplicantTabViewModel),
new FrameworkPropertyMetadata
(null));
But I can't seem to get a dependancy property to accept non static content.
This has been an issue for me for a while but assumed I'd find out but have failed so here I am here.
Thanks in advance,
Oli
Oli - it is OK (actually - recommended) to split portions of the View into UserControl, if UI became too big - and independently you can split the view models to sub view models, if VM became too big.
It appears though that you are doing double-instantiations of your sub VM. There is also no need to create Dependency Property in your VM (actually, I think it is wrong).
In your outer VM, just have the ClientComp a regular property. If you don't intend to change it - the setter doesn't even have to fire a property changed event, although it is recommended.
public class OuterVm
{
public ClientComp Client { get; private set; }
// instantiate ClientComp in constructor:
public OuterVm( ) {
Client = new ClientComp( );
}
}
Then, in the XAML, put the ApplicantTabView, and bind its data context:
...
<TabItem Header="Applicants">
<Views:ApplicantTabView DataContext="{Binding Client}" />
</TabItem>
I answered a similar question as yours recently: passing a gridview selected item value to a different ViewModel of different Usercontrol
Essentially setting up a dependency property which allows data from your parent view to persist to your child user control. Abstracting your view into specific user controls and hooking them using dependency properties along with the MVVM pattern is actually quite powerful and recommended for Silverlight/WPF development, especially when unit testing comes into play. Let me know if you'd like any more clarification, hope this helps.

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.

WPF + MVVM - How to bind a property to the parent view's data context

Working with the MVVM pattern, I have a pair of view model classes that represent a two-tier data hierarchy, each with a corresponding UserControl that represents its view. Both view model classes implement INotifyPropertyChanged and the root level view model exposes a property that is relevant to both its own view and the child view.
The root level view acquires the root level view model as its data context and explicitly assigns a data context to its contained view. However, it also needs to bind one of the properties of the child view to the above-mentioned shared property. Here is how I have attempted to achieve this, but it's not working:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
Although there are no runtime binding errors and the child view correctly binds to the appropriate child view model instance, its EditingMode property is never set. I have run tests to verify that the corresponding view model property is being modified and that it is notifying this change via INotifyPropertyChanged, but the binding fails to detect it.
Is there a better way to declare this binding or have I made a more basic architectural error?
Many thanks for your advice,
Tim
Update: As requested, I am posting some code to show a very simplified version of my views and view models, together with the results of an experiment that I have conducted that may provide some additional clues.
// The relevant parts of the ParentViewModel class
public class ParentViewModel : INotifyPropertyChanged
{
// Although not shown, the following properties
// correctly participate in INotifyPropertyChanged
public ChildViewModel SelectedChild { get; private set; }
public ContentEditingMode EditingMode { get; private set; }
}
// The relevant parts of the ChildViewModel class
public class ChildViewModel : INotifyPropertyChanged
{
// No properties of ChildViewModel affect this issue.
}
// The relevant parts of the ParentView class
public partial class ParentView : UserControl
{
// No properties of ParentView affect this issue.
}
// The relevant members of the ChildView class
public partial class ChildView : UserControl
{
public static readonly DependencyProperty EditingModeProperty =
DependencyProperty.Register(
"EditingMode",
typeof(ContentEditingMode),
typeof(PostView)
);
public ContentEditingMode EditingMode
{
get { return (ContentEditingMode)GetValue(EditingModeProperty); }
set { SetValue(EditingModeProperty, value); }
}
}
// The enumeration used for the EditingMode property
public enum ContentEditingMode
{
Html,
WYSYWIG
}
My intention is that the DataContext of the parent view instance will be assigned an instance of ParentViewModel and it will, in turn, assign the value of its SelectedChild property to the DataContext of the nested ChildView. All of this seems to work correctly, but the problem arises because the binding between ParentViewModel.EditingMode and ChildView.EditingMode does not work.
In an attempt to test whether there is a problem with my binding expression, I introduced a TextBlock adjacent to the ChildView and bound it similarly to the ParentViewModel.EditingMode property:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" />
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
In this test, the TextBlock is correctly updated every time the source property changes. However, if I set a breakpoint on the setter of ChildView.EditingMode, it never gets hit.
I'm baffled !
The simplest way to fix this is in your view model. Implement an EditingMode property in the child view model and bind to it. That way, you don't have to make any kind of guesses about what the right way to establish the binding might be; also, it's something that you can test outside of the UI.
Edit
Actually the right solution is not quite as simple, but it's worth knowing how to do.
What you want is for EditingMode in the child control to efficiently inherit its value from the parent control. Does that sound like something that anything else in WPF does? Like just about every framework element that implements dependency properties?
Implement EditingMode as a dependency property in both the parent and child UserControls and use property value inheritance, as described here. That takes the inheritance behavior out of the view model entirely and puts it where it belongs.
See if you can just use a full path to get the editing mode of the selected child:
<my:childView
DataContext="{Binding SelectedChild}"
EditingMode="{Binding SelectedChild.EditingMode />

Resources