Databinding Entity Framework navigation properties - handling change - c#

So I'm building my first larger application and I'm using WPF for Windows and stuff and Entity Framework for retrieving, updating and storing data.So far using a pattern similar to the MVVM pattern, I had a couple of issues but was able to resolve them and am quite far into design.
Also, I'm using database first approach.
But I have just ran into a brick wall that I should have anticipated. It has to do with nested properties in entities and the way changes to them are handled. Let's explain.
For the purpose of simplicity I will not be using my actual class names.
So let's say I have three entities in my EF Model: Department, Manager and PersonalInfo.
I modified my *.tt Template file so that all my entities also implement INotifyPropertyChanged interface, but only for their NON NAVIGATION properties since Navigation properties are declared as virtual and WILL be overridden by EF when their date gets set.
So let's say my generated classes look like this:
public partial class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Department() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
int _someproperty;
public int SomeProperty { get { return _someproperty; } set { _someproperty= value; OnPropChange("SomeProperty"); } }
int _managerid;
public int ManagerID { get { return _managerid; } set { _managerid = value; OnPropChange("ManagerID"); } }
public virtual Manager Manager { get; set; }
}
public partial class Manager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Manager() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
public virtual PersonalInfo PersonalInfo { get; set; }
}
public partial class PersonalInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public PersonalInfo() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
string _firstname;
public string FirstName { get { return _firstname; } set { _firstname = value; OnPropChange("FirstName"); } }
string _lastname;
public string LastName { get { return _lastname; } set { _lastname = value; OnPropChange("LastName"); } }
}
Now this works pretty well if I want to let's say display a list of Departments with their Managers. First I load the data into the EF Context like so
Context.Departments.Include(d => d.Manager.PersonalInfo).Load();
Departments = Context.Deparments.Local;
And than in the XAML I can do:
<DataGrid ItemsSource="{Binding Departments}" SelectedItem="{Binding CurrentDepartment, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}"/>SomeProperty
<DataGridTextColumn Binding="{Binding SomeProperty }" Header="Property"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.FirstName}" Header="FirstName"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.LastName}" Header="LastNameName"/>
</DataGrid.Columns>
</DataGrid>
And all of this works wonderfully. I can add and remove items with no problems by simply removing them from Context and saving changes. Since entity sets are ObservableCollections any additions or removal from them automatically raises appropriate events which update the datagrid. I can also modify any nonnavigation property of the Department and can refresh the data in CurrentDepartment like so:
Context.Entry(CurrentDepartment).Refresh();
and it automatically refreshes the data in the datagrid.
Problems start when I change one of the navigation properties. Let's say that I opened a window in which I edited the Department, where I changed the current manager from Bob Bobington to Dave Daveston. When I return to this window calling:
Context.Entry(CurrentDepartment).Refresh();
It will only refresh non navigation properties, and First and Lastname columns will still say Bob Bobington. But that is Refresh function working as intended. But if I load the correct data into the context like this:
Context.Entry(CurrentDepartment).Reference(d=>d.Manager);
Context.Entry(CurrentDepartment.Manager).Reference(m=>m.PersonalInfo);
is still won't change the contents of the first and last name columns since they are still bound to the OLD manager. They will only refresh if the change happens on Bob Bobington instance of PersonalInfo.
I can sort of solve this level of problem by binding the column directly to Manager property, and converting Manager to text either via a ValueConverter or by overriding ToString for Manager. But that won't help since WPF won't ever be notified that Manager property has changed since changes to that property don't raise PropertyChanged event.
Navigation properties can not raise that event since even if I edited the tt template so it generates the code for the navigation property like so:
Manager _manager;
public virtual Manager Manager { get{return _manager;}
set{
_manager=value;
OnPropChange("Manager");
}
}
all this code will likely be overridden by the EF framework itself.
Sooo, what is the best thing to do in these cases? Please don't tell me that conventional wisdom is to copy the data from EF Poco classes into your own and use them. :(
UPDATE:
Here goes a potentially stupid solution for this problem. But it works.
ObservableCollection<Department> tempd = Departments;
Department temp = CurrentDepartment;
Departments = null;
CurrentDepartment = null;
Context.Entry(temp).Refresh();
Context.Entry(temp).Reference(d=>d.Manager).Load();
Context.Entry(temp.Manager).Reference(m=>m.PersonalInfo).Load();
Departments = tempd;
CurrentDepartment = temp;
As you can clearly see the key is in forcing the DataGrid to rebind itself from scratch. This way it will use no shortcuts and will rebind itself properly. BUT this method is quite silly. I shiver at the thought of having to do this to datagrids with hundreds of rows.
So I'm still waiting for a proper solution, but I'll be continuing onwards using this. Something is better than nothing.

