using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.Composition; using System.Linq; using System.Threading.Tasks; using DTS.Common.Events; using DTS.Common.Events.ISO; using DTS.Common.Interactivity; using DTS.Common.Interface; using DTS.Common.Interface.ISO.ExtraProperties; using ExtraProperties.Model; using ExtraProperties.Resources; using Prism.Events; using Prism.Regions; using Unity; namespace ExtraProperties { [PartCreationPolicy(CreationPolicy.Shared)] public class ExtraPropertiesListViewModel : IExtraPropertiesListViewModel { public IExtraPropertiesListView View { get; set; } private IEventAggregator _eventAggregator { get; } private IUnityContainer UnityContainer { get; } public InteractionRequest NotificationRequest { get; } public InteractionRequest ConfirmationRequest { get; } #region constructors and initializers private static bool _bAddListeners = false; /// /// Creates a new instance of the ExtraPropertiesListViewModel /// /// /// The logical placeholder defined within the application's UI (in the shell or within views) into which views are displayed. /// The EventAggregator which allows different components to publish/subscribe to events without being coupled to each other. /// The unityContainer. public ExtraPropertiesListViewModel(IExtraPropertiesListView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer) { View = view; View.DataContext = this; NotificationRequest = new InteractionRequest(); ConfirmationRequest = new InteractionRequest(); _eventAggregator = eventAggregator; UnityContainer = unityContainer; _eventAggregator.GetEvent().Subscribe(OnRaiseNotification); _eventAggregator.GetEvent() .Subscribe(OnBusyIndicatorNotification, ThreadOption.PublisherThread, true); //this is a hack, the app startup is calling this, then the app itself, we only want the //app itself to be handling the listeners if (_bAddListeners) { _eventAggregator.GetEvent().Subscribe(OnTextPasted); } _bAddListeners = true; } private void OnTextPasted(ITextPastedEventArgs args) { if (args.Id != ExtraPropertyModel.PASTE_ID) { return; } if (!(args.Sender is IExtraProperty iep)) { return; } Paste(iep, args.Text, args.Tag); var last = ExtraProperties.Last(); if (!string.IsNullOrWhiteSpace(last.Key) || !string.IsNullOrWhiteSpace(last.Value)) { MarkModified(last); } _eventAggregator.GetEvent() .Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, Page)); } private void OnBusyIndicatorNotification(bool eventArg) { IsBusy = eventArg; } private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle) { // The NotificationRequest.Raise triggers the Invoke() method of the PopupWindowAction object to show the NotificationWindow window // Notification object expects a NotificationContentEventArgsWithoutTitle object and a Title string. var eventArgsWithoutTitle = new NotificationContentEventArgs(eventArgsWithTitle.Message, "", eventArgsWithTitle.Image, string.Empty); NotificationRequest.Raise(new Notification { Content = eventArgsWithoutTitle, Title = eventArgsWithTitle.Title }); } #endregion /// /// /// Occurs when a property value changes. /// public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); if ("ExtraProperties" == propertyName) { _eventAggregator.GetEvent() .Publish(new ExtraPropertiesChangedEventArgs(AllExtraProperties.Where(exp => !string.IsNullOrEmpty(exp.Key)).ToList(), this, Parent)); } } public void SetPage(IDataPROPage page) { Page = page; } public void SetParent(object parent) { Parent = parent; } public void Cleanup() { } public Task CleanupAsync() { return Task.CompletedTask; } public void Initialize() { } public void Initialize(object parameter) { } public void Initialize(object parameter, object model) { } public Task InitializeAsync() { return Task.CompletedTask; } public Task InitializeAsync(object parameter) { return Task.CompletedTask; } public void Activated() { } internal void MarkModified(IExtraProperty iep) { if (ExtraProperties.ToList().Last() == iep) { var newXP = new ExtraPropertyModel(); ExtraProperties.Add(newXP); AllExtraProperties.Add(newXP); } OnPropertyChanged("ExtraProperties"); _eventAggregator.GetEvent() .Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, Page)); } public void Filter(object tag, string term) { if (!(tag is string s)) { return; } switch (s) { case "Key": _searchTermForField[Fields.Key] = term; Filter(); break; case "Value": _searchTermForField[Fields.Key] = term; Filter(); break; } } public void Sort(object columnTag, bool columnClick) { if (!(columnTag is string s)) { return; } switch (s) { case "Key": Sort(Fields.Key, true); Filter(); break; case "Value": Sort(Fields.Value, true); Filter(); break; } } private void Sort(Fields field, bool bColumnClick) { if (bColumnClick) { if (field != _sortField) { _sortField = field; _sortAscending = true; } else { _sortAscending = !_sortAscending; } } var comparer = new PropertyComparer(); comparer.SortField = _sortField; comparer.SortAscending = _sortAscending; AllExtraProperties.Sort(comparer); } public enum Fields { Key, Value } private Fields _sortField = Fields.Key; private bool _sortAscending = true; private void Filter() { ExtraProperties.Clear(); foreach (var extraProperty in AllExtraProperties) { if (string.IsNullOrWhiteSpace(extraProperty.Key) && string.IsNullOrWhiteSpace(extraProperty.Value)) { ExtraProperties.Add(extraProperty); continue; } if (_searchTermForField.ContainsKey(Fields.Value)) { var term = _searchTermForField[Fields.Value]; if (!string.IsNullOrEmpty(term)) { if (extraProperty.Value.IndexOf(term, StringComparison.CurrentCultureIgnoreCase) < 0) { continue; } } } if (_searchTermForField.ContainsKey(Fields.Key)) { var term = _searchTermForField[Fields.Key]; if (!string.IsNullOrWhiteSpace(term)) { if (extraProperty.Key.IndexOf(term, StringComparison.CurrentCultureIgnoreCase) < 0) { continue; } } } ExtraProperties.Add(extraProperty); } } internal void Validate(ref List errors, ref List warnings) { var repeatKeys = AllExtraProperties.GroupBy(exp => exp.Key).Where(grp => grp.Count() > 1).Select(exp => exp.Key).ToArray(); if (repeatKeys.Length > 0) { var error = StringResources.RepeatKeyError + Environment.NewLine + string.Join(Environment.NewLine, repeatKeys); errors.Add(error); } var noValueKeys = AllExtraProperties.Where(exp => string.IsNullOrWhiteSpace(exp.Value)).Select(exp => exp.Key).ToArray(); if (noValueKeys.Length > 0) { var warning = StringResources.NoValueWarning + Environment.NewLine + string.Join(Environment.NewLine, noValueKeys); warnings.Add(warning); } } public bool Validate(ref List errors) { var before = errors.Count; var notUsed = new List(); Validate(ref errors, ref notUsed); return before == errors.Count; } private readonly Dictionary _searchTermForField = new Dictionary(); private IExtraProperty[] _selectedItems = null; public IDataPROPage Page { get; private set; } public object Parent { get; private set; } public bool IsDirty { get; private set; } private bool _isBusy = false; public bool IsBusy { get => _isBusy; set { _isBusy = value; OnPropertyChanged("IsBusy"); } } private bool _isMenuIncluded; private void NotifySelectionChanged(int count) { _eventAggregator.GetEvent().Publish(new PageSelectionChangedArg(count, Page)); } internal void SetSelection(IExtraProperty[] ieps) { _selectedItems = ieps; NotifySelectionChanged(_selectedItems?.Length ?? 0); } public bool IsMenuIncluded { get => _isMenuIncluded; set { _isMenuIncluded = value; OnPropertyChanged("IsMenuIncluded"); } } private bool _isNavigationIncluded; public bool IsNavigationIncluded { get => _isNavigationIncluded; set { _isNavigationIncluded = value; OnPropertyChanged("IsNavigationIncluded"); } } //FB14098: Ability to disable editing View if user doesn't have the permissions, but still view current data private bool _isReadOnly; public bool IsReadOnly { get => _isReadOnly; set { _isReadOnly = value; OnPropertyChanged("IsReadOnly"); } } public List AllExtraProperties { get; set; } = new List(); public ObservableCollection ExtraProperties { get; set; } = new ObservableCollection(); public IExtraProperty[] SelectedProperties => throw new NotImplementedException(); private void Paste(IExtraProperty extraProperty, string text, object tag) { bool bKey = tag.Equals("ExtraProperty"); var extraProperties = ParseText(text, bKey, out var oneColumn); int index = ExtraProperties.IndexOf(extraProperty); foreach (var extraproperty in extraProperties) { if (index > (ExtraProperties.Count - 1)) { ExtraProperties.Add(extraProperty); index++; } else { var exp = ExtraProperties[index++]; if (oneColumn) { exp.Key = bKey ? extraproperty.Key : exp.Key; exp.Value = bKey ? exp.Value : extraproperty.Value; } else { exp.Key = extraproperty.Key; exp.Value = extraproperty.Value; } } } Sort(_sortField, false); Filter(); } private IExtraProperty[] ParseText(string text, bool bKey, out bool oneColumn) { oneColumn = false; var list = new List(); var errors = new List(); var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; if (string.IsNullOrWhiteSpace(line)) { continue; } var tokens = line.Split(','); if (tokens.Length != 2) { tokens = line.Split('\t'); } if (tokens.Length != 2) { tokens = line.Split(';'); } var newProperty = new ExtraPropertyModel(); if (tokens.Length == 1) { oneColumn = true; if (bKey) { newProperty.Key = tokens[0]; newProperty.Value = ""; } else { newProperty.Value = tokens[0]; newProperty.Key = ""; } } else if (tokens.Length == 2) { newProperty.Key = tokens[0]; newProperty.Value = tokens[1]; } else { errors.Add(string.Format(Resources.StringResources.InvalidLine, 1 + i)); continue; } list.Add(newProperty); } return list.ToArray(); } public void SetExtraProperties(IList properties) { AllExtraProperties.Clear(); AllExtraProperties.AddRange(properties.Select(iep => new ExtraPropertyModel(iep))); AllExtraProperties.Add(new ExtraPropertyModel()); _sortField = Fields.Key; _sortAscending = true; Sort(_sortField, false); Filter(); } public void CopySelected() { if (null == _selectedItems) { return; } foreach (var item in _selectedItems) { AllExtraProperties.Insert(AllExtraProperties.Count - 1, new ExtraPropertyModel(item)); } Sort(_sortField, false); Filter(); _eventAggregator.GetEvent() .Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, Page)); } public void DeleteSelected() { if (null == _selectedItems) { return; } bool bAddLast = false; foreach (var exp in _selectedItems) { if (ExtraProperties.Last() == exp) { bAddLast = true; } ExtraProperties.Remove(exp); AllExtraProperties.Remove(exp); } if (bAddLast) { var newXP = new ExtraPropertyModel(); ExtraProperties.Add(newXP); AllExtraProperties.Add(newXP); } OnPropertyChanged("ExtraProperties"); _eventAggregator.GetEvent() .Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, Page)); } public class PropertyComparer : IComparer { public Fields SortField { get; set; } public bool SortAscending { get; set; } public int Compare(IExtraProperty left, IExtraProperty right) { if (left == right) { return 0; } var a = left; var b = right; if (string.IsNullOrWhiteSpace(a.Key) && string.IsNullOrWhiteSpace(a.Value)) { return 1; } if (string.IsNullOrWhiteSpace(b.Key) && string.IsNullOrWhiteSpace(b.Value)) { return -1; } if (!SortAscending) { a = right; b = left; } if (null == a) { return -1; } if (null == b) { return 1; } switch (SortField) { case Fields.Key: return DTS.Common.Utilities.NaturalStringComparer.StaticCompare(a.Key.ToUpper(), b.Key.ToUpper()); case Fields.Value: return DTS.Common.Utilities.NaturalStringComparer.StaticCompare(a.Value.ToUpper(), b.Value.ToUpper()); default: throw new ArgumentOutOfRangeException(); } } } } }