PLEASE IGNORE: Using ObservableCollection in Model extending ModelBase

Feb 9, 2015 at 5:19 PM
I have a program that has multiple tabs, one of which allows for editing of what should be shown on each tab. I have a model in which I would like to use an ObservableCollection to track each tab.

Whenever I use BeginEdit(), the model is immediately dirty. I have tried overriding the GetHashCode method and ignoring the ObservableCollection (just to see if that worked) but the model remains dirty.

While debugging, it looks as though the GetHashCode override function is correctly called when I manually call BeginEdit() or EndEdit(). However, when I switch tabs, AreSame is called and it doesn't appear to use the overriden GetHashCode method.

Is there something I can do to allow me to use an ObservableCollection and still have the IsDirty flag operate correctly?


Here is a scaled down version of my code:

CustomTabConfigModel Model
    /// <summary>
    /// The data model for the mapping of custom tabs
    /// </summary>
    [XmlRoot("CustomTabsConfig")]
    public class CustomTabsConfigModel : ModelBase<CustomTabsConfigModel>
    {
        #region Initialization & Cleanup

        // Overriding the GetHashCode prevents the Clone operation from marking an 
        // object Dirty when it is first cloned
        public override int GetHashCode()
        {
            return GetHashCode(this, "CustomTabsCollection");
        }
        private 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;
        }
        
        /// <summary>
        /// Create a configuration object bound to "CustomTabs.xml" 
        /// contained in [CommonApplicationData]\[Manufacturer]\[ProductName]
        /// </summary>
        public CustomTabsConfigModel()
        {
        }

        /// <summary>
        /// Create a configuration object bound to the specified file name
        /// contained in [CommonApplicationData]\[Manufacturer]\[ProductName]
        /// </summary>
        /// <param name="fileName"></param>
        public CustomTabsConfigModel(String fileName)
        {
        }

        #endregion Initialization & Cleanup


        #region Properties

        /// <summary>
        /// Collection of custom tabs
        /// </summary>
        [XmlElement("CustomTab")]
        public ObservableCollectionExtended<CustomTab> CustomTabsCollection
        {
            get { return _CustomTabsCollection; }
            set
            {
                if (_CustomTabsCollection != value)
                {
                    _CustomTabsCollection = value;
                    NotifyPropertyChanged(m => m.CustomTabsCollection);
                }
            }
        }
        private ObservableCollectionExtended<CustomTab> _CustomTabsCollection;
    }
CustomTab Model
public class CustomTab : ModelBase<CustomTab>
{
    #region Initialization & Cleanup

    // Overriding the GetHashCode prevents the Clone operation from marking an 
    // object Dirty when it is first cloned
    public override int GetHashCode()
    {
        return GetHashCode(this);
    }
    private 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 CustomTab()
    {
        this.Header = "";
        this.TabVisibility = Visibility.Visible;
    }

    #endregion Initialization & Cleanup


    #region Properties

    public String Header { get; set; }

    /// <summary>
    /// Sets the Visibility of the tab
    /// </summary>
    public Visibility TabVisibility
    {
        get { return _TabVisibility; }
        set
        {
            if (_TabVisibility != value)
            {
                _TabVisibility = value;
                NotifyPropertyChanged(m => m.TabVisibility);
            }
        }
    }
    private Visibility _TabVisibility = Visibility.Visible;

    #endregion Properties

}

ViewModel
public class TabsSetupViewModel : ViewModelDetailBase<TabsSetupViewModel, CustomTabsConfigModel>, ITabViewModel
{
    #region Initialization and Cleanup

    // Default ctor
    // Use the SystemSetupViewModel(model) constructor to make all the new objects
    public TabsSetupViewModel()
    {
        CustomTabsConfigInfo = new CustomTabsConfigModel();

        if (CustomTabsConfigInfo.LoadConfig())
        {
            Log.Write(LogLevel.Info, "Configuration file loaded");
            base.Model = CustomTabsConfigInfo;
            BeginEdit();
        }
        else
        {
            Log.Write(LogLevel.Error, "Error loading config file");
        }
    }

    #endregion Initialization and Cleanup


    #region Properties

    public String Header { get; set; }

    public CustomTabsConfigModel CustomTabsConfigInfo
    {
        get { return _CustomTabsConfigInfo; }
        set
        {
            if (_CustomTabsConfigInfo != value)
            {
                _CustomTabsConfigInfo = value;
                NotifyPropertyChanged(m => m.CustomTabsConfigInfo);
            }
        }
    }
    private CustomTabsConfigModel _CustomTabsConfigInfo;


    #endregion Properties


    #region Methods

    public void TabSelected()
    {
        Log.Write(LogLevel.Debug, "Selected tab '" + Header + "'");
    }

    /// <summary>
    /// Saves any changes to the current model
    /// </summary>
    public void SaveButtonClicked()
    {
        // verify database settings
        //VerifyDatabaseButtonClicked(this, new RoutedEventArgs());

        // Stop edit mode to proceed with save
        EndEdit();
        if (base.Model.SaveConfig(base.Model))
        {
            Log.Write(LogLevel.Info, "Tab Settings file saved.");

            // Send message notifying others about changes to tabs settings
            SendMessage(MessageTokens.TABS_CONFIG_CHANGED, new NotificationEventArgs());
        }

        // Put model back into edit mode
        BeginEdit();
    }
Feb 18, 2015 at 11:38 AM
Edited Feb 18, 2015 at 1:32 PM
Please ignore this thread (and delete if you want). I have updated my code to use List instead of ObservableCollection and am having issues with that (see next thread)
Coordinator
Feb 19, 2015 at 6:06 AM
I see ... I'll have a look at the next thread then.