From b73ea53622d3efa9ca891b37dabd34776e582d45 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 23 Sep 2020 19:26:44 +0200 Subject: [PATCH] Databindings - Added conditional databindings Conditions - Added easy method for null-handling in operators --- .../StringContainsConditionOperator.cs | 6 +- .../StringEndsWithConditionOperator.cs | 6 +- .../StringNotContainsConditionOperator.cs | 6 +- .../StringStartsWithConditionOperator.cs | 6 +- .../Profile/Conditions/ConditionOperator.cs | 29 ++++ .../Profile/DataBindings/DataBinding.cs | 9 +- .../Modes/ConditionalDataBinding.cs | 9 +- .../Modes/DataBindingCondition.cs | 19 +-- .../DataBindings/Modes/DataBindingModifier.cs | 4 +- .../DataBindings/Modes/DirectDataBinding.cs | 5 +- src/Artemis.Core/Services/ModuleService.cs | 3 + .../Input/DataModelDynamicView.xaml | 3 +- .../Input/DataModelStaticView.xaml | 8 +- .../BindableCollectionExtensions.cs | 20 +++ .../Ninject/Factories/IVMFactory.cs | 3 +- .../DataModelConditionGroupViewModel.cs | 9 ++ .../DataModelConditionPredicateViewModel.cs | 1 + .../ConditionalDataBindingModeView.xaml | 83 +++++++++-- .../ConditionalDataBindingModeViewModel.cs | 106 +++++++++++++- .../DataBindingConditionView.xaml | 27 ++++ .../DataBindingConditionViewModel.cs | 56 ++++++++ .../DataBindings/DataBindingView.xaml | 135 +++++++++--------- .../DataBindingModifierView.xaml | 2 +- .../DataBindingModifierViewModel.cs | 2 + .../DirectDataBindingModeView.xaml | 4 +- .../DirectDataBindingModeViewModel.cs | 64 +++++++-- 26 files changed, 491 insertions(+), 134 deletions(-) create mode 100644 src/Artemis.UI/Extensions/BindableCollectionExtensions.cs create mode 100644 src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionView.xaml create mode 100644 src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringContainsConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringContainsConditionOperator.cs index a2755c3d1..a16c90e03 100644 --- a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringContainsConditionOperator.cs +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringContainsConditionOperator.cs @@ -23,7 +23,11 @@ namespace Artemis.Core.DefaultTypes public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide) { - return Expression.Equal(Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)), Expression.Constant(true)); + var contains = Expression.Equal( + Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)), + Expression.Constant(true) + ); + return AddNullChecks(leftSide, rightSide, contains); } } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringEndsWithConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringEndsWithConditionOperator.cs index b90a2acf7..a256fbf37 100644 --- a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringEndsWithConditionOperator.cs +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringEndsWithConditionOperator.cs @@ -23,7 +23,11 @@ namespace Artemis.Core.DefaultTypes public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide) { - return Expression.Equal(Expression.Call(Expression.Call(leftSide, _toLower), _endsWith, Expression.Call(rightSide, _toLower)), Expression.Constant(true)); + var endsWith = Expression.Equal( + Expression.Call(Expression.Call(leftSide, _toLower), _endsWith, Expression.Call(rightSide, _toLower)), + Expression.Constant(true) + ); + return AddNullChecks(leftSide, rightSide, endsWith); } } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs index 100b47d50..0ee241bd7 100644 --- a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringNotContainsConditionOperator.cs @@ -23,7 +23,11 @@ namespace Artemis.Core.DefaultTypes public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide) { - return Expression.Equal(Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)), Expression.Constant(false)); + var notContains = Expression.Equal( + Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)), + Expression.Constant(false) + ); + return AddNullChecks(leftSide, rightSide, notContains); } } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringStartsWithConditionOperator.cs b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringStartsWithConditionOperator.cs index 7dd302975..7ae5bc06a 100644 --- a/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringStartsWithConditionOperator.cs +++ b/src/Artemis.Core/DefaultTypes/Conditions/Operators/StringStartsWithConditionOperator.cs @@ -23,7 +23,11 @@ namespace Artemis.Core.DefaultTypes public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide) { - return Expression.Equal(Expression.Call(Expression.Call(leftSide, _toLower), _startsWith, Expression.Call(rightSide, _toLower)), Expression.Constant(true)); + var startsWith = Expression.Equal( + Expression.Call(Expression.Call(leftSide, _toLower), _startsWith, Expression.Call(rightSide, _toLower)), + Expression.Constant(true) + ); + return AddNullChecks(leftSide, rightSide, startsWith); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Conditions/ConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/ConditionOperator.cs index 53ca9bd79..5a07ca4e5 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/ConditionOperator.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/ConditionOperator.cs @@ -53,5 +53,34 @@ namespace Artemis.Core /// The parameter on the right side of the expression /// public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide); + + /// + /// Wraps the provided expression in null-checks for the left and right side + /// + /// The resulting expression body looks like: + /// (a == null && b == null) || ((a != null && b != null) && <expression>) + /// + /// + /// The left side to check for nulls + /// The right side to check for nulls + /// The expression to wrap + /// The wrapped expression + protected BinaryExpression AddNullChecks(Expression leftSide, Expression rightSide, BinaryExpression expression) + { + var nullConst = Expression.Constant(null); + return Expression.OrElse( + Expression.AndAlso( + Expression.Equal(leftSide, nullConst), + Expression.Equal(rightSide, nullConst) + ), + Expression.AndAlso( + Expression.AndAlso( + Expression.NotEqual(leftSide, nullConst), + Expression.NotEqual(rightSide, nullConst) + ), + expression + ) + ); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index 54c63292f..1193015fe 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -152,9 +152,10 @@ namespace Artemis.Core private void ApplyRegistration(DataBindingRegistration dataBindingRegistration) { - if (dataBindingRegistration != null) - dataBindingRegistration.DataBinding = this; + if (dataBindingRegistration == null) + throw new ArgumentNullException(nameof(dataBindingRegistration)); + dataBindingRegistration.DataBinding = this; Converter = dataBindingRegistration?.Converter; Registration = dataBindingRegistration; @@ -226,9 +227,11 @@ namespace Artemis.Core { if (_disposed) throw new ObjectDisposedException("DataBinding"); + // General var registration = LayerProperty.GetDataBindingRegistration(Entity.TargetExpression); - ApplyRegistration(registration); + if (registration != null) + ApplyRegistration(registration); EasingTime = Entity.EasingTime; EasingFunction = (Easings.Functions) Entity.EasingFunction; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/ConditionalDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/ConditionalDataBinding.cs index 0e3cd309d..09dca0393 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/ConditionalDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/ConditionalDataBinding.cs @@ -17,6 +17,7 @@ namespace Artemis.Core { DataBinding = dataBinding; Entity = entity; + Load(); } internal ConditionalDataBindingEntity Entity { get; } @@ -90,8 +91,14 @@ namespace Artemis.Core OnConditionsUpdated(); } - internal void ApplyOrder() + /// + /// Applies the current order of conditions to the collection + /// + public void ApplyOrder() { + if (_disposed) + throw new ObjectDisposedException("ConditionalDataBinding"); + _conditions.Sort((a, b) => a.Order.CompareTo(b.Order)); for (var index = 0; index < _conditions.Count; index++) { diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingCondition.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingCondition.cs index 49f08ee8c..bf0f3ff92 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingCondition.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingCondition.cs @@ -13,10 +13,11 @@ namespace Artemis.Core /// Creates a new instance of the class /// /// The conditional data binding this condition is applied too - public DataBindingCondition(ConditionalDataBinding conditionalDataBinding) + internal DataBindingCondition(ConditionalDataBinding conditionalDataBinding) { ConditionalDataBinding = conditionalDataBinding ?? throw new ArgumentNullException(nameof(conditionalDataBinding)); Order = conditionalDataBinding.Conditions.Count + 1; + Condition = new DataModelConditionGroup(null); Entity = new DataBindingConditionEntity(); Save(); } @@ -34,9 +35,9 @@ namespace Artemis.Core public ConditionalDataBinding ConditionalDataBinding { get; } /// - /// Gets the position at which the modifier appears on the data binding + /// Gets or sets the position at which the modifier appears on the data binding /// - public int Order { get; internal set; } + public int Order { get; set; } /// /// Gets or sets the value to be applied when the condition is met @@ -82,7 +83,7 @@ namespace Artemis.Core ? new DataModelConditionGroup(null, Entity.Condition) : new DataModelConditionGroup(null); - Value = JsonConvert.DeserializeObject(Entity.Value); + Value = Entity.Value == null ? default : JsonConvert.DeserializeObject(Entity.Value); Order = Entity.Order; } @@ -93,15 +94,5 @@ namespace Artemis.Core Condition.Dispose(); } - - /// - /// Updates the order and resorts the Conditions list in the - /// - /// - public void UpdateOrder(int order) - { - Order = order; - ConditionalDataBinding.ApplyOrder(); - } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingModifier.cs index 391027097..52d64cd0d 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DataBindingModifier.cs @@ -46,9 +46,9 @@ namespace Artemis.Core public ProfileRightSideType ParameterType { get; private set; } /// - /// Gets the position at which the modifier appears on the data binding + /// Gets or sets the position at which the modifier appears on the data binding /// - public int Order { get; internal set; } + public int Order { get; set; } /// /// Gets the currently used instance of the parameter data model diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs index 75e3c8c82..c60b31fdb 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modes/DirectDataBinding.cs @@ -224,7 +224,10 @@ namespace Artemis.Core OnModifiersUpdated(); } - internal void ApplyOrder() + /// + /// Applies the current order of conditions to the collection + /// + public void ApplyOrder() { _modifiers.Sort((a, b) => a.Order.CompareTo(b.Order)); for (var index = 0; index < _modifiers.Count; index++) diff --git a/src/Artemis.Core/Services/ModuleService.cs b/src/Artemis.Core/Services/ModuleService.cs index 74fa1d660..37903801f 100644 --- a/src/Artemis.Core/Services/ModuleService.cs +++ b/src/Artemis.Core/Services/ModuleService.cs @@ -70,6 +70,9 @@ namespace Artemis.Core.Services public async Task UpdateModuleActivation() { + if (ActiveModuleSemaphore.CurrentCount == 0) + return; + try { await ActiveModuleSemaphore.WaitAsync(); diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml index e1d719218..3a3821f83 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml @@ -36,8 +36,7 @@ ToolTip="{Binding SelectedPropertyViewModel.DisplayPropertyPath}" IsEnabled="{Binding IsEnabled}" HorizontalAlignment="Left" - Click="PropertyButton_OnClick" - Visibility="{Binding ShowFreeInput, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}"> + Click="PropertyButton_OnClick"> diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml index e84147b0c..67446972a 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticView.xaml @@ -32,12 +32,12 @@ + Text="{Binding TargetDescription.Prefix}" + Visibility="{Binding TargetDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" /> + Text="{Binding TargetDescription.Affix}" + Visibility="{Binding TargetDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}" /> diff --git a/src/Artemis.UI/Extensions/BindableCollectionExtensions.cs b/src/Artemis.UI/Extensions/BindableCollectionExtensions.cs new file mode 100644 index 000000000..f523f169f --- /dev/null +++ b/src/Artemis.UI/Extensions/BindableCollectionExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using Stylet; + +namespace Artemis.UI.Extensions +{ + public static class BindableCollectionExtensions + { + public static void Sort(this BindableCollection collection, Func order) + { + var ordered = collection.OrderBy(order).ToList(); + for (var index = 0; index < ordered.Count; index++) + { + var dataBindingConditionViewModel = ordered[index]; + if (collection.IndexOf(dataBindingConditionViewModel) != index) + collection.Move(collection.IndexOf(dataBindingConditionViewModel), index); + } + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index f504a6bf9..8a2d0871a 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -88,9 +88,10 @@ namespace Artemis.UI.Ninject.Factories public interface IDataBindingsVmFactory { IDataBindingViewModel DataBindingViewModel(IDataBindingRegistration registration); - DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier); DirectDataBindingModeViewModel DirectDataBindingModeViewModel(DirectDataBinding directDataBinding); + DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier); ConditionalDataBindingModeViewModel ConditionalDataBindingModeViewModel(ConditionalDataBinding conditionalDataBinding); + DataBindingConditionViewModel DataBindingConditionViewModel(DataBindingCondition dataBindingCondition); } public interface IPropertyVmFactory diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs index 960b49d56..27f6875fc 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionGroupViewModel.cs @@ -142,6 +142,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions if (IsRootGroup && Parent is DisplayConditionsViewModel displayConditionsViewModel) displayConditionsViewModel.DisplayStartHint = !Items.Any(); + + OnUpdated(); + } + + public event EventHandler Updated; + + protected virtual void OnUpdated() + { + Updated?.Invoke(this, EventArgs.Empty); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs index cc351d7a9..5a3bf7f66 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Conditions/DataModelConditionPredicateViewModel.cs @@ -157,6 +157,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; } + RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue; if (RightSideInputViewModel.TargetType != targetType) RightSideInputViewModel.UpdateTargetType(targetType); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeView.xaml index 461452e5f..d5321a776 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeView.xaml @@ -4,17 +4,78 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:s="https://github.com/canton7/Stylet" + xmlns:dd="urn:gong-wpf-dragdrop" xmlns:Converters="clr-namespace:Artemis.UI.Converters" + xmlns:utilities="clr-namespace:Artemis.UI.Utilities" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - - - - Conditional data bindings not yet implemented - - - This is where you'd provide values and their conditions.. :> - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeViewModel.cs index 9ef950be5..605ee367f 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/ConditionalDataBindingModeViewModel.cs @@ -1,28 +1,120 @@ -using Artemis.Core; +using System; +using System.Collections.Specialized; +using System.Linq; +using Artemis.Core; +using Artemis.UI.Extensions; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Shared.Services; using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding { public class ConditionalDataBindingModeViewModel : Screen, IDataBindingModeViewModel { - public ConditionalDataBindingModeViewModel(ConditionalDataBinding conditionalDataBinding) + private readonly IProfileEditorService _profileEditorService; + private readonly IDataBindingsVmFactory _dataBindingsVmFactory; + private bool _updating; + + public ConditionalDataBindingModeViewModel(ConditionalDataBinding conditionalDataBinding, + IProfileEditorService profileEditorService, + IDataBindingsVmFactory dataBindingsVmFactory) { + _profileEditorService = profileEditorService; + _dataBindingsVmFactory = dataBindingsVmFactory; + ConditionalDataBinding = conditionalDataBinding; + ConditionViewModels = new BindableCollection>(); + + Initialize(); } public ConditionalDataBinding ConditionalDataBinding { get; } - - public void Dispose() - { - } + public BindableCollection> ConditionViewModels { get; } public void Update() { + UpdateConditionViewModels(); } public object GetTestValue() { - return null; + return ConditionalDataBinding.DataBinding.Converter.GetValue(); } + + public void AddCondition(string type) + { + var condition = ConditionalDataBinding.AddCondition(); + + // Find the VM of the new condition + var viewModel = ConditionViewModels.First(c => c.DataBindingCondition == condition); + viewModel.ActiveItem.AddCondition(type); + + _profileEditorService.UpdateSelectedProfileElement(); + } + + private void UpdateConditionViewModels() + { + _updating = true; + + // Remove old VMs + var toRemove = ConditionViewModels.Where(c => !ConditionalDataBinding.Conditions.Contains(c.DataBindingCondition)).ToList(); + foreach (var dataBindingConditionViewModel in toRemove) { + ConditionViewModels.Remove(dataBindingConditionViewModel); + dataBindingConditionViewModel.Dispose(); + } + + // Add missing VMs + foreach (var condition in ConditionalDataBinding.Conditions) + { + if (ConditionViewModels.All(c => c.DataBindingCondition != condition)) + ConditionViewModels.Add(_dataBindingsVmFactory.DataBindingConditionViewModel(condition)); + } + + // Fix order + ConditionViewModels.Sort(c => c.DataBindingCondition.Order); + + _updating = false; + } + + private void Initialize() + { + ConditionalDataBinding.ConditionsUpdated += ConditionalDataBindingOnConditionsUpdated; + ConditionViewModels.CollectionChanged += ConditionViewModelsOnCollectionChanged; + UpdateConditionViewModels(); + } + + private void ConditionViewModelsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (_updating || e.Action != NotifyCollectionChangedAction.Add) + return; + + for (var index = 0; index < ConditionViewModels.Count; index++) + { + var conditionViewModel = ConditionViewModels[index]; + conditionViewModel.DataBindingCondition.Order = index + 1; + } + + ConditionalDataBinding.ApplyOrder(); + + _profileEditorService.UpdateSelectedProfileElement(); + } + + private void ConditionalDataBindingOnConditionsUpdated(object sender, EventArgs e) + { + UpdateConditionViewModels(); + } + + #region IDisposable + + public void Dispose() + { + ConditionalDataBinding.ConditionsUpdated -= ConditionalDataBindingOnConditionsUpdated; + + foreach (var conditionViewModel in ConditionViewModels) + conditionViewModel.Dispose(); + ConditionViewModels.Clear(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionView.xaml new file mode 100644 index 000000000..4b068ef90 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionView.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs new file mode 100644 index 000000000..b2cac837d --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/ConditionalDataBinding/DataBindingConditionViewModel.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using Artemis.Core; +using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.ProfileEditor.Conditions; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Input; +using Artemis.UI.Shared.Services; +using Stylet; + +namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.ConditionalDataBinding +{ + public class DataBindingConditionViewModel : Conductor, IDisposable + { + private readonly IProfileEditorService _profileEditorService; + + public DataBindingConditionViewModel(DataBindingCondition dataBindingCondition, + IProfileEditorService profileEditorService, + IDataModelConditionsVmFactory dataModelConditionsVmFactory, + IDataModelUIService dataModelUIService) + { + _profileEditorService = profileEditorService; + DataBindingCondition = dataBindingCondition; + + ActiveItem = dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataBindingCondition.Condition, false); + ActiveItem.IsRootGroup = true; + ActiveItem.Update(); + ActiveItem.Updated += ActiveItemOnUpdated; + + ValueViewModel = dataModelUIService.GetStaticInputViewModel(typeof(TProperty)); + ValueViewModel.ValueUpdated += ValueViewModelOnValueUpdated; + ValueViewModel.Value = DataBindingCondition.Value; + } + + private void ActiveItemOnUpdated(object sender, EventArgs e) + { + if (!ActiveItem.GetChildren().Any()) + DataBindingCondition.ConditionalDataBinding.RemoveCondition(DataBindingCondition); + } + + private void ValueViewModelOnValueUpdated(object sender, DataModelInputStaticEventArgs e) + { + DataBindingCondition.Value = (TProperty) Convert.ChangeType(e.Value, typeof(TProperty)); + _profileEditorService.UpdateSelectedProfileElement(); + } + + public DataBindingCondition DataBindingCondition { get; } + + public DataModelStaticViewModel ValueViewModel { get; set; } + + public void Dispose() + { + ValueViewModel.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml index 8f6668f59..2f35ca31e 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml @@ -4,7 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" - xmlns:dd="urn:gong-wpf-dragdrop" xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> @@ -16,28 +15,27 @@ - - - - - - + + + + + - - - - - - - - + + + + + + + - - + - - + - + - - - - - - - - - - + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + Test result - - - - - - - - - - - - Input - + + + + + + + + + + + Input + - Output - Output + - - - - + + + + - - - + + - - - - + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml index 6f09ca838..8a8ac38c2 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierView.xaml @@ -3,11 +3,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings" xmlns:s="https://github.com/canton7/Stylet" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:utilities="clr-namespace:Artemis.UI.Utilities" + xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDataBinding" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataBindingModifierViewModel}"> 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 c66ca6730..e13cd2572 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DataBindingModifierViewModel.cs @@ -64,6 +64,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa public void Delete() { Modifier.DirectDataBinding.RemoveModifier(Modifier); + _profileEditorService.UpdateSelectedProfileElement(); } public void SwapType() @@ -74,6 +75,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa Modifier.UpdateParameter(null, null); Update(); + _profileEditorService.UpdateSelectedProfileElement(); } private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeView.xaml index c71e0d2a9..3df27da45 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeView.xaml @@ -33,9 +33,8 @@ Height="22" Command="{s:Action AddModifier}"> - - Add modifier + ADD MODIFIER @@ -48,7 +47,6 @@ dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True" dd:DragDrop.UseDefaultDragAdorner="True" - dd:DragDrop.DropHandler="{Binding}" HorizontalContentAlignment="Stretch" Margin="0 5 0 0"> 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 e20f2c33d..dde2ca976 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DirectDataBinding/DirectDataBindingModeViewModel.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Specialized; +using System.Linq; using Artemis.Core; +using Artemis.UI.Extensions; using Artemis.UI.Ninject.Factories; using Artemis.UI.Shared; using Artemis.UI.Shared.Input; @@ -14,6 +17,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa private readonly IDataModelUIService _dataModelUIService; private readonly IProfileEditorService _profileEditorService; private DataModelDynamicViewModel _targetSelectionViewModel; + private bool _canAddModifier; + private bool _updating; public DirectDataBindingModeViewModel(DirectDataBinding directDataBinding, IProfileEditorService profileEditorService, @@ -34,6 +39,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa public DirectDataBinding DirectDataBinding { get; } public BindableCollection> ModifierViewModels { get; } + public bool CanAddModifier + { + get => _canAddModifier; + set => SetAndNotify(ref _canAddModifier, value); + } + public DataModelDynamicViewModel TargetSelectionViewModel { get => _targetSelectionViewModel; @@ -45,6 +56,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DirectDataBinding.SourceDataModel, DirectDataBinding.SourcePropertyPath); TargetSelectionViewModel.FilterTypes = new[] {DirectDataBinding.DataBinding.GetTargetType()}; + CanAddModifier = DirectDataBinding.SourceDataModel != null; UpdateModifierViewModels(); } @@ -52,16 +64,32 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa { return TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); } - + private void Initialize() { DirectDataBinding.ModifiersUpdated += DirectDataBindingOnModifiersUpdated; TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected; - + ModifierViewModels.CollectionChanged += ModifierViewModelsOnCollectionChanged; Update(); } - + + private void ModifierViewModelsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (_updating || e.Action != NotifyCollectionChangedAction.Add) + return; + + for (var index = 0; index < ModifierViewModels.Count; index++) + { + var dataBindingModifierViewModel = ModifierViewModels[index]; + dataBindingModifierViewModel.Modifier.Order = index + 1; + } + + DirectDataBinding.ApplyOrder(); + + _profileEditorService.UpdateSelectedProfileElement(); + } + #region Target private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) @@ -73,7 +101,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa } #endregion - + #region Modifiers public void AddModifier() @@ -81,16 +109,30 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa DirectDataBinding.AddModifier(ProfileRightSideType.Dynamic); _profileEditorService.UpdateSelectedProfileElement(); } - - + private void UpdateModifierViewModels() { - foreach (var dataBindingModifierViewModel in ModifierViewModels) - dataBindingModifierViewModel.Dispose(); - ModifierViewModels.Clear(); + _updating = true; - foreach (var dataBindingModifier in DirectDataBinding.Modifiers) - ModifierViewModels.Add(_dataBindingsVmFactory.DataBindingModifierViewModel(dataBindingModifier)); + // Remove old VMs + var toRemove = ModifierViewModels.Where(m => !DirectDataBinding.Modifiers.Contains(m.Modifier)).ToList(); + foreach (var modifierViewModel in toRemove) + { + ModifierViewModels.Remove(modifierViewModel); + modifierViewModel.Dispose(); + } + + // Add missing VMs + foreach (var modifier in DirectDataBinding.Modifiers) + { + if (ModifierViewModels.All(m => m.Modifier != modifier)) + ModifierViewModels.Add(_dataBindingsVmFactory.DataBindingModifierViewModel(modifier)); + } + + // Fix order + ModifierViewModels.Sort(m => m.Modifier.Order); + + _updating = false; } private void DirectDataBindingOnModifiersUpdated(object sender, EventArgs e)