using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.Core; using Tango.Core.DI; namespace Tango.SharedUI { /// /// Represents an abstract view model. /// /// /// public abstract class ViewModel : ExtendedObject, INotifyDataErrorInfo { private List> _currentErrors = new List>(); //Holds the current validation errors. private bool _hasErrors; /// /// Gets a value that indicates whether the entity has validation errors. /// public bool HasErrors { get { return _hasErrors; } set { _hasErrors = value; RaisePropertyChangedAuto(); } } private ObservableCollection _validationErrors; /// /// Gets or sets the validation errors. /// public ObservableCollection ValidationErrors { get { return _validationErrors; } protected set { _validationErrors = value; RaisePropertyChangedAuto(); } } /// /// Occurs when the validation errors have changed for a property or for the entire entity. /// public event EventHandler ErrorsChanged; /// /// Gets the validation errors for a specified property or for the entire entity. /// /// The name of the property to retrieve validation errors for; or null or , to retrieve entity-level errors. /// /// The validation errors for the property or entity. /// public virtual IEnumerable GetErrors(string propertyName) { return _currentErrors.Where(x => x.Key == propertyName).Select(x => x.Value).ToList(); } /// /// Invoked the event. /// /// Name of the property. protected void RaiseError(String propName) { ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propName)); } /// /// Validates this view model by validation attributes. /// /// protected bool Validate() { OnValidating(); HasErrors = false; _currentErrors.Clear(); if (ValidationErrors == null) { ValidationErrors = new ObservableCollection(); } ValidationErrors.Clear(); foreach (var prop in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { foreach (var validation in prop.GetCustomAttributes()) { if (!validation.IsValid(prop.GetValue(this))) { HasErrors = true; _currentErrors.Add(new KeyValuePair(prop.Name, validation.ErrorMessage)); ValidationErrors.Add(validation.ErrorMessage); RaiseError(prop.Name); } } } return !HasErrors; } /// /// Called before validating. /// protected virtual void OnValidating() { } /// /// Posts the specified message using the messenger service. /// /// /// The message. /// protected virtual T PostMessage(T message) { TangoMessenger.Default.Send(message); return message; } /// /// Registers a messenger message handler. /// /// /// The handler. protected virtual void RegisterMessage(Action handler) { TangoMessenger.Default.Register(handler); } } /// /// Represents an abstract view model which expects an of the specified type T. /// /// /// /// public abstract class ViewModel : ViewModel where T : IView { /// /// Gets or sets the view. /// public T View { get; set; } /// /// Initializes a new instance of the class. /// /// The view. public ViewModel(T view) { View = view; View.ViewAttached += (x, e) => { View = (T)e; OnViewAttached(); }; } /// /// Initializes a new instance of the class. /// /// The view. /// if set to true waits for the view to be valid before registering for the event. public ViewModel(T view, bool delayed) { Task.Factory.StartNew(() => { while (view == null) { Thread.Sleep(10); } }).ContinueWith((c) => { View = view; View.ViewAttached += (x, e) => { View = (T)e; OnViewAttached(); }; }); } /// /// Called when the is loaded and attached. /// protected virtual void OnViewAttached() { } } }