I want to store a temporary version of my Company model within WPF MVVM however I am having issues where even though the temp I create isn't bound to my UI elements, it is still being updated.
Here is what happens when the ModifyCompanyViewModel is instantiated:
public ModifyCompanyViewModel(Company passedCompany)
{
SelectedCompany = passedCompany;
_tempCompany = passedCompany;
CloseWindowCommand = new CloseableCommand<Window>(CloseWindow);
}
So I have a readonly Company named _tempCompany. The UI elements are bound like so:
<TextBox Grid.Row="1" Grid.Column="1" x:Name="NameTextBox" Text="{Binding SelectedCompany.Name, Mode=TwoWay}"/>
Clearly they're bound to the SelectedCompany. If I then type something different in the TextBox that contains the Company's Name, but return the _tempCompany the _tempCompany's name reflects that which I have typed.
How can I store the _tempCompany in a way that no matter what is typed it's name stays the same? I have started using this approach which works:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
But this seems very cumbersome and that I am overlooking an easier way.
The SelectedCompany property and the _tempCompany field reference the same Company object. If you want to store "a temporary version" of the Company object you need to create a temporary version, i.e. you need to create another instance of the Company class like you are currently doing:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
This is not cumbersome.
As suggested in the comments you could implement the ICloneable interface but this just moves the creation of the other instance to a method within the class that implements the interface. You still need to create another instance somewhere.
How can I implement ICloneable when the model is generated by EF?
Create a partial class and implement the Clone method in this one.
You have to create a viewmodel. Currently Company is a model. Attempting to use it as viewmodel (to bind to its properties) sooner or later will cause you problem, since you mention it's generated.
Consider a simple viewmodel wrapping Company:
public class CompanyViewModel: INotifyPropertyChanged
{
readonly Company _company;
public CompanyViewModel(Company company)
{
_company = company;
}
// now expose something
public string Address
{
get { return _company.Address }
set
{
// tracking changes
// note: you aren't tracking changes made to model instance!
_company.Address = value;
OnPropertyChanged();
}
}
// and here is what you actually want, read-only name
public string Name => _company.Name;
// you can optinally expose model and bind to it properties
// but that wouldn't let you track the changes
// unless model implements INotifyPropertyChanged
public Company Company => _company;
...
}
If you want to edit company name, then just make another property (call it NewName), set its initial value in constructor and decide for yourself when its value will replace _company.Name (e.g. in some method AcceptChanges() which will be called when user finish editing). You will be able to access both: NewName and not yet changed _company.Name to compare them and display confirmation button.
Related
What's the best way to design a class (or classes) that can hold the potential values of item, as well as the one the user actually selected? I've come across this problem before and always feel like I'm missing a core class design feature.
Right now I usually do something like the following
class MultiChoice
Name (I.e. Box Size)
Default Value ("22x15")
PotentialValues ({"10x10","20x20","22x15"})
But that doesn't handle the actual value the user selected, so I add that in.
class MultiChoice
Name (I.e. Box Size)
Default Value ("22x15")
PotentialValues ({"10x10","20x20","22x15"})
SelectedValue
That doesn't feel right though, because when I construct a drop-down I'm filling in stuff with SelectedValue = null. Then when I store the data, I'm storing all the options too, which I don't need.
Is there a better way to handle this with an interface or other language construct? I always feel like I'm missing something blatantly obvious here.
You really have two separate entities here:
MultiChoiceQuestion
MultiChoiceAnswer
Create two separate classes to represent these two separate concepts.
ASP.NET MVC has the SelectList class. While you might not actually be working in ASP.NET MVC, it seems clear that Microsoft felt that the concept of "backing class for a dropdown" was universal enough to warrant its own class.
In whatever you consider the "Model" (that part of your program containing the business domain classes and business logic), there will always exist database tables that serve as lookups for these dropdowns.
tblCountries
CountryID PK
CountryCode string
FullName string
In your ViewModel, there will be a corresponding list of countries from which you can populate the dropdown:
public class InvoiceViewModel
{
...
public int CountryID { get; set; }
public SelectList Countries { get; set; }
// or
public List<Country> Countries { get; set; }
...
}
Of course, by the time you get to the UI, the actual dropdown contains enough plumbing to hold both the select list and the selected value.
You really only need a single Value field. Set it to whatever you want in the constructor (so it's defaulted when the object is created). You can also change your 'potential values' to be static, so it's the same for the entire class.
public class Box
{
public string Value { get; set; }
public static List<string> AllowedValues { get; private set; }
public Box()
{
AllowedValues.AddRange(new string[]{"10x10","20x20","22x15"});
Value = AllowedValues.First();
}
}
Then when a user changes the value, simply update it.
Box thisBox = new Box();
string val = "22x15";
if (Box.AllowedValues.Contains(val))
thisBox.Value = val;
I am working on a Windows Phone app that uses MVVM, but am struggling with the implementation of MVVM for properties that need to be formatted from the model class to show in the view.
Let's say that I have a simple model class called Person.
public class Person {
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
There is a list of Person objects that are loaded from a locally saved file, and I want to show a list of persons on a list page and then let the user tap on a person to navigate to a details page where there are more details about this person.
On the list page, I want to show the person's birthday as "Birthday: 2/22/1980" (where "2/22/1980" is the person's formatted Birthday)
On the details page, I want to show the person's birthday in a different format: "Eric's birthday is 2/22/1980" (where "Eric" is the person's Name and "2/22/1980" is the person's formatted Birthday).
Normally, I would just create a view model that formats the Birthday properly:
public class PersonViewModel {
private Person person;
public PersonViewModel(Person person) {
this.person = person;
}
public string BirthdayForList {
get {
return "Birthday: " + person.Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public string BirthdayForDetails {
get {
return person.Name + "'s birthday is " + person.Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
}
In order to show these values in the UI, I would create a collection of these view model objects (and bind them to the view):
ObservableCollection<PersonViewModel> Items
Now, what do I do when a person's birthday is updated (somewhere on the details page) and make sure that Items has been updated with up-to-date BirthdayForList and BirthdayForDetails properties, while at the same time saving the Person locally?
I want to keep everything simple and not have to update both the saved list of Person objects and list of PersonViewModel objects manually each time a value needs to be updated.
What is the best way to do this? Should I be using an ObservableCollection of PersonViewModel objects? Also, I have read in several places on this website that the model class should not implement NotifyPropertyChanged.
(Note: I have simplified the problem for this question. You should assume that there are many other ways that I need to format the Birthday property throughout the application as well as other properties from the model class that need to be formatted differently on different pages.)
Why don't simply do the whole thing in xaml and don't use the "calculated properties"?
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}'s birthday is {1:ddd}">
<Binding Path="Person.Name">
<Binding Path="Person.BirthDay">
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Then all you need to do is implement INotifyPropertyChanged in the Person class and raise the event in the setter.
EDIT: I would also recommend using a framework like MVVM light so you can use the ViewModel and ObservableObject base classes for your objects and simply be able to use their implementation of INotifyPropertyChanged
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged(() => FirstName);
}
}
private string _firstName;
Converters and XAML formatting are good solutions when they work, but sometimes you reall just need to do it in the ViewModel. Typically, you'd need to implement INotifyPropertyChanged and raise the PropertyChanged event for the calculated property when any of its dependencies change.
Managing these dependencies is a royal pain in the ... In fact I got so fed up with this very problem that I an MVVM framework called Catwalk that allows you to do these types of calculated properties in your ViewModel. If you use the framework, you can have code like
public string BirthdayForDetails
{
get
{
return Calculated(() => this.Name + "'s birthday is " + this.Birthday.ToString("ddd", CultureInfo.CurrentCulture));
}
}
Where the base class for model will automatically raise a PropertyChanged event for BirthdayForDetails if either Name or Birthday change. You just have to inherit from ObservableModel and Birthday & Name have to be observable properties like
public string Name
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
If you decide to try it out, let me know what you think.
You have two options:
Just format the date in XAML. Here is an example.
For more complex conversions, you can use converters.
What you should NOT do is store the format in the view model. The format of the data is a view/presentation concern only. So, the benefit of the above approach is that you don't need to keep separate lists just because of formatting.
Putting all your PersonViewModels in an ObservableCollection only solves the issue that your UI needs to update when a new PersonViewModel is added / removed from the collection.
That however does not solve the problem, that one object inside your collection changes. So if the Birthdate of the first person in the list changes, the collection stays the same.
So what you need to achive is to notify the UI that one object inside this collection changed.
You can do so either by letting your ViewModel implement INotifyPropertyChanged or deriving it from DependencyObject (discussion on what's the better solution: INotifyPropertyChanged vs. DependencyProperty in ViewModel).
I'd recommend using INotifyPropertyChanged. Implementing that interface will give you an PropertyChanged event. You need to raise that event everytime one of your properties change. Unfortunately this also requires you to create additional properties in the ViewModel so that you get notified when the changes happen.
The simplest (definitly not the best) way would be to just call OnPropertyChanged for every property that is dependent.
public class PersonViewModel : INotifyPropertyChanged
{
private Person person;
public PersonViewModel(Person person)
{
this.person = person;
}
public DateTime Birthday
{
get { return person.Birthday; }
set
{
person.Birthday = value;
OnPropertyChanged("Birthday");
OnPropertyChanged("BirthdayForList");
OnPropertyChanged("BirthdayForDetails");
}
}
public string Name
{
get { return person.Name; }
set
{
person.Name = value;
OnPropertyChanged("Name");
OnPropertyChanged("BirthdayForDetails");
}
}
public string BirthdayForList
{
get
{
return "Birthday: " + Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public string BirthdayForDetails
{
get
{
return Name + "'s birthday is " + Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I already mentioned that this solution is not the best. You are very likely to add another dependent property and you would have to remember adding the OnPropertyChanged to the Property you depend on.
So you could either use converters for your ViewModel (that now notifies on property change) and remove your calculated properties, or you stick to your properties and find an easier way to mark dependent properties.
Thankfully somebody allready solved the issue of dependent / calculated properties.
When googeling "INotifyPropertyChanged dependent properties" a lot of results show up. One that I really like is this one (Handling INotifyPropertyChanged for dependant properties) because it uses clean and readable attributes to mark the dependencies.
Also there are several MVVM Frameworks out there, that include solutions for said problem.
I hope one of the suggested solutions does help you fix your problem.
You can simply call the PropertyChanged method in the setter of your "person" property
like this
private Person myPerson;
public Person MyPerson
{
get { return myPerson; }
set
{
myPerson = value;
PropertyChanged("MyPerson");
PropertyChanged("BirthdayForList");
PropertyChanged("BirthdayForDetails");
}
}
You can use a combination of multiple elements embedded inside a TextBlock element
<TextBlock Foreground="DarkGray" VerticalAlignment="Bottom" Margin="0,0,0,8"><Run Text="total length "/><Run Text="{Binding TotalHours}" FontSize="48"/><Run Text="h "/><Run Text=":" FontSize="48"/><Run Text="{Binding TotalMinutes}" FontSize="48"/><Run Text="m "/></TextBlock>
Somewhat like this sample https://stackoverflow.com/a/8130843/3214300
I have a class with similar structure as given below:-
public class Sample
{
public List<string> Names { get; private set; }
public List<int> IDs { get; private set; }
// Some logic to populate these collections.
}
Now in another XAML file, I need to bind Names property to a ComboBox and based on the selection I need to get the corresponding ID as selected value. Is there way I can solve this problem using binding?
I have an object of Sample class in my model like below:-
public class Model
{
public Sample Object
{
get { return _sample; }
set { _sample = value; }
}
}
I'm not allowed to change the Sample entity class. Please guide me on how to solve this problem.
You need to merge the two different lists into a single list of an object with two values before binding, fortunately doing so is rather straightforward, just use Zip:
var data = sample.Names.Zip(sample.IDs, (name, id)=> new{name, id});
Then bind to data as you normally would.
Your best bet is to wrap the Name and ID into a separate class, then bind the ItemsSource to the collection of Name/ID pairs. Set the DisplayMemberPath on the ComboBox to "Name".
On the View Model, you can have a property for the selected Name/ID pair, or just the selected ID. If you want to do the latter, just set SelectedValuePath to "ID" and bind SelectedValue to the ID property on your view model (note that if you do it this way, you can use the anonymous class projection from Servy's answer). Otherwise, just bind SelectedItem to your selected Name/ID pair property (this version requires a named class).
I am trying to come up with a good way of implementing the MVVM pattern using Entity-Framework where my entities are my models. My DataContext is my viewmodel. This is a small reproduction of the problem.
View
<TextBox Text="{Binding MyText}" />
ViewModel:
I have the requirement of needing to navigate record by record from my DB. When a button is clicked in the View a command is sent to the Viewmodel that executes nextRecord(). EF does its magic and _myObject is the next row/record from the database
public class myViewModel: INotifyPropertyChanged
{
private MyEntityObject _myObject;
public string MyText
{
get { return _myObject.MyText; }
set
{
if (_myObject.MyText != value)
{
_myObject.MyText = value;
OnPropertyChanged("MyText");
}
}
}
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord() //pseudocode
}
}
Autogenerated Entity Model
public partial class MyEntityObject
{
public string MyText { get; set; }
}
Since the View has no knowledge of _myObject changing, it doesn't update when _myObject changes. A few approaches I have thought of.
I haven't tested wrapping my entities in a INotifyPropertyChanged wrapper class but am wary to do this as I have a lot of entity objects.
I could call OnPropertyChanged("...") for all properties, but some of my entities have a lot of properties to them, which would be ugly. Possible to use reflection to make it cleaner, but I may have properties that aren't databound.
I might be able to defer this to the UI, somehow refreshing the bindings when I click "Next Record", but this breaks MVVM and looks dirty
How can I get the UI to recognize changes from _myObject?
As I've mentioned in the comments, calling OnPropertyChanged("") or OnPropertyChanged(null) invalidates all properties and is equivalent to calling OnPropertyChanged for each and every property. This behavior is also documented here:
The PropertyChanged event can indicate all properties on the object
have changed by using either null or String.Empty as the property name
in the PropertyChangedEventArgs.
This means that you can simply add a call to OnPropertyChanged("") when you update your object to force WPF to reevaluate all bindings to your view model:
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord();
OnPropertyChanged("");
}
That being said, I'd still go with #Anand's solution (+1). There's an ongoing debate on whether it's OK or not for the viewmodel to expose the model as a property, and I tend to go with exposing it until you need to introduce some view model specific logic. Most of the time you won't have to and it's not worth the trouble of wrapping model properties.
The problem with your code is that when _myObject changes the MyText property changed event is not fired. A work around would be to create a new property to hold you entity
and make this property as your Grids DataContext in your view as shown below. Now when this line is executed MyObject = myEntitiesObject.NextRecord() your view will be notified about the change.
public class myViewModel : INotifyPropertyChanged
{
private MyEntityObject _myObject;
public MyEntityObject MyObject
{
get { return _myObject; }
set {
if (_myObject != value)
{
_myObject = value;
OnPropertyChanged("MyObject");
}
}
}
private void _nextRecord()
{
MyObject = myEntitiesObject.NextRecord() //pseudocode
}
}
View:
<Grid DataContext="{Binding MyObject}">
<TextBlock Text="{Binding MyText}"/>
</Grid>
An extremely simple but not very elegant solution that I believe would meet needs: upon switching records, set the DataContext to null, then back to the ViewModel.
However, there are arguably more elegant alternatives that require more work to meet all requirements. See Anand's answer for an improvement upon this.
The tag in View should have the mode and UpdateSourceTrigger attribute set with values.
I have read several articles, tutorials and blog posts about the MVVM pattern. However there is one thing I don't understand. Taking the three "layers":
Model
View
View Model
As far as I have understood MVVM the model contains the "raw" data, e.g. a name and address in case of a Student class. The view model exposes properties to the view which represent data of the model.
Example for a property in the view model
public string Name {
get { return model.Name; }
set { model.Name = value; }
}
Example for the model
private string name;
public string Name {
get { return name; }
set { name = value; }
}
This might sound a bit stupid but doesn't this create a redundancy? Why do I have to keep the name in the model and in the view model? Why should one not handle the name on the view model completely?
In such a simple example, this answer would be yes (it is unreasonably redundant). But, presumably, a page will contain more than just a single Model object. You may have the page state as well as multiple other Model objects which must all be tracked. This is done in the ViewModel.
For example, you may have additional information about the logged in user displayed in a status bar, as well as a service running to detect changes to a text file.
You may also have a form for editing the Student object. If you intend to validate those changes, then you wouldn't want to directly edit the Student object until after the modifications have been verified. The ViewModel can act as a temporary storage location in such a case.
Note on the above: It is not uncommon for validation to occur in the Model, but even then you will probably want the user to be able to enter invalid values while in the process of editing a form. For example, if your Model does not allow a zero-length value in a field, you still want to enable your user to delete the value, move to another field (say, for example, to copy it) then return to the field and finish editing (paste). If you are tied directly to the Model, then your validation logic may not handle this "in-between", "not-yet-finished" state as you'd like. For example, you might not want to accost your user with validation errors until they've finished and clicked 'Save'.
You will also probably have Command objects in the ViewModel to handle button clicks and the like. These would be domain-specific objects that would be useless in a Model.
ViewModels are also useful when you need to filter or somehow temporarily "modify" Model objects to get something useful on the screen. For example, you may want to display a list of all the Users in a system along with a real-time list of the top ten performers among them (updated every 10 seconds). Or you may want to show a list of Reports and a graph showing the overall usage rate, etc. Filtering, sorting and customizing that data would take place within the ViewModel.
The Model, on the other hand, is typically as pure as possible. Ideally, you want only POCOs that (usually) model exactly what's in your persistent storage (database, or what have you). If your persistent storage has FirstName and LastName fields, then so would your Model. Only in your ViewModel would you combine them to get a Name field (either "First Last" or "Last, First" depending on the View's needs).
For example:
namespace Model
{
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Class
{
public string Name { get; set; }
public float Score { get; set; }
}
}
namespace ViewModel
{
public class EditStudentRecordViewModel
{
private Model.Student _student;
private IEnumerable<Model.Class> _studentClasses;
/* Bind your View to these fields: */
public string FullName
{
return _student.LastName + ", " + _student.FirstName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public IEnumerable<Model.Class> PassingClasses
{
get
{
return _studentClasses.Where( c => c.Score >= 78 );
}
}
public IEnumerable<Model.Class> FailingClasses
{
get
{
return _studentClasses.Where( c => c.Score < 78 );
}
}
public void Save()
{
List<string> l_validationErrors = new List<string>();
if ( string.IsNullOrEmpty( this.FirstName ) )
l_validationErrors.Add( "First Name must not be empty." );
if ( string.IsNullOrEmpty( this.LastName ) )
l_validationErrors.Add( "Last Name must not be empty." );
if ( l_validationErrors.Any() )
return;
_student.FirstName = this.FirstName;
_student.LastName = this.LastName;
Model.Utilities.SaveStudent( _student );
}
}
}
The model is the object graph that contains your business logic.
That's where you hold the behaviour (validation, calculation and such).
The ViewModel is something that models the UI and its interactions.
These are different and have different reasons for existing - the point of the pattern is to separate your display logic to the VVM (View and ViewModel) and have your business logic completely separated.
The view model is where you would keep track of properties that are specific to the view and not necessary to the model.
Let's take your model, assume it's called Person.
And then you create a view model for Person called PersonViewModel, which looks like this:
public class PersonViewModel
{
public Person Person { get; set; }
}
(Note, you might not want to expose the model like this directly, but that's another story)
Now let's say that you have an button in the view which is used to save the Person instance. To provide a better user experience (UX), you want to enable the button only if your model has actually changed. So you implement the INotifyPropertyChanged interface on the Person class:
public class Person : INotifyPropertyChanged
{
...
Now, you could expose a HasUnsavedChanges property from your Person which the Enabled property on the save button would bind to, but that logic has nothing to do with the person.
This is where the view model comes in. You would define this view-specific property on the view model, like so:
public class PersonViewModel
{
public Person Person { get; set; }
public bool HasUnsavedChanges { get; set; }
}
Then, your view model would subscribe to the PropertyChanged event of the INotifyPropertyChanged interface, and toggle the HasUnsavedChanges property on the view model.
Then, if the binding is set up correctly, the save button would enable/disable when any change happens on your model, but your model doesn't have any logic tying it to the view.
Note that you'd have to also implement INotifyPropertyChanged on the view model as well for your view to pick up when changes are made to the view model it is bound to.
Again, the point is acting as a bridge to contain the logic that is a combination of model properties and view properties that don't belong on the model.
Model in MVVM is exactly the same as in MVP or Model2 MVC. It is the one part of MVC-inspired patterns that is not affected by variations on the theme.
Model is the layer which contains repositories, units of work, domain/model objects, data mappers, services and some other structures. All they combined create the model layer, which contains all of the domain business logic for the particular application.
Model is not any single instance. Anyone who tels you otherwise is full of it.
The specific usecases, for which MVVM has been designed, are situation, when you are unable to modify either the model layer or view instances, or both.
P.S. Though, if you are using ViewModel instances as per ASP.NET MVC documentation, then you actually are NOT using MVVM. It is just Model2 MVC with different names for things (where "viewmodels" are actually views and "views" are templates). They kinda messed up when they marketed Rails-like architecture as "MVC".
I've always viewed Models as the "Building Blocks" of the application. They are usually self-contained classes with some properties and perhaps some rudimentary validation or logic for its own properties only.
View Models on the other hand are my actual application classes that end up using the "building blocks" (Models) when building and running the application. They do things like perform advanced validation, process commands, handle events, any kind of business logic, etc.
It should be noted that you don't have to expose your Model's properties in your ViewModel like you have in your example code. Doing so is the "MVVM purist" approach as it completely separates your Model layer from the View layer, however it's also perfectly acceptable to expose the entire Model to the View instead. This is what I typically use in most small projects due to it's simplicity and lack of code-duplication.
public MyModel CurrentModel
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
RaisePropertyChanged("CurrentModel");
}
}
}
However if there are cases where only a few properties from the Model is needed in the View, or if the project is large enough where I'll want to keep the layers totally separate, then I expose my Model's properties to the View through the ViewModel like you have in your example code.