Well, conventional wisdom is to copy the data across to another POCO, or at least make your ViewModel class peek through to an underlying Model class. You have combined your Model and ViewModel classes such that Model-based constraints (virtual methods required by your ORM) are interfering with your ViewModel-based constraints (to allow databinding, you must be able to raise events from property setters).
If your Model and ViewModel were properly separated (Separation of Concerns) then you could have your virtual methods and database-required fields on your Model (a DB persistable object) and your purely View-based functions (PropertyChanged events) on your ViewModel. Your DB code should never care about your PropertyChanged events anyway.
You can make it easier by making the ViewModel a look-through class so every property getter-setter looks like:
public string PropertyThing
{
get { return _myModel.PropertyThing; }
set { _myModel.PropertyThing = value; PropChanged("PropertyThing"); }
}
If you're already doing code generation this shouldn't be a major chore.
Alternatively, you could duplicate all the values with something like AutoMapper to separate out your Model and ViewModel to separate classes.
It's not what you wanted to hear, but your ORM and your UI are clashing and that's the sort of thing that MVVM architecture (specifically separating the Model and ViewModel) are supposed to make better.

Related

How to make the transition from "traditional" to reactive MVVM

I want to make a transition to a reactive view model / model.
I've used 3 scenarios so far:
"ValueA": The model value is only accessed from one view model at a time and the value is only changed through the view model
=> simple property in model, forwarding property with PropertyChanged in view model
"ValueB": The model value is accessed from several view models and/or changes from other sources
=> property with event in model, forwarding property and translation from changed event to PropertyChanged in view model
"ValueC": A value only used in the view model
=> no property in model, property backed by own field with PropertyChanged in view model
This is my "current" approach:
class Model
{
public string ValueA {get;set;}
private string valueB;
public event ValueBChangedEvent ValueBChanged;
public string ValueB
{
get
{
return valueB;
}
set
{
valueB = value;
ValueBChanged();
}
}
}
class ViewModel : INotifyPropertyChanged
{
private Model model;
public string ValueA
{
get
{
return model.ValueA;
}
set
{
model.ValueA = value;
OnPropertyChanged();
}
}
ViewModel()
{
model.ValueBChanged += model_ValueBChanged;
}
private void model_ValueBChanged()
{
OnPropertyChanged("ValueB");
}
public string ValueB
{
get
{
return model.ValueB;
}
set
{
model.ValueB = value;
// no change notification since done via model
}
}
private string valueC;
public string ValueC
{
get
{
return valueC;
}
set
{
valueC = value;
OnPropertyChanged();
}
}
}
This is how I intend to model them using reactive extensions:
class ReactiveModel
{
public string ValueA {get;set;}
private ISubject<string> valueB = new Subject<string>();
public ISubject<string> ValueB
{
get
{
return valueB;
}
}
}
class ReactiveViewModel : INotifyPropertyChanged
{
private ReactiveModel model;
public string ValueA
{
get
{
return ???;
}
set
{
???
}
}
private ReactiveProperty<string> valueB = model.valueB.ToReactiveProperty();
public Reactive<string> ValueB
{
get
{
return valueB;
}
// no setter since access via ValueB.Value which is read-write
}
private ISubject<string> _valueC = new Subject<string>();
private ReactiveProperty<string> valueC = _valueC.ToReactiveProperty();
public ReactiveProperty<string> ValueC
{
get
{
return valueC;
}
// no setter since access via ValueC.Value which is read-write
}
}
Summary:
"ValueA": I have no clue for this case
"ValueB": works at first glance but does neither propagate changes from view model to model nor the other way.
"ValueC": this works as intended
I'd be happy if I had a solution for ValueA and ValueB.
ValueB: View model is responsible for updating model. ReactivePropertyuses only IObservable interface from your model properties and reads values from ValueB(does not write anything).
ReactiveProperty is changed by view through Value property.
ReactiveProperty implements IObservable and you should subscribe to changes to get new values.
ValueA: We can make a ReactiveProperty on the view model side an subscribe to propagate the changed value to the model.
Here is the code for the solution:
class ReactiveModel
{
public string ValueA {get;set;}
private readonly Subject<string> valueB = new Subject<string>();
public IObservable<string> ValueB
{
get
{
return valueB;
}
}
public void UpdateB(string newValue)
{
valueB.OnNext(newValue);
}
}
class ReactiveViewModel
{
private readonly ReactiveModel model;
private readonly ReactiveProperty<string> valueA;
private readonly ReactiveProperty<string> valueB;
public ReactiveViewModel(ReactiveModel model)
{
this.model = model;
valueA = new ReactiveProperty<string>(model.ValueA);
valueA.Subscribe(x => model.ValueA = x);
valueB = model.ValueB.ToReactiveProperty();
valueB.Subscribe(model.UpdateB);
}
public IObservable<string> ValueA
{
get
{
return valueA;
}
}
public ReactiveProperty<string> ValueB
{
get
{
return valueB;
}
}
}
XAML would be in both cases:
<TextBox Text="{Binding ValueA.Value, UpdateSourceTrigger=PropertyChanged}"/>
This is a bit of a contentious topic but I personally don't see property change notification as being specific to the view model and view, I therefore use B but I add INPC to the models as well in my data layer. This can be done in a post-processing build step using Fody or by wrapping the models in a proxy using something like Castle Dynamic Proxy. I personally use the latter, although it requires integration with your ORM so as to not hammer performance i.e. you don't want your database code loading a model object and then thinking that object has changed because you've tried to update it use the proxy wrapper (this is especially true when you turn IList<> into an ObservableCollection).
Your current approach doesn't seem to make a lot of sense. You are implementing events to signal when the Model changes so the View Model can take action. However only the View Model should change the Model, therefore events are completely unnecessary.
The View Model is responsible for making changes to the Model, therefore it should know when a change has been performed, as it is the source of said change.
A pure MVVM approach would be something like this:
public class MyModel
{
public string MyValue { get; set; }
...
}
public class MyViewModel
{
private MyModel _Model;
public string MyModelValue
{
get { return _Model.MyValue; }
set
{
_Model.MyValue = value;
//Notify property changed.
}
}
...
}
It is not the responsibility of the Model to notify the View of changes, instead it is the responsibility of the ViewModel to signal these changes. The Model should not be exposed to the View, but instead the properties of the Model that the View requires should be exposed.
Think of it this way.
The user changes the MyModelValue property in a TextBox on the View.
The View notifies the ViewModel of the change.
The ViewModel changes the Model.
The only purpose of INotifyPropertyChanged is when the above process is reversed, where the ViewModel needs to tell the View that a property has changed:
A method in the ViewModel is called that updates MyModelValue.
The ViewModel notifies the View of the change.
The View updates the TextBox.
The pattern of exposing only properties of the Model that the view requires is not always followed, instead you may see the entire Model being exposed to the View, but as I have said many times before, MVVM is a pattern, not the law. Implementing INotifyPropertyChanged in the Model is perfectly acceptable.

