using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.Core.Commands; using Tango.BL.Entities; using Tango.MachineStudio.Common.Notifications; using Tango.MachineStudio.DB.Managers; using Tango.SharedUI; using System.Data.Entity.Infrastructure; using Tango.MachineStudio.DB.Messages; using System.Collections.ObjectModel; using System.Reflection; using Tango.MachineStudio.Common.StudioApplication; using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using Tango.BL; using Tango.Core.DI; namespace Tango.MachineStudio.DB.ViewModels { public abstract class DbTableViewModel : ViewModel where T : class, IObservableEntity { protected INotificationProvider _notification; /// /// Initializes a new instance of the class. /// public DbTableViewModel(INotificationProvider notification) : base() { _notification = notification; Adapter = ObservablesEntitiesAdapter.Instance; ValidationErrors = new ObservableCollection(); AddCommand = new RelayCommand(OnAdd); EditCommand = new RelayCommand(OnEdit, (x) => SelectedEntity != null); DeleteCommand = new RelayCommand(OnDelete, (x) => SelectedEntity != null); DialogOKCommand = new RelayCommand(() => OnDialogOKPressed(DialogOpenMode, EditEntity)); DialogCancelCommand = new RelayCommand(() => OnDialogCancelPressed(DialogOpenMode, EditEntity)); IsDialogOpen = false; } private T _editEntity; /// /// Gets or sets the edit entity. /// public T EditEntity { get { return _editEntity; } set { _editEntity = value; RaisePropertyChangedAuto(); } } private DialogOpenMode _dialogOpenMode; /// /// Gets or sets the dialog open mode. /// public DialogOpenMode DialogOpenMode { get { return _dialogOpenMode; } set { _dialogOpenMode = value; RaisePropertyChangedAuto(); } } private bool _isDialogOpen; /// /// Gets or sets a value indicating whether this instance is dialog open. /// public bool IsDialogOpen { get { return _isDialogOpen; } set { _isDialogOpen = value; RaisePropertyChangedAuto(); } } private ObservablesEntitiesAdapter _adapter; /// /// Gets or sets the DB adapter. /// public ObservablesEntitiesAdapter Adapter { get { return _adapter; } set { _adapter = value; RaisePropertyChangedAuto(); } } private T _selectedEntity; /// /// Gets or sets the selected entity. /// public T SelectedEntity { get { return _selectedEntity; } set { _selectedEntity = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _filter; /// /// Gets or sets the search filter. /// public String Filter { get { return _filter; } set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(value); } } /// /// Gets or sets the dialog OK command. /// public RelayCommand DialogOKCommand { get; set; } /// /// Gets or sets the dialog cancel command. /// public RelayCommand DialogCancelCommand { get; set; } /// /// Gets or sets the add command. /// public RelayCommand AddCommand { get; set; } /// /// Gets or sets the edit command. /// public RelayCommand EditCommand { get; set; } /// /// Gets or sets the delete command. /// public RelayCommand DeleteCommand { get; set; } protected override void OnValidating() { base.OnValidating(); ValidationErrors.Clear(); foreach (var prop in typeof(T).GetPropertiesWithAttribute(BindingFlags.Public | BindingFlags.Instance).Where(x => !x.PropertyType.IsGenericType && x.PropertyType.IsClass && !x.Name.Contains("Guid"))) { if (prop.GetValue(EditEntity) == null) { ValidationErrors.Add(prop.Name + " is required"); } } } /// /// Called when delete command invoked. /// protected virtual async void OnDelete() { using (_notification.PushTaskItem("Saving changes to database...")) { try { SelectedEntity.Detach(Adapter.Context); await Adapter.Context.SaveChangesAsync(); } catch (Exception ex) { Adapter.Invalidate(); _notification.ShowError("Could not delete entity." + Environment.NewLine + ex.Message); } SelectedEntity = null; } } /// /// Called when edit command invoked. /// protected virtual void OnEdit() { ValidationErrors.Clear(); DialogOpenMode = DialogOpenMode.Editing; EditEntity = GetEditableEntity(DialogOpenMode); TangoMessenger.Default.Send(new OpenEntityEditViewMessage(DialogOpenMode, this, typeof(T))); IsDialogOpen = true; } /// /// Called when add command invoked. /// protected virtual void OnAdd() { ValidationErrors.Clear(); DialogOpenMode = DialogOpenMode.Adding; EditEntity = GetEditableEntity(DialogOpenMode); var codeProp = EditEntity.GetType().GetProperty("Code"); if (codeProp != null) { if (EditEntity.GetDbSet(Adapter.Context).Count() > 0) { int lastCode = EditEntity.GetDbSet(Adapter.Context).ToList().Max(x => (int)codeProp.GetValue(x)); codeProp.SetValue(EditEntity, lastCode + 1); } } TangoMessenger.Default.Send(new OpenEntityEditViewMessage(DialogOpenMode, this, typeof(T))); IsDialogOpen = true; } /// /// Called when dialog closes with OK button. /// /// The mode. protected virtual async void OnDialogOKPressed(DialogOpenMode mode, T entity) { if (!Validate()) return; if (ValidationErrors.Count > 0) return; var codeProp = entity.GetType().GetProperty("Code"); if (codeProp != null) { int code = (int)codeProp.GetValue(entity); if (entity.GetDbSet(Adapter.Context).ToList().Where(x => x.Guid != entity.Guid).Select(x => (int)codeProp.GetValue(x)).Contains(code)) { _notification.ShowError("The entity code specified already exists."); return; } } TangoMessenger.Default.Send(new CloseEntityEditViewMessage()); if (mode == DialogOpenMode.Editing) { entity.ShallowCopyTo(SelectedEntity); entity = SelectedEntity; } OnBeforeEntitySave(mode, entity); using (_notification.PushTaskItem("Saving changes to database...")) { if (mode == DialogOpenMode.Adding) { entity.Attach(Adapter.Context); } try { await entity.SaveAsync(Adapter.Context); } catch (DbUpdateException ex) { if (mode == DialogOpenMode.Adding) { entity.Detach(Adapter.Context); } Adapter.Invalidate(); _notification.ShowError("Could not save entity." + Environment.NewLine + ex.InnerException.InnerException != null ? ex.InnerException.InnerException.Message : ex.InnerException.Message); } catch (Exception) { Adapter.Invalidate(); _notification.ShowError("Could not save entity." + Environment.NewLine + "Please make sure all fields are properly populated."); } IsDialogOpen = false; SelectedEntity = EditEntity; SelectedEntity = null; } } /// /// Called when [before entity save]. /// /// The mode. /// The entity. protected virtual void OnBeforeEntitySave(DialogOpenMode mode, T entity) { } /// /// Called when dialog closes with cancel button. /// /// The mode. protected virtual void OnDialogCancelPressed(DialogOpenMode mode, T entity) { TangoMessenger.Default.Send(new CloseEntityEditViewMessage()); IsDialogOpen = false; } /// /// Gets the editable entity. /// /// The mode. /// private T GetEditableEntity(DialogOpenMode mode) { if (mode == DialogOpenMode.Adding) { var newEntity = Activator.CreateInstance(); InitializeEntity(newEntity); return newEntity; } else { return SelectedEntity.ShallowClone(); } } protected virtual void OnFilterChanged(String filter) { String viewSourceName = this.GetType().Name.Replace("ViewVM", "ViewSource"); ICollectionView collectionView = Adapter.GetType().GetProperty(viewSourceName).GetValue(Adapter) as ICollectionView; collectionView.Filter = (entity) => { return FilterEntity((T)entity, filter); }; } private bool FilterEntity(T entity, String filter) { foreach (var prop in entity.GetType().GetPropertiesWithAttribute(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType.IsClass && x.PropertyType != typeof(String) && x.PropertyType != typeof(byte[]) && !x.PropertyType.IsGenericType)) { object obj = prop.GetValue(entity); if (obj != null) { foreach (var innerProp in obj.GetType().GetPropertiesWithAttribute(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != "Deleted" && x.Name != "ID" && x.Name != "LastUpdated").Where(x => !x.PropertyType.IsGenericType && (x.PropertyType.IsClass || x.PropertyType == typeof(String)))) { object value = innerProp.GetValue(obj); if (value != null) { if (value.ToString().ToLower().Contains(filter.ToLower())) { return true; } } } } } return entity. GetType(). GetPropertiesWithAttribute(BindingFlags.Public | BindingFlags.Instance). Where(x => x.Name != "Deleted" && x.Name != "ID" && x.Name != "LastUpdated"). Where(x => !x.PropertyType.IsGenericType && (x.PropertyType.IsClass || x.PropertyType == typeof(String))). Select(prop => prop.GetValue(entity).ToString()). ToList(). Any(x => x.ToLower().Contains(filter.ToLower())); } protected virtual void InitializeEntity(T entity) { } } }