I am having an issue where the PropertyChanged event from the generated Ria Services entities is not raised for all the properties.
When I look at the generated code (client-side), I can see that my entities are deriving from the Entity object which implements INotifyPropertyChanged. I can also see that some properties, like the Id property, is raising the PropertyChanged event but some are not.
I did not use any T4 templates so the default were used.
So, my question is:
Is there an option/attribute that I can set so that the PropertyChanged event be raised for any properties of the generated client-side entities?
Any help would be appreciated.
EDIT:
Here's an example of a property, in the auto-generated client-side file, which doesn't raise the PropertyChanged event:
[DataMember()]
[Required()]
[StringLength(50)]
public string FirstName
{
get
{
return this._firstName;
}
set
{
if ((this._firstName != value))
{
this.OnFirstNameChanging(value);
this.RaiseDataMemberChanging("FirstName");
this.ValidateProperty("FirstName", value);
this._firstName = value;
this.RaiseDataMemberChanged("FirstName");
this.OnFirstNameChanged();
}
}
}
And this is what was defined in the model server-side:
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String FirstName
{
get
{
return _FirstName;
}
set
{
OnFirstNameChanging(value);
ReportPropertyChanging("FirstName");
_FirstName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("FirstName");
OnFirstNameChanged();
}
}
Here's an example of a property, in the auto-generated client-side file, which does raise the PropertyChanged event:
[DataMember()]
[Editable(false, AllowInitialValue=true)]
[Key()]
[RoundtripOriginal()]
public Guid Id
{
get
{
return this._id;
}
set
{
if ((this._id != value))
{
this.OnIdChanging(value);
this.ValidateProperty("Id", value);
this._id = value;
this.RaisePropertyChanged("Id");
this.OnIdChanged();
}
}
}
And this is what was defined in the model server-side:
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Guid Id
{
get
{
return _Id;
}
set
{
if (_Id != value)
{
OnIdChanging(value);
ReportPropertyChanging("Id");
_Id = StructuralObject.SetValidValue(value);
ReportPropertyChanged("Id");
OnIdChanged();
}
}
}
The PropertyChanged event is encapsulated in the RaiseDataMemberChanged(). You can check this with any Decompiler (.NET Reflector, etc.).
The RaiseDataMemberChanged and RaiseDataMemberChanging methods are used to notify the framework about changes for change tracking and state transitions.
These two properties are diffrent, cause the Id property is decorated with the Key-Attribute. I think properties with this attribute get special handling, due to associations with other entities, and a diffrent auto-generated implementation, cause there is not need to track changes or transistions on change.
So in your case, i think there is another problem, why your PropertyChanged event is (maybe) not fired.
Related
I have a Person class:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name{
get { return _name; }
set {
if ( _name != value ) {
_name = value;
OnPropertyChanged( "Name" );
}
}
private Address _primaryAddress;
public Address PrimaryAddress {
get { return _primaryAddress; }
set {
if ( _primaryAddress != value ) {
_primaryAddress = value;
OnPropertyChanged( "PrimaryAddress" );
}
}
//OnPropertyChanged code goes here
}
I have an Address class:
public class Address : INotifyPropertyChanged
{
private string _streetone;
public string StreetOne{
get { return _streetone; }
set {
if ( _streetone != value ) {
_streetone = value;
OnPropertyChanged( "StreetOne" );
}
}
//Other fields here
//OnPropertyChanged code goes here
}
I have a ViewModel:
public class MyViewModel
{
//constructor and other stuff here
private Person _person;
public Person Person{
get { return _person; }
set {
if ( _person != value ) {
_person = value;
OnPropertyChanged( "Person" );
}
}
}
I have a View which has the following lines:
<TextBox Text="{Binding Person.Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged />
<TextBox Text="{Binding Person.Address.StreetOne, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged />
Both values show up in the text box ok when the view loads.
Changes to the first text box will fire OnPropertyChanged( "Person" ) in MyViewModel. Great.
Changes to the second text box ("Person.Address.StreetOne") does NOT fire OnPropertyChanged( "Person" ) inside MyViewModel. Meaning it doesn't call the Person object's SET method. Not great. Interestingly the SET method of StreetOne inside the Address class is called.
How do I get the SET method of the Person object inside the ViewModel to be called when Person.Address.StreetOne is changed???
Do I need to flatten my data so SteetOne is inside Person and not Address??
Thanks!
While adding 'pass-through' properties to your ViewModel is a fine solution, it can quickly become untenable. The standard alternative is to propagate changes as below:
public Address PrimaryAddress {
get => _primaryAddress;
set {
if ( _primaryAddress != value )
{
//Clean-up old event handler:
if(_primaryAddress != null)
_primaryAddress.PropertyChanged -= AddressChanged;
_primaryAddress = value;
if (_primaryAddress != null)
_primaryAddress.PropertyChanged += AddressChanged;
OnPropertyChanged( "PrimaryAddress" );
}
void AddressChanged(object sender, PropertyChangedEventArgs args)
=> OnPropertyChanged("PrimaryAddress");
}
}
Now change notifications are propagated from Address to person.
Edit: Moved handler to c# 7 local function.
if you want the viewmodel SET to be called you could create a street property
public class MyViewModel
{
//constructor and other stuff here
public string Street{
get { return this.Person.PrimaryAddress.StreetOne; }
set {
if ( this.Person.PrimaryAddress.StreetOne!= value ) {
this.Person.PrimaryAddress.StreetOne = value;
OnPropertyChanged( "Street" );
}
}
}
xaml
<TextBox Text="{Binding Street, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged />
but this solution has its drawbacks. i go with Reeds answer in my projects
How do I get the SET method of the Person object inside the ViewModel to be called when Person.Address.StreetOne is changed???
Why do you want to do this? It should not be required - you only need the StreetOne property changed event to fire.
Do I need to flatten my data so SteetOne is inside Person and not Address??
If you want to actually cause this to trigger, you don't need to flatten it (though that is an option). You can subscribe to the Address's PropertyChanged event within your Person class, and raise the event for "Address" within Person when it changes. This shouldn't be necessary, however.
Since I wasn't able to find a ready-to-use solution, I've done a custom implementation based on Pieters (and Marks) suggestions (thanks!).
Using the classes, you will be notified about any change in a deep object tree, this works for any INotifyPropertyChanged implementing Types and INotifyCollectionChanged* implementing collections (Obviously, I'm using the ObservableCollection for that).
I hope this turned out to be a quite clean and elegant solution, it's not fully tested though and there is room for enhancements. It's pretty easy to use, just create an instance of ChangeListener using it's static Create method and passing your INotifyPropertyChanged:
var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged +=
new PropertyChangedEventHandler(listener_PropertyChanged);
the PropertyChangedEventArgs provide a PropertyName which will be always the full "path" of your Objects. For example, if you change your Persons's "BestFriend" Name, the PropertyName will be "BestFriend.Name", if the BestFriend has a collection of Children and you change it's Age, the value will be "BestFriend.Children[].Age" and so on. Don't forget to Dispose when your object is destroyed, then it will (hopefully) completely unsubscribe from all event listeners.
It compiles in .NET (Tested in 4) and Silverlight (Tested in 4). Because the code in seperated in three classes, I've posted the code to gist 705450 where you can grab it all: https://gist.github.com/705450 **
*) One reason that the code is working is that the ObservableCollection also implements INotifyPropertyChanged, else it wouldn't work as desired, this is a known caveat
**) Use for free, released under MIT License
There is a spelling mistake in your property change notification:
OnPropertyChanged( "SteetOne" );
should be
OnPropertyChanged( "StreetOne" );
WPF-MVVM beginner here.
My problem: in a WPF-MVVM UI I am editing an entity. Some properties when changed, require automatic updates on other properties. These are done in Entity class, set methods, but not reflected in my View
More details:
1) I have the Model (a simple class with properties) in a separate assembly (not WPF related since is the general business model). Note that "SomeOption" when set to false, requires some other options to automatically be changed.
Example:
public class Employee : BaseEntity
{
public string EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
....
private bool someOption
public bool SomeOption {
get
{ return someOption}
set {
someOption= value;
if (!value)
{
OtherOption = false;
OtherProperty= "";
AndAnotherOption= false;
}
}
}
}
2) The WPF UI has a base ViewModel implementing INotifyPropertyChanged. The current edited record (Employee) is a public property of the ViewModel:
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
if (_selectedEmployee != value)
{
_selectedEmployee = value;
OnPropertyChanged(nameof(SelectedEmployee));
}
}
}
3) When un-checking the checkbox bound to "SomeOption", the other properties which are changed in entity code, are not reflected on the View, and stay on the screen as edited by user.
Please let me know what I am missing. Thanks!
You should implement INotifyPropertyChanged in your model to update entities at your UI. For example:
public class Employee : BaseEntity, INotifyPropertyChanged
{
private string employeeNumber;
public string EmployeeNumber {
get{return employeeNumber};
set
{
employeeNumber=value;
OnPropertyChanged("EmployeeNumber");
}
//...Other properties...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Employee needs to implement INotifyPropertyChanged just as your viewmodel does, and fire PropertyChanged on changes to its own properties (the ones you're calling OtherOption, OtherProperty, etc.)
What you've got now will update the UI when the view model selects a different Employee, but subsequent changes to that Employee don't send any notifications.
I have a usercontrol bound to a VM. This VM contains a collection property, lets call it "MyCollection" and several regular properties lets call one of them "SomeProperty". As you can see, the get and set logic for this property references the collection in the VM.
The problem is, when I make a change to "MyCollection", this obviously has an impact on the values that are shown in the UI (as they are calculated based on it). However, my UI doesn't appear to be smart enough to update itself whenever "MyCollection" changes.
Here is the VM my usercontrol is bound to:
public class MyVM
{
private ObservableCollection<SomeOtherVM> _myCollection = new ObservableCollection<SomeOtherVM>();
public MyVM()
{
}
public ObservableCollection<SomeOtherVM> MyCollection
{
get { return _myCollection; }
[Notify]
set
{
_myCollection = value;
}
}
public virtual string SomeProperty
{
get
{
if (_myCollection.Count == 1)
return _myCollection[0].SomeProperty;
else
return "More than one "SomeOtherVM" has been selected";
}
[Notify]
set
{
foreach (SomeOtherVM s in _myCollection)
{
s.SomeProperty = value;
}
}
}
}
}
Note that nothing in my usercontrol is directly bound to the collection, it is only bound to other properties that reference the collection in its get; set; methods.
Is there anything I could do in the VM to force the UI to update whenever "MyCollection" is changed? I want to avoid having to put anything in the code behind for the user control.
Subscribe to CollectionChanged of MyCollection and fire PropertyChanged-notifications for the other properties there (no fancy attribute usage for you).
How can I implement cancelation of editing an object using MVVM.
For example: I have a list of customers. I choose one customer an click the button "Edit", a dialog window(DataContext is binded to CustomerViewModel) opens and I start editing customer's fields. And then I decide to cancel editing, but the fields of the customer have been already changed, so how can I return a customer to its previous state in MVVM?
Check out the IEditableObject interface. Your Customer class should implement that, and your commands can execute BeginEdit / CancelEdit / EndEdit as appropriate.
You can use binding with UpdateSourceTrigger=Explicit. Here you can find more information how this can be implemented.
One super easy way, if your object is already serializable, such as if you are using WCF. You can serialize your original object into an internal field. If, your object isn't serializable, then just use AutoMapper to create a copy of your object with one line of code.
Order backup = Mapper.Map<Order, Order>(order);
When you handle your CancelCommand, just call AutoMapper in reverse. Since your properties already have a change notification everything just works. Its possible you could combine these techniques with IEditableObject, if you need and want to write the extra code.
In this article, Raul just reload the object from the DB. I guess it's less trouble than the solution Kent proposes.
internal void Cancel(CustomerWorkspaceViewModel cvm)
{
Mainardi.Model.ObjectMapping.Individual dc = cvm.DataContext
as Mainardi.Model.ObjectMapping.Individual;
int index = 0;
if (dc.ContactID > 0 && dc.CustomerID > 0)
{
index = _customerCollectionViewModel.List.IndexOf(dc);
_customerCollectionViewModel.List[index] =
_customerBAL.GetCustomerById(dc.CustomerID);
}
Collection.Remove(cvm);
}
Based on Камен Великов's answer:
You can mark your bindings as to be updated manually by defining
<TextBox Name="yourTextBox" Text="{BindingPath=YourBinding, UpdateSourceTrigger=Explicit}" />
in your view (XAML). Then, you have to write the changes from your UI in ViewModel by calling
yourTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
when Save is clicked.
Please note, if there are updated to the binding source triggered from anything else, they are still shown directly in the UI.
I had this problem too. I solved it using "The Memento Pattern Design". With this pattern you could easy save a copy of your original object and, in selectedIndexChange (of a control) or in the Cancel button, you could restore easy the prior version of your object.
An example of use of this pattern is available at How is the Memento Pattern implemented in C#4?
An example of code:
If we have a class User with properties UserName Password and NombrePersona we need to add methods CreateMemento and SetMemento:
public class Usuario : INotifyPropertyChanged
{
#region "Implementación InotifyPropertyChanged"
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private String _UserName = "Capture su UserName";
public String UserName
{
get { return _UserName; }
set { _UserName = value; RaisePropertyChanged("UserName"); }
}
private String _Password = "Capture su contraseña";
public String Password
{
get { return _Password; }
set { _Password = value; RaisePropertyChanged("Password"); }
}
private String _NombrePersona = "Capture su nombre";
public String NombrePersona
{
get { return _NombrePersona; }
set { _NombrePersona = value; RaisePropertyChanged("NombrePersona"); }
}
// Creates memento
public Memento CreateMemento()
{
return (new Memento(this));
}
// Restores original state
public void SetMemento(Memento memento)
{
this.UserName memento.State.UserName ;
this.Password = memento.State.Password ;
this.NombrePersona = memento.State.NombrePersona;
}
Then, we need a class Memento that will contain the "copy" of our object like this:
/// <summary>
/// The 'Memento' class
/// </summary>
public class Memento
{
//private Usuario _UsuarioMemento;
private Usuario UsuarioMemento { get; set; }
// Constructor
public Memento(Usuario state)
{
this.UsuarioMemento = new Usuario();
this.State.UserName = state.UserName ;
this.State.Password = state.Password ;
this.State.NombrePersona = state.NombrePersona ;
}
// Gets or sets state
public Usuario State
{
get { return UsuarioMemento; }
}
}
And we need a class that will generate and contains our memento object:
/// <summary>
/// The 'Caretaker' class
/// </summary>
class Caretaker
{
private Memento _memento;
// Gets or sets memento
public Memento Memento
{
set { _memento = value; }
get { return _memento; }
}
}
Then for implement this pattern we have to create an instance of Caretaker class
Caretaker creadorMemento = new Caretaker();
And create our memento object when a new user was selected for edit, for example in selectedIndexChange after the SelectedUser has been initializing, I use the method for event RaisPropertyChanged like this:
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
if (prop == "RowIndexSelected") // This is my property assigned to SelectedIndex property of my DataGrid
{
if ((this.UserSelected != null) && (creadorMemento .Memento != null))
{
this.UserSelected.SetMemento(creadorMemento .Memento);
}
}
if (prop == "UserSelected") // Property UserSelected changed and if not is null we create the Memento Object
{
if (this.UserSelected != null)
creadorMemento .Memento = new Memento(this.UserSelected);
}
}
An explication for this, when selectedIndexChanged change value we check if UserSelected and our memento object are not null means that our actual item in edit mode has changed then we have to Restore our object with the method SetMemento.
And if our UserSelected property change and is not null we "Create our Memento Object" that we will use when the edit was cancel.
For finish, we have use the SetMemento method in every method that we need to cancel the edition, and when the edit has commited like in the SaveCommand we can set null our memento object like this this.creadorMemento = null.
You could also, in your ViewModel copy the model's state to internal fields, and then expose these and then only set them on the model, when the user actually commits the change.
Problem could be, that on-the-fly validation will be more troublesome if validation relies on the entity being updated - if this is a requirement you could create a clone of the model to work on and then merging the clone with the actual entity when it is saved.
I'm developing an application in Silverlight2 and trying to follow the Model-View-ViewModel pattern. I am binding the IsEnabled property on some controls to a boolean property on the ViewModel.
I'm running into problems when those properties are derived from other properties. Let's say I have a Save button that I only want to be enabled when it's possible to save (data has been loaded, and we're currently not busy doing stuff in the database).
So I have a couple of properties like this:
private bool m_DatabaseBusy;
public bool DatabaseBusy
{
get { return m_DatabaseBusy; }
set
{
if (m_DatabaseBusy != value)
{
m_DatabaseBusy = value;
OnPropertyChanged("DatabaseBusy");
}
}
}
private bool m_IsLoaded;
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
}
}
}
Now what I want to do is this:
public bool CanSave
{
get { return this.IsLoaded && !this.DatabaseBusy; }
}
But note the lack of property-changed notification.
So the question is: What is a clean way of exposing a single boolean property I can bind to, but is calculated instead of being explicitly set and provides notification so the UI can update correctly?
EDIT: Thanks for the help everyone - I got it going and had a go at making a custom attribute. I'm posting the source here in case anyone's interested. I'm sure it could be done in a cleaner way, so if you see any flaws, add a comment or an answer.
Basically what I did was made an interface that defined a list of key-value pairs to hold what properties depended on other properties:
public interface INotifyDependentPropertyChanged
{
// key,value = parent_property_name, child_property_name, where child depends on parent.
List<KeyValuePair<string, string>> DependentPropertyList{get;}
}
I then made the attribute to go on each property:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class NotifyDependsOnAttribute : Attribute
{
public string DependsOn { get; set; }
public NotifyDependsOnAttribute(string dependsOn)
{
this.DependsOn = dependsOn;
}
public static void BuildDependentPropertyList(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
var obj_interface = (obj as INotifyDependentPropertyChanged);
if (obj_interface == null)
{
throw new Exception(string.Format("Type {0} does not implement INotifyDependentPropertyChanged.",obj.GetType().Name));
}
obj_interface.DependentPropertyList.Clear();
// Build the list of dependent properties.
foreach (var property in obj.GetType().GetProperties())
{
// Find all of our attributes (may be multiple).
var attributeArray = (NotifyDependsOnAttribute[])property.GetCustomAttributes(typeof(NotifyDependsOnAttribute), false);
foreach (var attribute in attributeArray)
{
obj_interface.DependentPropertyList.Add(new KeyValuePair<string, string>(attribute.DependsOn, property.Name));
}
}
}
}
The attribute itself only stores a single string. You can define multiple dependencies per property. The guts of the attribute is in the BuildDependentPropertyList static function. You have to call this in the constructor of your class. (Anyone know if there's a way to do this via a class/constructor attribute?) In my case all this is hidden away in a base class, so in the subclasses you just put the attributes on the properties. Then you modify your OnPropertyChanged equivalent to look for any dependencies. Here's my ViewModel base class as an example:
public class ViewModel : INotifyPropertyChanged, INotifyDependentPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
// fire for dependent properties
foreach (var p in this.DependentPropertyList.Where((x) => x.Key.Equals(propertyname)))
{
PropertyChanged(this, new PropertyChangedEventArgs(p.Value));
}
}
}
private List<KeyValuePair<string, string>> m_DependentPropertyList = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> DependentPropertyList
{
get { return m_DependentPropertyList; }
}
public ViewModel()
{
NotifyDependsOnAttribute.BuildDependentPropertyList(this);
}
}
Finally, you set the attributes on the affected properties. I like this way because the derived property holds the properties it depends on, rather than the other way around.
[NotifyDependsOn("Session")]
[NotifyDependsOn("DatabaseBusy")]
public bool SaveEnabled
{
get { return !this.Session.IsLocked && !this.DatabaseBusy; }
}
The big caveat here is that it only works when the other properties are members of the current class. In the example above, if this.Session.IsLocked changes, the notification doesnt get through. The way I get around this is to subscribe to this.Session.NotifyPropertyChanged and fire PropertyChanged for "Session". (Yes, this would result in events firing where they didnt need to)
The traditional way to do this is to add an OnPropertyChanged call to each of the properties that might affect your calculated one, like this:
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
OnPropertyChanged("CanSave");
}
}
}
This can get a bit messy (if, for example, your calculation in CanSave changes).
One (cleaner? I don't know) way to get around this would be to override OnPropertyChanged and make the call there:
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "IsLoaded" /* || propertyName == etc */)
{
base.OnPropertyChanged("CanSave");
}
}
You need to add a notification for the CanSave property change everywhere one of the properties it depends changes:
OnPropertyChanged("DatabaseBusy");
OnPropertyChanged("CanSave");
And
OnPropertyChanged("IsEnabled");
OnPropertyChanged("CanSave");
How about this solution?
private bool _previousCanSave;
private void UpdateCanSave()
{
if (CanSave != _previousCanSave)
{
_previousCanSave = CanSave;
OnPropertyChanged("CanSave");
}
}
Then call UpdateCanSave() in the setters of IsLoaded and DatabaseBusy?
If you cannot modify the setters of IsLoaded and DatabaseBusy because they are in different classes, you could try calling UpdateCanSave() in the PropertyChanged event handler for the object defining IsLoaded and DatabaseBusy.