The best way how to update a complex ViewModel on demand

I have TaskViewModel class with a lot of different properties. The simplified piece of code is below:
internal class TaskViewModel : ViewModelBase
{
private TaskModel _model;
public long Id
{
get { return _model.Id; }
set
{
_model.Id = value;
RaisePropertyChanged("Id");
}
}
public string Title
{
get { return _model.Title; }
set
{
_model.Title = value;
RaisePropertyChanged("Title");
}
}
public DateTime? Date
{
get { return _model.Date; }
set
{
_model.Date = value;
RaisePropertyChanged("Date";);
}
}
private RelayCommand _updateCommand;
public RelayCommand UpdateCommand
{
get
{
return _updateCommand
?? (_updateCommand = new RelayCommand(
() =>
{
// somehow update _model
}));
}
}
}
And I have TaskView where I could edit the instance of TaskViewModel. Also I have a few validation rules, for example, if Titleis empty I can't update model and have to reestablish previous Title. That's why I cannot use "{Binding Mode=TwoWay}.
The question is what is the best way to update view model.
I have two ways to do it:
Add property of TaskViewModel type to the instance and bind all properties of this to the view and than using ICommand for updating properties in main instance if all validations rules are performing. But in this case I need to keep whole copy of object.
Using "{Binding Mode=TwoWay, UpdateSourceTrigger=Explicit}" for necessary properties and than in code-behind using event handlers call binding.UpdateSource(). But in that case I have to implement validation logic in code-behind too, which looks like a bad way in mvvm-approach.
May be you should recommend the best way for this task.
Thanks in advance.
UPDATE:
For example of the typical validation case, Title mustn't be empty. If I changed the Title property from "Buy milk" to "Buy mi" it would be valid, but I don't want to update my model after every change of every property and save it to a storage. So I have to implement SaveCommand which will update the model. But also I need to have a possibility to rollback all the changes, so I can't change current view model properties directly by using Mode=TwoWay binding.
So the problem is how to update all changed properties on demand if they are valid?

