| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 | using System.Collections.Generic;using System.ComponentModel;using System.Runtime.CompilerServices;using InABox.Core;using Xamarin.Forms;namespace comal.timesheets{    public abstract class Shell<TParent,TEntity> : BindableObject, INotifyPropertyChanged, IShell        where TParent : IModel        where TEntity : Entity    {                #region INotifyPropertyChanged                public event PropertyChangedEventHandler PropertyChanged;                protected void DoPropertyChanged(string propertyName)        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }                protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)        {            if (EqualityComparer<T>.Default.Equals(field, value)) return false;            field = value;            DoPropertyChanged(propertyName);            return true;        }                #endregion                protected virtual void RowChanged()        {                    }                private CoreRow _row = null;        public CoreRow Row        {            get => _row;            set            {                _row = value;                RowChanged();            }        }                public TParent Parent { get; set; }        IModel IShell.Parent => this.Parent;                #region Row Get/Set Caching                // We do this for three reasons:        // 1. Rather than define properties in once class and columns in another,        // we can define and link properties and columns in the one class,        // using a _static_ constructor, which reduces complexity        // 2. By caching based on the property name, we eliminate the need to convert        // expressions to strings (expensive), while still retaining type-safety        // 3. Using the Get/Set helper functions reduces code complexity when defining         // shell properties, and distinguishes between data and calculated properties                private static ShellColumns<TParent,TEntity> _columns = new ShellColumns<TParent,TEntity>();        public static ShellColumns<TParent,TEntity> Columns        {            get {  return _columns; }        }        protected virtual T Get<T>([CallerMemberName] string property = null)        {            var value = Row.Values[Columns.IndexOf(property)];            return value != null ? (T)CoreUtils.ChangeType(value, typeof(T)) : CoreUtils.GetDefault<T>();                }                protected virtual void Set<T>(T value, bool notify = true, [CallerMemberName] string property = null)        {                        Row.Values[Columns.IndexOf(property)] = value;            if (notify)                DoPropertyChanged(property);        }        #endregion            }}
 |