I had asked a question about mapping multiple view models to a single view (here). I got some good answers but I am still having some trouble applying what I learned there to my particular case.
A brief recap: I want to create a base ItemViewModelBase class that exposes properties that my view will bind to. Then I will create two specific view models, PeopleViewModel and CarsViewModel. Both of these inherit from ItemViewModelBase and implement the appropriate properties. Now, I want to be able to create a single view that will display the appropriate info based on which view model it is bound to. Since both the PeopleViewModel and CarsViewModel expose the same properties and I want the view to look the same for both of these, I only need one view.
One of the answers in my previous question suggested using a DataTemplate:
<DataTemplate DataType="{x:Type ItemViewModelBase}">
//some user control
</DataTemplate>
I am new to using DataTemplates with MVVM (and MVVM in general) so I have a few questions:
Right now ItemViewModelBase is an abstract class and I defined the appropriate properties (ItemName, Items, etc.). My Items property is an ObservableCollection:
public virtual ObservableCollection<???> Items { get; set; }
What would I put as the collection type? The classes that derive from this base class will have different lists (Person, Car). Is the base view model the right place to put the property? I do want all of the derived classes to implement it so it seems so. ANd it doesn't make sense to have Person and Car extend some base object.
Let's say I do not need any customization of my views. I would only need one View in that case. It is not clear how I would set this up. Should I create a DataTemplate for ItemViewModelBase and a single view (user control) to represent it? Right now I use Unity to register my view models and when the view is created, the view model gets injected in the view. How would I differentiate between the different view models when I try to create the view?
Basically, I don't know how to show the appropriate view when using DataTemplates. In my application right now I have a window that contains a tab control defined like this:
<Grid>
<TabControl TabStripPlacement="Left" ItemsSource="{Binding TabItems}"/>
</Grid>
The TabControl's style contains the below setters:
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Content" Value="{Binding Content}"/>
TabItems is defined like so:
public ObservableCollection<ConfigTabItem> TabItems { get; set; }
TabItems.Add(new ConfigTabItem() { Header = "People", ResolveView = (Func<object>)(() => (PeopleView)Container.Resolve(typeof(PeopleView), "peopleView")) });
TabItems.Add(new ConfigTabItem() { Header = "Cars", ResolveView = (Func<object>)(() => (CarsView)ConfigurationModule.Container.Resolve(typeof(CarsView), "carsView")) });
So as it stands right now, I have separate view models and views for People and Cars, and whenever a tab is clicked, the appropriate view is resolved.
I want to change this setup to use the above mentioned base view model class and single view with DataTemplates.
Any sample code/sample would be greatly appreciated, showing a base view model class, some other view model classes extending that base view model, and then being able to show the appropriate view based on the view model (where there is only one generic view).
You are asking too much in a single question IMHO. Try getting your code to work with or without DataTemplates (make it hacky if you need to) and then focus on ONE area of the code that you think needs refinement, and post a question about how to solve a specific problem. I started typing out an answer and it quickly got too complicated to articulate an overall recommendation.
I'm not sure you got good advice in the other question about DataTemplates. I'm also very concerned about how you have the ConfigTabItem set up--it seems to be a parent viewmodel that uses the container (directly, which is not a good practice) to resolve a view, which presumably also has its own viewmodel. This seems needlessly complicated.
Anyway, again, try to distill it down to a few focused questions. In the case that any of it is helpful, my original start at an answer is below (unedited):
First, I'm not sure I understand the answer you were given in your previous question that talks about DataTemplates. If you will always be binding to a ItemViewModelBase, I'm not clear on why you need to specify a DataType (or even a DataTemplate). This is not to say that using a DataTemplate is a bad idea, but I'm not sure I see the necessity of it in this case.
I'm also going to say that I'm not sure I see the necessity of inheritence either. Databinding works at runtime, and outside of switching a DataTemplate based on VM type (which, as I said, I don't think you need), the view doesn't care what it is binding to, so long as the properties it is looking for are found at runtime.
So as a general matter, I would start with ONE concrete implementation of your view model. Get it to bind correctly, and then determine which parts of it you can abstract into a base class or interface if that's the approach you want to take. It's not necessary, but might help make the requirements for binding easier to "enforce"--for example, using the base class or interface will restrict you (or someone else) from changing the name of a property needed for binding.
What would I put as the collection
type? The classes that derive from
this base class will have different
lists (Person, Car). Is the base view
model the right place to put the
property? I do want all of the derived
classes to implement it so it seems
so. ANd it doesn't make sense to have
Person and Car extend some base
object.
If you are going to use a base class or interface for your VM and you want the collection to be a part of it, it can simply be of type ObservableCollection<object>. The items added to it will need to all have property names that match what you reference in your XAML. So you can't have "PersonName" and "CarName" properties if you want only one view; you would need to use something more general like "ItemName" (unless you use DataTemplates with DataTypes--this is where that actually would be useful). Again, you don't need each collection item to inherit from a base type or implement a common interface, unless (again) you want enforcement at compile time.
Look at the Architecture from Microsoft App Studio for universal apps. According to Microsofts App Studio the DataTemplates should live in a DataTemplates Subdirectory under the Views Directory. A Universal app has this directory for both the Windows UI as for the Windows Phone UI so its not in the Shared project because they are not the Same. Don't use the Converge PRISM architecture. Its completely wrong designed! That was not written with a Windows and a Windows Phone architecture in mind but like they call it Converged. It should have been completely redesigned like it works in Microsofts App Studio. Don't look for Dependency Injection its not in it and not needed. Most use Dependency Injection for stub or fake interfaces. The DataContext for design data works now so good with json data that a Dependency Injection component would be overkill.
Related
I recently started learning about WPF, which led me to learn about MVVM and eventually MVVM Light, so still a starter in these three. I am building an application with a layout similar to the picture in the link -> Application layout
In order to maintain good code separation and avoid huge files i decided the best approach would be to create a main View, and in that create several smaller Views per "zone" of the UI. From what i read in several tutorials, it is advised to maintain 1 ViewModel per View. Therefore i have a Master View / ViewModel, and several View / ViewModels running simultaneously.
Finally i have a single Model that keeps track of the information I plan to display in the UI. The Model interacts with an external API that can modify the data in it. So besides data being modified by user request (ex: pressing buttons or timers), the data will also change with asynchronous events from the API. Which means I need two way communication between the Model and the ViewModels / Views.
The questions:
1. Do you agree with the "1 view per zone of the UI"? And the 1 ViewModel per View?
2. In the Main View-Code-Behind I instantiate all the ViewModels, and in each View I bind them like in the MVVM Light examples i saw:
<UserControl ... DataContext="{Binding Main, Source={StaticResource Locator}}">
<UserControl ... DataContext="{Binding SideBar, Source={StaticResource Locator}}">
<UserControl ... DataContext="{Binding TopBar, Source={StaticResource Locator}}">
Is this the correct way to instantiate and bind several ViewModels to the respective Views?
3. Each ViewModel is passed a reference to the Main ViewModel (except the Main itself) in its constructor, which is the only one with a reference to the Model. This is how i connect the several ViewModels to the Model. Is this conceptually correct?
4. Initially i was trying to avoid using MVVM Light or other frameworks if i could do all i wanted with the RaisePropertyChanged method. I might be doing something wrong, but for example, when the Model calls RaisePropertyChanged, i can catch that event in the Main ViewModel, however it doesn't propagate to the rest of the ViewModels, so i had to do it myself by calling RaisePropertyChanged a second time:
public MountainTopViewModel()
{
_model = new MachineStatusModel();
_model.PropertyChanged += ModelPropertyChanged;
}
void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TestVarModel")
{
// do something else if needed
RaisePropertyChanged("TestVar");
}
}
I'm guessing this is either not the correct way to do it, or there is a better one. So how can I inform all the Views and ViewModels when a property changes in the Model, without having to re-call the method in different places?
Sorry for the long story, i would appreciate some help.
This seems unwise to me:
The Model interacts with an external API that can modify the data in it. So besides data being modified by user request (ex: pressing buttons or timers), the data will also change with asynchronous events from the API. Which means I need two way communication between the Model and the ViewModels / Views.
I would have the async API events driving changes in the viewmodel, not the model. Then, in response to changes from the async API or from the View, the viewmodel does its usual thing: Updates the model and raises events which the view responds to. The viewmodel already plays that role, so use what you've got. Your scheme adds complexity that you don't need, and it's more complexity than you may realize. Item # 4 in your question is just the tip of the iceberg of that added complexity; believe me, it'll only get uglier from there. Don't do that to yourself. You're young. You've got everything to live for.
It's not unusual for a viewmodel to handle the PropertyChanged event of another viewmodel, or for a viewmodel to expose specific custom events which fire when specific properties change value. It might have a NameChanged event or whatever. But I don't see any particular need for that in your question, as I understand it.
1. Yes.
2. If MVVMLight does things the way you've got it there, do that. Plunkett's Razor: Whenever possible, conform to the practices of the framework you're using. Consistency is golden in programming for many reasons, and if you follow the "rules", you'll usually find the framework will be there waiting to help you instead of fighting you every step of the way. This isn't a law of nature, but it's an amazingly reliable rule of thumb.
3. That's conceptually a little shaky. Ideally, we try to write viewmodels that aren't dependent on "parent" viewmodels. Your Library viewmodel has to know what a Book viewmodel is because it contains a collection of them, but a Book viewmodel can often be more useful if it doesn't depend on a Library. Imagine writing a Wheel that's got all kinds of dependencies on Car, and then having to implement Bicycle. Do you give Bicycle an Alternator, or do you create a dummy Car? Times like that you start to daydream about moving to a commune in Vermont and making useful objects out of wood.
I would probably have a model type for each viewmodel, and give all the viewmodels a reference to their own model in their constructors. The models would probably have the same parent-child relationship as the viewmodels. If your model is Entity Framework or something instead of "POCO" classes, then yeah, one model for everybody or whatever. But still, just hand it to them directly.
I’m new to MVVM and am trying to establish good practices as I convert a large non-Model-View WinForms project. Here’s an example of a solution I’ve implemented. I’m wondering if there is a better pattern for solving this class of problem.
MyModel has ten properties. MyView exposes two of them for users to update. MyViewModel handles the usual stuff in between.
Other models depend on MyModel’s properties, so I only want to change MyModel when values are committed to. MyView has OK and Cancel buttons, so instead of having MyViewModel directly update MyModel when the user interacts with MyView, I’ve created another layer: MyTempModel. MyTempModel contains two properties which correspond to the two from MyModel.
So inside MyModel, prompting code looks something like this:
var tempModel = new TempModel{Prop1=Prop1,Prop2=Prop2};
bool? response = new MyView().ShowDialog();
if (response.HasValue && response.Value)
{
Prop1 = tempModel.Prop1
Prop2 = tempModel.Prop2
}
Thus if the user clicks ‘Cancel’, MyModel’s properties are not changed.
Note: Not shown here is that I set a reference to MyTempModel in MyViewModel once to establish that wiring. MyViewModel subscribes to property changed events in MyTempModel and MyView uses databinding to connect to MyViewModel.
model - > view data flow summary:
MyModel sets property in MyTempModel, which fires an event. MyViewModel’s evenhandler picks up the change and sets a dependency property, causing MyView to update.
view -> model data flow summary:
Changes to MyView result in dependency property in MyViewModel to change. This property’s setter pushes the value to MyTempModel. When user clicks Ok then MyModel copies values from MyTempModel.
I'm particularly interested in the role of the ViewModel. I have in mind that keeping dialogs "humble" is a good thing, and maybe that is spilling over into making ViewModels humble. So comments on what kind of functionality you put into your view models vs your domain models would be especially interesting.
I appreciate any and all design wisdom for this pattern. I'll gladly update this info if anyone needs clarification.
I don't think the use of a proxy class is a bad thing. I'm a little concerned about this: 'MyModel sets property in MyTempModel, which fires an event. MyViewModel’s evenhandler picks up the change and sets a dependency property, causing MyView to update.' I think that renders your Model classes a little less universal and obscures that functionality from the ViewModel. I would just use the ViewModel to mediate the swap in/out of the proxy, rather than having the Models do it. I also think that using a proxy opens up use of DataTemplates for that type which could make the UI work easier.
An alternative to the direction given by Alex is to make the viewmodel a representation of what is in both client and server side.
E.g., the number shown is always what the user entered and the background of the number indicates that this number has been confirmed or not (pending)by the server. The viewmodel could use two different properties to contain these values and a status property to indicate the fact the property has been synchronized.
This way the viewmodel captures all the user needs to know. All the view needs to do is bind to the correct properties for display and editing.
I'm currently in the need of setting the SelectedIndex property of my TabControl when a certain event (IEventAggregator) takes place and thought about how I'd implement that.
I came up with 2 possibilities:
Use GetView() provided by ViewAware in order to access my TabControl and set the SelectedIndex to my value
Use a property in my associated ViewModel and bind this property to my TabControl's SelectedIndex property via XAML
Both options are working fine but I personally want to get this question answered since this is not the first time I'm wondering where to implement the functionality in such cases.
I know that the first option won't enable the Notify support but besides that: What would be the proper way?
Having a GetView() method to manipulate the view directly from the viewmodel completely breaks MVVM. You might as well just put all your logic in codebehind. The whole point of MVVM is to abstract away the actual view so that it is decoupled from the logic, and the app can be unit tested.
What if you change your mind about the tabs in the future and decide to show your multiple views some other way? You've now got to start editing your viewmodel to edit the new view instead of just tweaking some XAML.
And for unit testing you're going to have no way to mock out your TabControl.
I'm trying to make something like a quiz application where 3 questions will be brought up on screen at a time, allowing the user the check a radio button containing "Yes" or "No", and have an answer come up appropriately to his response. The questions will continually come from a database I'm using.
After a few attempts I've figured that using x:Name property is not a great solution, since it doesn't allow me to use a loop in order to change the questions and answers. Is there any other way to make a grid with the same types of objects in each cell, being able to access each object inside each cell of the grid in the code-behind?
Here is list of steps you need to implement,
Need to create QuestionModel, contains question properties, make sure your model inherits INotifyPropertyChanged.
Need to create ViewModel, which contains data objects, public/dependency properties
Need to bind/set data objects/properties on viewmodel constructor
Need to set your ViewModel as a DataContext of your View(.xaml) (You can create this on zammel directly and codebehind as well
Need to bind your UI objects like Question/answers/yes-no with viewmodel properties accordingly
WPF/Silverlight has their own fundamentals like Data Binding, Resources, Compiler, Dependency Properties. Above steps comprises MVVM design pattern. During each steps, please google for specific stuff.
I have a View that contains a GridView object and a ViewModel that the View represents. Now, I'd like to override the controls in that ViewModel so that I keep the same GUI but with different business logic (i.e. contained within the ViewModel). What's the proper way of doing that?
I'd assume it is not as simple as extending the ViewModel since there's the issue with its GUI being represented in the View (which has a XAML component).
Any thoughts?
Thanks.
Edit: Fixed typo
What I tend to do, is create a Base ViewModel Class with the standard UI and Control logic in it.
I then set whichever Functions / Subs I want to control individually to overrideable. But I populate them up with default code, such as The standard Form State (Editting/Adding/Deleting) code. Any I want to force to be Overridden are set to Must Override of course.
Then, I create a Individual View-Based ViewModel, inherit from the Base ViewModel Class, override the methods I require, and set this Individual ViewModel to be the DataContext for the View.
You can switch in which ever ViewModels you like using this method, so long as they all inherit from your base ViewModel class.
I hope this helps!