How to call one viewmodel's method from another viewmodel

I am developing Windows Universal app. I have one GridView which has one textblock and a button. The gridview gets data of un-purchased objects from a service. The button is for purchasing particular object. So if user clicks on button that object is purchased & gridview gets refreshed to remove purchased item from it.
I am illustrating my requirement in simplified manner. I tried two ways, both are not working. Can you please suggest me solution regarding it.
First way I used is to inherit Model class with ViewModel class so I can access methods of ViewModel class, but it throws StackOverflowException in ViewModelBase at SetProperty<T> method.
P.S. - I don't want to migrate to any framework like MVVMLight, etc.
ViewModel.cs
public class ViewModel : ViewModelBase
{
public ViewModel()
{
DataCollection = new ObservableCollection<Model>();
for (int i = 1; i < 10; i++)
{
DataCollection.Add(new Model { Number = i });
}
}
private ObservableCollection<Model> _DataCollection;
public ObservableCollection<Model> DataCollection
{
get { return _DataCollection; }
set { this.SetProperty(ref this._DataCollection, value); }
}
}
Model.cs
public class Model : ViewModel
{
public RelayCommand<int> DeleteCommand { get; set; }
public Model()
{
DeleteCommand = new RelayCommand<int>((x) => DeleteNumber(x));
}
private void DeleteNumber(int x)
{
var obj = DataCollection.Where(varNum => varNum.Number == x).FirstOrDefault();
if (obj != null)
{
DataCollection.Remove(obj);
}
}
private int _Number;
public int Number
{
get { return _Number; }
set { this.SetProperty(ref this._Number, value); }
}
}
2nd way I keep that isolated, so I was not able to access the methods.
ViewModel.cs is same as above
Model.cs
public class Model : ViewModelBase
{
public RelayCommand<int> DeleteCommand { get; set; }
public Model()
{
DeleteCommand = new RelayCommand<int>((x) => DeleteNumber(x));
}
private void DeleteNumber(int x)
{
// How to access ViewModel's DataCollection property or
// a method which sets un-purchased objects in DataCollection property
}
private int _Number;
public int Number
{
get { return _Number; }
set { this.SetProperty(ref this._Number, value); }
}
}
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Well, in the first example you're getting a StackOverflowException because your ViewModel instantiates 9 Models each time - and since your Model is an extension of ViewModel, each one of those instantiates 9 more Models and an infinite recursion happens. That doesn't answer your main question, though :)
Your class names are confusing to me, because in MVVM a "Model" is simply a representation of the data and methods to manipulate it, whereas the ViewModel requests this data from the Model and presents it via publicly accessible properties that are retrieved from the View via binding. The View knows about the ViewModel, the ViewModel knows about the Model and the Model just knows about the data. In any case you shouldn't be binding directly from the View to the Model!
You'll want to house the RelayCommand in your ViewModel so your View can bind to it, and depending on what you want to happen when a user purchases an item (store it in a database, track this in another variable, simply remove from the view without doing anything else, etc.) you may or may not need to write additional logic for when this occurs. Generally you'll want the ViewModel to handle user input and update both the presentation object as well as notify the Model a change was made, if this is something your app requires. Think of it as the Model holds the actual data whereas the ViewModel only holds what the user sees.
Unfortunately, without knowing what you're trying to do in a little more detail it's hard to give more specific advice than this!

