IsDirty being set when BeginEdit called

Jun 4, 2014 at 4:34 PM
Hi,

I have a situation where I am using the ViewModelDetailBase and I require my view to start in edit mode without the user taking action. My ViewModel creates an instance of the ViewModelDetailBase class and sets its model. At this stage all is well and the IsDirty property is false. As soon as this has completed I then call the BeginEdit method of the ViewModelDetailBase. Once this call has completed then the IsDirty property is set to true.

I attached to the PropertyChanged event on the model as well as the ViewModelDetailBase to determine which property is causing this. The model propertychanged is never executed. Only the ViewModelDetailBase propertychanged executes and the properties changed on this are the Model, IsDirty and IsEditing properties. This does not seem like the correct behavior as I would have only expected the IsEditing property to be set to true and not the IsDirty.

How can I go about determining what exactly is causing the IsDirty property to be set to true. The value on the cloned object and original also seem to be the same.

Thanks
Coordinator
Jun 6, 2014 at 10:53 PM
I'll take a look at this to see what is happening. I'm on vacation this week, so probably next week I'll have a chance. Thanks for your patience.

Cheers,
Tony
Aug 1, 2014 at 4:23 PM
Edited Aug 1, 2014 at 4:24 PM
I have also encountered this problem. I started with an object like this:
    public class LocalConfig : ModelBase<LocalConfig>
    {
        #region Properties

        public String ServerName
        {
            get { return _ServerName; }
            set
            {
                if (_ServerName != value)
                {
                    _ServerName = value;
                }
                NotifyPropertyChanged(m => m.ServerName);
            }
        }
        private String _ServerName;

        //public String ServerName

        #endregion Properties

        #region Constructors

        /// <summary>
        /// Create a configuration object bound to "config.xml" 
        /// contained in [CommonApplicationData]\[Manufacturer]\[ProductName]
        /// </summary>
        public LocalConfig()
        {

        }

        #endregion Constructors
    }
And I bind to it like this:
                    <TextBox Name="DatabaseServer" 
                             Style="{StaticResource DisabledDuringIOTextBox}" 
                             Text="{Binding Model.ServerName, UpdateSourceTrigger=PropertyChanged}"/>
Using this method, everything works as expected (IsDirty is False until I change 'ServerName')


I then add a class to contain all the database info like this:
    public class Database : ModelBase<Database>
    {
        public String ServerName //{ get; set; }
        {
            get { return _ServerName; }
            set
            {
                if (_ServerName != value)
                {
                    _ServerName = value;
                }
                //NotifyPropertyChanged(m => m.ServerName);
            }
        }
        private String _ServerName = "TestServer";
    }
And add the following to LocalConfig() (and removed the ServerName property)

      public Database DatabaseInfo 
        {
            get { return _DatabaseInfo; }
            set
            {
                if (_DatabaseInfo != value)
                {
                    _DatabaseInfo = value;
                    NotifyPropertyChanged(m => m.DatabaseInfo);
                }
            }

        }
        private Database _DatabaseInfo = new Database();
And bind to it like this:
                <TextBox Name="DatabaseServer" 
                         Style="{StaticResource DisabledDuringIOTextBox}" 
                         Text="{Binding Model.DatabaseInfo.ServerName, UpdateSourceTrigger=PropertyChanged}"/>

And now IsDirty is True as soon as the page loads. Hopefully this example can help identify the issue. I would love to be able to use this to monitor the changes to my database info.
Coordinator
Aug 2, 2014 at 4:24 PM
I'll definitely have a look at this. I'll repro the issue and see what's going on. Thanks for reporting it!

Cheers,
Tony
Aug 11, 2014 at 4:43 PM
Have you had a chance to look at this? I have a new project on which I would like to use SimpleMVVM but cannot use it unless I can extend the base class.
Coordinator
Aug 11, 2014 at 8:20 PM
Ah yes, next on my To Do list. Sorry for the delay...
Coordinator
Aug 12, 2014 at 10:08 AM
@BrianK: Alright, I can see what is happening here now. IsDirty is based on object hash codes. To get this to work, you just need to override GetHashCode in your Database class, by OR-ing together each property on the class, like so:
        // Calculates object has code based on property hash codes
        public override int GetHashCode(object item, params string[] excludeProps)
        {
            int hashCode = 0;
            foreach (var prop in item.GetType().GetProperties())
            {
                if (!excludeProps.Contains(prop.Name))
                {
                    object propVal = prop.GetValue(item, null);
                    if (propVal != null)
                    {
                        hashCode = hashCode ^ propVal.GetHashCode();
                    }
                }
            }
            return hashCode;
        }
The reason why IsDirty is flipping to true is that the default implementation of System.Object is to use reference equality.
Aug 12, 2014 at 2:54 PM
Edited Aug 12, 2014 at 2:54 PM
Thanks so much for your help on this issue.

I have added your code snippet to the Database model as follows:
    public class Database : ModelBase<Database>
    {
        public Database() { }

        // Overriding the GetHashCode prevents the Clone operation from marking an 
        // object Dirty when it is first cloned
        public override int GetHashCode(object item, params string[] excludeProps)
        {
            int hashCode = 0;
            foreach (var prop in item.GetType().GetProperties())
            {
                if (!excludeProps.Contains(prop.Name))
                {
                    object propVal = prop.GetValue(item, null);
                    if (propVal != null)
                    {
                        hashCode = hashCode ^ propVal.GetHashCode();
                    }
                }
            }
            return hashCode;
        }

        public String ServerName //{ get; set; }
        {
            get { return _ServerName; }
            set
            {
                if (_ServerName != value)
                {
                    _ServerName = value;
                }
                NotifyPropertyChanged(m => m.ServerName);
            }
        }
        private String _ServerName = "MyTestServer";
    }
But when I try to run this I get an error of "No suitable method found to override". Is there something else I need to add to get this to work? GetHashCode() doesn't appear to take any parameters so I am not sure how to implement this in my code?
Coordinator
Aug 12, 2014 at 3:01 PM
Sorry that should be a helper method, which is called by the override to object.GetHashCode. In Visual Studio, just type "override" and select GetHashCode, to generate the method stub. Then from that method call the helper method which generates the hash code based on property values, excluding certain properties.
Aug 12, 2014 at 5:43 PM
Thanks, that solved the IsDirty issue when first loading the object.