diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs index c09736df7..ef1261628 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionList.cs @@ -43,7 +43,7 @@ namespace Artemis.Core /// /// Gets the path of the list property /// - public DataModelPath? ListPath { get; set; } + public DataModelPath? ListPath { get; private set; } /// /// Gets the type of the content of the list this predicate is evaluated on @@ -82,15 +82,15 @@ namespace Artemis.Core throw new ArtemisCoreException("Cannot update list to an invalid path"); ListPath?.Dispose(); - ListPath = path; + ListPath = path != null ? new DataModelPath(path) : null; // Remove the old root group that was tied to the old data model while (Children.Any()) RemoveChild(Children[0]); - if (path != null) + if (ListPath != null) { - Type listType = path.GetPropertyType()!; + Type listType = ListPath.GetPropertyType()!; ListType = listType.GetGenericArguments()[0]; IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string); @@ -99,7 +99,6 @@ namespace Artemis.Core } else { - ListPath = null; ListType = null; } } @@ -171,12 +170,21 @@ namespace Artemis.Core // Ensure the list path is valid and points to a list DataModelPath listPath = new DataModelPath(null, Entity.ListPath); Type listType = listPath.GetPropertyType()!; - if (!listPath.IsValid || !typeof(IList).IsAssignableFrom(listType)) + // Can't check this on an invalid list, if it becomes valid later lets hope for the best + if (listPath.IsValid && !typeof(IList).IsAssignableFrom(listType)) return; ListPath = listPath; - ListType = listType.GetGenericArguments()[0]; - IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string); + if (ListPath.IsValid) + { + ListType = listType.GetGenericArguments()[0]; + IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string); + } + else + { + ListType = null; + IsPrimitiveList = false; + } // There should only be one child and it should be a group if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup) diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs index e53b29698..baabcf6be 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionListPredicate.cs @@ -54,9 +54,15 @@ namespace Artemis.Core /// public DataModelConditionList DataModelConditionList { get; private set; } - public DataModelPath? LeftPath { get; set; } + /// + /// Gets the path of the left property + /// + public DataModelPath? LeftPath { get; private set; } - public DataModelPath? RightPath { get; set; } + /// + /// Gets the path of the right property + /// + public DataModelPath? RightPath { get; private set; } /// /// Gets the right static value, only used it is @@ -70,17 +76,17 @@ namespace Artemis.Core /// Updates the left side of the predicate /// /// The path pointing to the left side value inside the list - public void UpdateLeftSide(string? path) + public void UpdateLeftSide(DataModelPath? path) { if (DataModelConditionList.IsPrimitiveList) throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list"); + if (path != null && !path.IsValid) + throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path"); + LeftPath?.Dispose(); - if (path != null && DataModelConditionList.ListType != null) - LeftPath = new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), path); - else - LeftPath = null; - + LeftPath = path != null ? new DataModelPath(path) : null; + ValidateOperator(); ValidateRightSide(); } @@ -92,8 +98,11 @@ namespace Artemis.Core /// The path pointing to the right side value inside the list public void UpdateRightSideDynamicList(DataModelPath? path) { + if (path != null && !path.IsValid) + throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path"); + RightPath?.Dispose(); - RightPath = path; + RightPath = path != null ? new DataModelPath(path) : null; PredicateType = ListRightSideType.DynamicList; } diff --git a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs index 12e7a5b6f..c37ebf1c1 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DataModelConditionPredicate.cs @@ -48,12 +48,12 @@ namespace Artemis.Core /// /// Gets the path of the left property /// - public DataModelPath? LeftPath { get; set; } + public DataModelPath? LeftPath { get; private set; } /// /// Gets the path of the right property /// - public DataModelPath? RightPath { get; set; } + public DataModelPath? RightPath { get; private set; } /// /// Gets the right static value, only used it is @@ -66,27 +66,14 @@ namespace Artemis.Core /// /// Updates the left side of the predicate /// - /// The data model of the left side value /// The path pointing to the left side value inside the data model - public void UpdateLeftSide(DataModel? dataModel, string? path) + public void UpdateLeftSide(DataModelPath? path) { - if (dataModel != null && path == null) - throw new ArtemisCoreException("If a data model is provided, a path is also required"); - if (dataModel == null && path != null) - throw new ArtemisCoreException("If path is provided, a data model is also required"); + if (path != null && !path.IsValid) + throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path"); LeftPath?.Dispose(); - if (dataModel != null) - { - DataModelPath newPath = new DataModelPath(dataModel, path); - if (!newPath.IsValid) - throw new ArtemisCoreException($"New left path '{newPath}' is invalid"); - LeftPath = newPath; - } - else - { - LeftPath = null; - } + LeftPath = path != null ? new DataModelPath(path) : null; ValidateOperator(); ValidateRightSide(); @@ -95,27 +82,14 @@ namespace Artemis.Core /// /// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression /// - /// The data model of the right side value /// The path pointing to the right side value inside the data model - public void UpdateRightSide(DataModel? dataModel, string? path) + public void UpdateRightSideDynamic(DataModelPath? path) { - if (dataModel != null && path == null) - throw new ArtemisCoreException("If a data model is provided, a path is also required"); - if (dataModel == null && path != null) - throw new ArtemisCoreException("If path is provided, a data model is also required"); + if (path != null && !path.IsValid) + throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path"); RightPath?.Dispose(); - if (dataModel != null) - { - DataModelPath newPath = new DataModelPath(dataModel, path); - if (!newPath.IsValid) - throw new ArtemisCoreException($"New right path '{newPath}' is invalid"); - RightPath = newPath; - } - else - { - RightPath = null; - } + RightPath = path != null ? new DataModelPath(path) : null; PredicateType = ProfileRightSideType.Dynamic; } @@ -124,7 +98,7 @@ namespace Artemis.Core /// Updates the right side of the predicate, makes the predicate static and re-compiles the expression /// /// The right side value to use - public void UpdateRightSide(object? staticValue) + public void UpdateRightSideStatic(object? staticValue) { PredicateType = ProfileRightSideType.Static; RightPath?.Dispose(); @@ -253,7 +227,7 @@ namespace Artemis.Core ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved; // Left side - if (Entity.LeftPath != null) + if (Entity.LeftPath != null) LeftPath = new DataModelPath(null, Entity.LeftPath); // Operator @@ -288,12 +262,12 @@ namespace Artemis.Core rightSideValue = Activator.CreateInstance(leftSideType); } - UpdateRightSide(rightSideValue); + UpdateRightSideStatic(rightSideValue); } else { // Hope for the best... - UpdateRightSide(JsonConvert.DeserializeObject(Entity.RightStaticValue)); + UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue)); } } catch (JsonReaderException) @@ -328,14 +302,14 @@ namespace Artemis.Core Type rightSideType = RightPath.GetPropertyType()!; if (leftType != null && !leftType.IsCastableFrom(rightSideType)) - UpdateRightSide(null, null); + UpdateRightSideDynamic(null); } else { if (RightStaticValue != null && (leftType == null || leftType.IsCastableFrom(RightStaticValue.GetType()))) - UpdateRightSide(RightStaticValue); + UpdateRightSideStatic(RightStaticValue); else - UpdateRightSide(null); + UpdateRightSideStatic(null); } } diff --git a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs index b98fcdcb4..78cc14677 100644 --- a/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs +++ b/src/Artemis.Core/Models/Profile/DataModel/DataModelPath.cs @@ -14,6 +14,7 @@ namespace Artemis.Core /// public class DataModelPath : IStorageModel, IDisposable { + private bool _disposed; private readonly LinkedList _segments; private Expression>? _accessorLambda; @@ -52,6 +53,26 @@ namespace Artemis.Core SubscribeToDataModelStore(); } + /// + /// Creates a new instance of the class based on an existing path + /// + /// The path to base the new instance on + public DataModelPath(DataModelPath dataModelPath) + { + if (dataModelPath == null) + throw new ArgumentNullException(nameof(dataModelPath)); + + Target = dataModelPath.Target; + Path = dataModelPath.Path; + Entity = new DataModelPathEntity(); + + _segments = new LinkedList(); + + Save(); + Initialize(); + SubscribeToDataModelStore(); + } + internal DataModelPath(DataModel? target, DataModelPathEntity entity) { Target = target; @@ -65,26 +86,11 @@ namespace Artemis.Core SubscribeToDataModelStore(); } - internal DataModelPath(DataModelPath dataModelPath) - { - Target = dataModelPath.Target; - Path = dataModelPath.Path; - Entity = new DataModelPathEntity(); - - _segments = new LinkedList(); - - Save(); - Initialize(); - SubscribeToDataModelStore(); - } - /// /// Gets the data model at which this path starts /// public DataModel? Target { get; private set; } - internal DataModelPathEntity Entity { get; } - /// /// Gets the data model GUID of the if it is a /// @@ -108,7 +114,10 @@ namespace Artemis.Core /// /// Gets a boolean indicating whether this data model path points to a list /// - public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null && typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType()); + public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null && + typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType()); + + internal DataModelPathEntity Entity { get; } internal Func? Accessor { get; private set; } @@ -117,6 +126,9 @@ namespace Artemis.Core /// public object? GetValue() { + if (_disposed) + throw new ObjectDisposedException("DataModelPath"); + if (_accessorLambda == null || Target == null) return null; @@ -132,6 +144,9 @@ namespace Artemis.Core /// If static, the property info. If dynamic, null public PropertyInfo? GetPropertyInfo() { + if (_disposed) + throw new ObjectDisposedException("DataModelPath"); + return Segments.LastOrDefault()?.GetPropertyInfo(); } @@ -141,6 +156,9 @@ namespace Artemis.Core /// If possible, the property type public Type? GetPropertyType() { + if (_disposed) + throw new ObjectDisposedException("DataModelPath"); + return Segments.LastOrDefault()?.GetPropertyType(); } @@ -150,6 +168,9 @@ namespace Artemis.Core /// If found, the data model property description public DataModelPropertyAttribute? GetPropertyDescription() { + if (_disposed) + throw new ObjectDisposedException("DataModelPath"); + return Segments.LastOrDefault()?.GetPropertyDescription(); } @@ -186,7 +207,9 @@ namespace Artemis.Core for (int index = 0; index < segments.Length; index++) { string identifier = segments[index]; - LinkedListNode node = _segments.AddLast(new DataModelPathSegment(this, identifier, string.Join('.', segments.Take(index + 1)))); + LinkedListNode node = _segments.AddLast( + new DataModelPathSegment(this, identifier, string.Join('.', segments.Take(index + 1))) + ); node.Value.Node = node; } } @@ -224,6 +247,21 @@ namespace Artemis.Core DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved; } + #region IDisposable + + /// + public void Dispose() + { + _disposed = true; + + DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; + DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; + + Invalidate(); + } + + #endregion + #region Storage /// @@ -238,25 +276,16 @@ namespace Artemis.Core /// public void Save() { + // Do not save an invalid state + if (!IsValid) + return; + Entity.Path = Path; Entity.DataModelGuid = DataModelGuid; } #endregion - #region IDisposable - - /// - public void Dispose() - { - DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded; - DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved; - - Invalidate(); - } - - #endregion - #region Event handlers private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e) diff --git a/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs b/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs index 2d9f9200e..0ff1e40e3 100644 --- a/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs +++ b/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs @@ -15,16 +15,12 @@ namespace Artemis.UI.Shared else direction = (Parameters) Enum.Parse(typeof(Parameters), (string) parameter); - if (direction == Parameters.Normal) - { - if (value == null) - return Visibility.Collapsed; - return Visibility.Visible; - } + if (value is string stringValue && string.IsNullOrWhiteSpace(stringValue)) + value = null; - if (value == null) - return Visibility.Visible; - return Visibility.Collapsed; + if (direction == Parameters.Normal) + return value == null ? Visibility.Collapsed : Visibility.Visible; + return value == null ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml index 972cb9c74..d2b351696 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:shared="clr-namespace:Artemis.UI.Shared" xmlns:input="clr-namespace:Artemis.UI.Shared.Input" + xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance input:DataModelDynamicViewModel}"> @@ -30,7 +31,7 @@ diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs index be37a3ad5..79f00613b 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs @@ -83,15 +83,30 @@ namespace Artemis.UI.Shared.Input public DataModelPath DataModelPath { - private get => _dataModelPath; - set + get => _dataModelPath; + private set { if (!SetAndNotify(ref _dataModelPath, value)) return; + NotifyOfPropertyChange(nameof(IsValid)); NotifyOfPropertyChange(nameof(DisplayValue)); + NotifyOfPropertyChange(nameof(DisplayPath)); } } - public string DisplayValue => DataModelPath.GetPropertyDescription()?.Name ?? DataModelPath.Segments.LastOrDefault()?.Identifier; + public bool IsValid => DataModelPath?.IsValid ?? true; + public string DisplayValue => DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier; + + public string DisplayPath + { + get + { + if (DataModelPath == null) + return "Click to select a property"; + if (!DataModelPath.IsValid) + return "Invalid path"; + return string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier)); + } + } public void ChangeDataModel(DataModelPropertiesViewModel dataModel) { @@ -104,6 +119,12 @@ namespace Artemis.UI.Shared.Input DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; } + public void ChangeDataModelPath(DataModelPath dataModelPath) + { + DataModelPath?.Dispose(); + DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null; + } + private void Initialize() { // Get the data models @@ -130,7 +151,7 @@ namespace Artemis.UI.Shared.Input if (!(context is DataModelVisualizationViewModel selected)) return; - DataModelPath = selected.DataModelPath; + ChangeDataModelPath(selected.DataModelPath); OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath)); } @@ -139,6 +160,8 @@ namespace Artemis.UI.Shared.Input _updateTimer.Stop(); _updateTimer.Dispose(); _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; + + DataModelPath?.Dispose(); } #region Events diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml index e11580842..6bc1c6987 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml @@ -32,13 +32,7 @@ Visibility="{Binding InputViewModel, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}"> - - diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs index 6174a6b2a..aaabcd5bd 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs @@ -23,14 +23,15 @@ namespace Artemis.UI.Shared.Input private object _value; private bool _isEnabled; - internal DataModelStaticViewModel(Type targetType, IDataModelUIService dataModelUIService) + internal DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService) { _dataModelUIService = dataModelUIService; _rootView = Application.Current.Windows.OfType().SingleOrDefault(x => x.IsActive); TargetType = targetType; + TargetDescription = targetDescription; IsEnabled = TargetType != null; - DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true); + DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true); if (_rootView != null) { @@ -117,7 +118,7 @@ namespace Artemis.UI.Shared.Input public void UpdateTargetType(Type target) { TargetType = target; - DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true); + DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true); IsEnabled = TargetType != null; // If null, clear the input diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs index 42767fd67..4d6a1b9db 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListPropertyViewModel.cs @@ -48,7 +48,7 @@ namespace Artemis.UI.Shared ((ListPredicateWrapperDataModel)DataModel).UntypedValue = DisplayValue; if (DisplayViewModel == null) - DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), true); + DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true); ListType = DisplayValue.GetType(); UpdateDisplayParameters(); diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs index c4c337e96..a77ffd163 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelListViewModel.cs @@ -105,7 +105,7 @@ namespace Artemis.UI.Shared protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType) { // If a display VM was found, prefer to use that in any case - DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType); + DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription); if (typeViewModel != null) return new DataModelListPropertyViewModel(listType, typeViewModel); // For primitives, create a property view model, it may be null that is fine diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs index d36412d6b..558e7c54e 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelPropertyViewModel.cs @@ -40,7 +40,7 @@ namespace Artemis.UI.Shared if (DisplayViewModel == null) { - DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DataModelPath.GetPropertyType(), true); + DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DataModelPath.GetPropertyType(), PropertyDescription, true); DisplayViewModel.PropertyDescription = DataModelPath.GetPropertyDescription(); } diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 69faf8fe0..f6bcb010d 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -251,7 +251,7 @@ namespace Artemis.UI.Shared return null; // If a display VM was found, prefer to use that in any case - DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType); + DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription); if (typeViewModel != null) return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth}; // For primitives, create a property view model, it may be null that is fine diff --git a/src/Artemis.UI.Shared/Services/DataModelUIService.cs b/src/Artemis.UI.Shared/Services/DataModelUIService.cs index efeab96c2..db31a09d4 100644 --- a/src/Artemis.UI.Shared/Services/DataModelUIService.cs +++ b/src/Artemis.UI.Shared/Services/DataModelUIService.cs @@ -159,14 +159,24 @@ namespace Artemis.UI.Shared.Services } } - public DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, bool fallBackToDefault) + public DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault) { lock (_registeredDataModelDisplays) { + DataModelDisplayViewModel result; + DataModelVisualizationRegistration match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType); if (match != null) - return (DataModelDisplayViewModel) match.PluginInfo.Kernel.Get(match.ViewModelType); - return !fallBackToDefault ? null : _kernel.Get(); + result = (DataModelDisplayViewModel) match.PluginInfo.Kernel.Get(match.ViewModelType); + else if (!fallBackToDefault) + result = null; + else + result = _kernel.Get(); + + if (result != null) + result.PropertyDescription = description; + + return result; } } @@ -199,9 +209,9 @@ namespace Artemis.UI.Shared.Services return _kernel.Get(new ConstructorArgument("module", module)); } - public DataModelStaticViewModel GetStaticInputViewModel(Type targetType) + public DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription) { - return _kernel.Get(new ConstructorArgument("targetType", targetType)); + return _kernel.Get(new ConstructorArgument("targetType", targetType), new ConstructorArgument("targetDescription", targetDescription)); } private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute description, object initialValue) @@ -219,7 +229,7 @@ namespace Artemis.UI.Shared.Services IParameter[] parameters = new IParameter[] { - new ConstructorArgument("description", description), + new ConstructorArgument("targetDescription", description), new ConstructorArgument("initialValue", initialValue) }; DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.PluginInfo.Kernel.Get(registration.ViewModelType, parameters); diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs index 198dbb47d..7ad914a33 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs @@ -19,10 +19,10 @@ namespace Artemis.UI.Shared.Services void RemoveDataModelInput(DataModelVisualizationRegistration registration); void RemoveDataModelDisplay(DataModelVisualizationRegistration registration); - DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, bool fallBackToDefault = false); + DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault = false); DataModelInputViewModel GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute description, object initialValue, Action updateCallback); DataModelDynamicViewModel GetDynamicSelectionViewModel(Module module); - DataModelStaticViewModel GetStaticInputViewModel(Type targetType); + DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription); } } \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/DataModel/Input/DoubleDataModelInputView.xaml b/src/Artemis.UI/DefaultTypes/DataModel/Input/DoubleDataModelInputView.xaml index 7daddf3ee..cd487c12e 100644 --- a/src/Artemis.UI/DefaultTypes/DataModel/Input/DoubleDataModelInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/DataModel/Input/DoubleDataModelInputView.xaml @@ -6,9 +6,15 @@ xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:s="https://github.com/canton7/Stylet" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + diff --git a/src/Artemis.UI/DefaultTypes/DataModel/Input/IntDataModelInputView.xaml b/src/Artemis.UI/DefaultTypes/DataModel/Input/IntDataModelInputView.xaml index 2a0aa7691..eae99304e 100644 --- a/src/Artemis.UI/DefaultTypes/DataModel/Input/IntDataModelInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/DataModel/Input/IntDataModelInputView.xaml @@ -6,9 +6,15 @@ xmlns:b="http://schemas.microsoft.com/xaml/behaviors" xmlns:s="https://github.com/canton7/Stylet" xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs index ad2afb3d5..bad970ea8 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs @@ -109,11 +109,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions NotifyOfPropertyChange(nameof(SelectedBooleanOperator)); // Remove VMs of effects no longer applied on the layer - List toRemove = Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList(); - // Using RemoveRange breaks our lovely animations - foreach (DataModelConditionViewModel DataModelConditionViewModel in toRemove) - Items.Remove(DataModelConditionViewModel); + Items.RemoveRange(Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList()); + List viewModels = new List(); foreach (DataModelConditionPart childModel in Model.Children) { if (Items.Any(c => c.Model == childModel)) @@ -122,21 +120,23 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions switch (childModel) { case DataModelConditionGroup DataModelConditionGroup: - Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataModelConditionGroup, IsListGroup)); + viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataModelConditionGroup, IsListGroup)); break; case DataModelConditionList DataModelConditionListPredicate: - Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(DataModelConditionListPredicate)); + viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(DataModelConditionListPredicate)); break; case DataModelConditionPredicate DataModelConditionPredicate: if (!IsListGroup) - Items.Add(_dataModelConditionsVmFactory.DataModelConditionPredicateViewModel(DataModelConditionPredicate)); + viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionPredicateViewModel(DataModelConditionPredicate)); break; case DataModelConditionListPredicate DataModelConditionListPredicate: if (IsListGroup) - Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(DataModelConditionListPredicate)); + viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(DataModelConditionListPredicate)); break; } } + if (viewModels.Any()) + Items.AddRange(viewModels); foreach (DataModelConditionViewModel childViewModel in Items) childViewModel.Update(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs index 108e7f21b..7f8563952 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListPredicateViewModel.cs @@ -120,7 +120,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions // Lists use a different color LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188)); LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray(); - LeftSideSelectionViewModel.DataModelPath = DataModelConditionListPredicate.LeftPath; + LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.LeftPath); } Type leftSideType = _isPrimitiveList @@ -155,14 +155,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions } RightSideSelectionViewModel.FilterTypes = new[] {leftSideType}; - LeftSideSelectionViewModel.DataModelPath = DataModelConditionListPredicate.RightPath; + RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.RightPath); } else if (SelectedOperator.SupportsRightSide) { DisposeRightSideDynamic(); if (RightSideInputViewModel == null) { - RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType); + RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription()); RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue; RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; @@ -175,7 +175,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public void ApplyLeftSide() { - DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath.Path); + DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath); _profileEditorService.UpdateSelectedProfileElement(); SelectedOperator = DataModelConditionListPredicate.Operator; diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs index 93fefcbad..5b191e8f8 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionListViewModel.cs @@ -104,15 +104,16 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public override void Update() { - TargetSelectionViewModel.DataModelPath = DataModelConditionList.ListPath; + TargetSelectionViewModel.ChangeDataModelPath(DataModelConditionList.ListPath); NotifyOfPropertyChange(nameof(SelectedListOperator)); // Remove VMs of effects no longer applied on the layer - List toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList(); - // Using RemoveRange breaks our lovely animations - foreach (DataModelConditionViewModel conditionViewModel in toRemove) - Items.Remove(conditionViewModel); + Items.RemoveRange(Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList()); + + if (DataModelConditionList.ListPath == null || !DataModelConditionList.ListPath.IsValid) + return; + List viewModels = new List(); foreach (DataModelConditionPart childModel in Model.Children) { if (Items.Any(c => c.Model == childModel)) @@ -122,8 +123,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, true); viewModel.IsRootGroup = true; - Items.Add(viewModel); + viewModels.Add(viewModel); } + if (viewModels.Any()) + Items.AddRange(viewModels); foreach (DataModelConditionViewModel childViewModel in Items) childViewModel.Update(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs index 52055076b..15ae774a8 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs @@ -117,11 +117,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public override void Update() { LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray(); - LeftSideSelectionViewModel.PopulateSelectedPropertyViewModel( - DataModelConditionPredicate.LeftPath?.Target, - DataModelConditionPredicate.LeftPath?.Path - ); - Type leftSideType = LeftSideSelectionViewModel.SelectedPropertyViewModel?.DataModelPath?.GetPropertyType(); + LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.LeftPath); + + Type leftSideType = LeftSideSelectionViewModel.DataModelPath?.GetPropertyType(); // Get the supported operators Operators.Clear(); @@ -146,10 +144,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected; } - RightSideSelectionViewModel.PopulateSelectedPropertyViewModel( - DataModelConditionPredicate.RightPath?.Target, - DataModelConditionPredicate.RightPath?.Path - ); + RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath); RightSideSelectionViewModel.FilterTypes = new[] {leftSideType}; } else if (SelectedOperator.SupportsRightSide) @@ -157,7 +152,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions DisposeRightSideDynamic(); if (RightSideInputViewModel == null) { - RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType); + RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription()); RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush"); RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; } @@ -170,10 +165,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public void ApplyLeftSide() { - DataModelConditionPredicate.UpdateLeftSide( - LeftSideSelectionViewModel.SelectedPropertyViewModel.DataModel, - LeftSideSelectionViewModel.SelectedPropertyViewModel.Path - ); + DataModelConditionPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath); _profileEditorService.UpdateSelectedProfileElement(); SelectedOperator = DataModelConditionPredicate.Operator; @@ -182,10 +174,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public void ApplyRightSideDynamic() { - DataModelConditionPredicate.UpdateRightSide( - RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel, - RightSideSelectionViewModel.SelectedPropertyViewModel.Path - ); + DataModelConditionPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath); _profileEditorService.UpdateSelectedProfileElement(); Update(); @@ -193,7 +182,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions public void ApplyRightSideStatic(object value) { - DataModelConditionPredicate.UpdateRightSide(value); + DataModelConditionPredicate.UpdateRightSideStatic(value); _profileEditorService.UpdateSelectedProfileElement(); Update(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs index 41cb52dd2..89b2a4e96 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs @@ -27,7 +27,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio ActiveItem.Update(); ActiveItem.Updated += ActiveItemOnUpdated; - ValueViewModel = dataModelUIService.GetStaticInputViewModel(typeof(TProperty)); + ValueViewModel = dataModelUIService.GetStaticInputViewModel(typeof(TProperty), null); ValueViewModel.ValueUpdated += ValueViewModelOnValueUpdated; ValueViewModel.Value = DataBindingCondition.Value; } diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 31b5f07be..0de5a854f 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -42,8 +42,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings DataBindingModes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType))); EasingViewModels = new BindableCollection(); - TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true); - TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true); + TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); + TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); Initialize(); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs index 52996caa4..de58da9d9 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs @@ -145,7 +145,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa else { DynamicSelectionViewModel = null; - StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.ParameterType ?? sourceType); + StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.ParameterType ?? sourceType, null); if (StaticInputViewModel != null) StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated; } @@ -166,10 +166,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa SelectedModifierType = Modifier.ModifierType; // Parameter - if (DynamicSelectionViewModel != null) - DynamicSelectionViewModel.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath); - else if (StaticInputViewModel != null) - StaticInputViewModel.Value = Modifier.ParameterStaticValue; + throw new NotImplementedException(); + // if (DynamicSelectionViewModel != null) + // DynamicSelectionViewModel.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath); + // else if (StaticInputViewModel != null) + // StaticInputViewModel.Value = Modifier.ParameterStaticValue; } private void ExecuteSelectModifierTypeCommand(object context) diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs index 67abde326..a74d645fd 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs @@ -56,7 +56,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa public void Update() { - TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DirectDataBinding.SourceDataModel, DirectDataBinding.SourcePropertyPath); + throw new NotImplementedException(); + // TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DirectDataBinding.SourceDataModel, DirectDataBinding.SourcePropertyPath); TargetSelectionViewModel.FilterTypes = new[] {DirectDataBinding.DataBinding.GetTargetType()}; CanAddModifier = DirectDataBinding.SourceDataModel != null; @@ -65,7 +66,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa public object GetTestValue() { - return TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); + throw new NotImplementedException(); + // return TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); } #region IDisposable @@ -112,7 +114,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) { - DirectDataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.Path); + throw new NotImplementedException(); + // DirectDataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.Path); Update(); _profileEditorService.UpdateSelectedProfileElement();