MVVM - How to keep formatted properties up-to-date?

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

WPF MVVM PropertyChanged notifications in the ViewModel triggered by Model Events

I am having a problem understanding how to propagate a property changed event in a Model class up through the ViewModel and into the view. I am trying to conform to the MVVM pattern so please keep that in mind.
I have a Model that I am trying to expose by the ViewModel. My Model class queries an Api call to get the server status and exposes that status in public properties. Ex:
public class ServerStatusRequest : ApiRequest
{
//Exposable properties by request
public ServerStatusHelperClass Status { get; set; }
Where ServerStatusHelperClass is just a wrapper for the combined results in the query:
public class ServerStatusHelperClass
{
public bool ServerStatus { get; set; }
public int OnlinePlayers { get; set; }
The cool thing about my ApiRequest base class is that it checks the cache time of a particular Api call and updates the Results by using a System.Timers.Timer. So, for example, the ServerStatus Api call is cached for 3 minutes on the Api, so every 3 minutes my ServerStatusApiRequest object will have fresh data for it. I expose a UpdatedResults event in all ApiRequest classes to notify when new data comes in.
Now I want my ViewModel to have an instance of ServerStatusApiRequest and bind to its ServerStatusHelperClass Status property and stay up to date with the changes every time the information is updated, but my view (for binding) can't know about my model, and thus, doesn't know about my UpdatedResults event in my ApiRequest class. How can I reflect that out to the View through my ViewModel? Am I doing something completely weird here?
Here is what I have that is semi-working but I feel is a very hacky solution:
In my ViewModel:
public const string EveServerStatusPropertyName = "EveServerStatus";
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
set
{
//if (_eveServerStatus == value)
//{
// return;
//}
//RaisePropertyChanging(EveServerStatusPropertyName);
_eveServerStatus = value;
RaisePropertyChanged(EveServerStatusPropertyName);
}
}
public void UpdateEveServerStatus(object sender, EventArgs e)
{
EveServerStatus = (ServerStatusRequest)sender;
}
And in the ViewModels constructor I subscribe to the Model's event:
EveServerStatus.UpdatedResults += new UpdatedResultsEventHandler(UpdateEveServerStatus);
As you can see, this seems extremely redundant. And I also ran into a problem where I had to comment out the check in the setter for EveServerStatus because at that point the _eveServerStatus was already updated to value just without it knowing and I wanted to fire the event anyway.
I fell like I'm missing a key concept here to link this all together much more easily.
Thanks for any input.
I have come across a much better way to implement the behavior I was looking for. Here is the code in my ViewModel:
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
}
No setter as my ViewModel nor my View should be changing this data. And Inside my ServerStatusRequest class I have a property exposing the ServerStatusHelperClass object as shown in the Question. I have changed the ServerStatusHelperClass and made it implement INotifyPropertyChanged as so:
public class ServerStatusHelperClass : ObservableObject
{
private bool _serverStatus;
public bool ServerStatus
{
get
{
return _serverStatus;
}
set
{
_serverStatus = value;
RaisePropertyChanged("ServerStatus");
}
}
...
ObservableObject is just a simple class that implements INotifyPropertyChanged for me from mvvmlight.
By doing this my View is automatically updated when my ApiRequest class modifies it's ServerStatusHelperClass object.
Input on this solution is welcome.

Categories

Resources