Data bindings - WIP commit

Layer/effect config dialogs - Don't require specific ctor parameter names
This commit is contained in:
Robert committed 2020-09-03 17:24:52 +02:00
1 parent 0d0ba7bd7d
commit 3bb4e86f65
15 files changed
+408 -130

No files matched your search

@@ -0,0 +1,96 @@
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DataBindingModifierView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:DataBindingModifierViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
ToolTip="Delete the modifier"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<Button Grid.Column="1"
ToolTip="Swap modifier type to static/dynamic"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="{StaticResource SecondaryAccentBrush}"
Width="25"
Height="25"
Command="{s:Action SwapType}">
<materialDesign:PackIcon Kind="SwapHorizontalVariant" Width="18" Height="18" />
</Button>
<Button Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}">
<!-- Click="PropertyButton_OnClick" -->
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Grid.Column="3"
Background="#ab47bc"
BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
HorizontalAlignment="Left">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectLeftPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedLeftSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
</Grid>
</UserControl>
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
/// <summary>
/// Interaction logic for DataBindingModifierView.xaml
/// </summary>
public partial class DataBindingModifierView : UserControl
{
public DataBindingModifierView()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,15 @@
using Artemis.Core;
using Stylet;
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingModifierViewModel : PropertyChangedBase
{
public DataBindingModifierViewModel(DataBindingModifier modifier)
{
Modifier = modifier;
}
public DataBindingModifier Modifier { get; }
}
}
@@ -8,8 +8,10 @@
xmlns:utilities="clr-namespace:Artemis.UI.Utilities"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:DataBindingViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@@ -165,103 +167,20 @@
</StackPanel>
</Button>
<!-- ItemsSource="{Binding ModifierViewModels}" -->
<ListBox Grid.Row="1"
ItemsSource="{Binding ModifierViewModels}"
materialDesign:RippleAssist.IsDisabled="True"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.DropHandler="{Binding}"
HorizontalContentAlignment="Stretch">
<!-- <ListBox.ItemTemplate> -->
<!-- <DataTemplate> -->
<!-- <ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" /> -->
<!-- </DataTemplate> -->
<!-- </ListBox.ItemTemplate> -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
ToolTip="Delete the modifier"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="#E74C4C"
Width="25"
Height="25"
Command="{s:Action Delete}">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
<Button Grid.Column="1"
ToolTip="Swap modifier type to static/dynamic"
Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Left"
Foreground="{StaticResource SecondaryAccentBrush}"
Width="25"
Height="25"
Command="{s:Action SwapType}">
<materialDesign:PackIcon Kind="SwapHorizontalVariant" Width="18" Height="18" />
</Button>
<Button Grid.Column="2"
Style="{StaticResource DisplayConditionButtonLeftClickMenu}"
Background="#7B7B7B"
BorderBrush="#7B7B7B"
Content="{Binding SelectedOperator.Description}">
<!-- Click="PropertyButton_OnClick" -->
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding Operators}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Icon}" VerticalAlignment="Center" Margin="0 0 15 0" />
<TextBlock Text="{Binding Description}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ContextMenu.ItemTemplate>
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="Command" Value="{Binding Data.SelectOperatorCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Grid.Column="3"
Background="#ab47bc"
BorderBrush="#ab47bc"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedLeftSideProperty.DisplayPropertyPath}"
HorizontalAlignment="Left">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LeftSideDataModel.Children}" IsOpen="{Binding LeftSideDataModelOpen, Mode=OneWayToSource}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MaterialDesignMenuItem}">
<Setter Property="ItemsSource" Value="{Binding Children}" />
<Setter Property="Command" Value="{Binding Data.SelectLeftPropertyCommand, Source={StaticResource DataContextProxy}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="CommandTarget" Value="{Binding}" />
<Setter Property="IsEnabled" Value="{Binding IsMatchingFilteredTypes}" />
<Setter Property="IsSubmenuOpen" Value="{Binding IsVisualizationExpanded, Mode=TwoWay}" />
<Setter Property="HeaderTemplate" Value="{StaticResource DataModelDataTemplate}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
<Grid>
<TextBlock Text="{Binding SelectedLeftSideProperty.PropertyDescription.Name}"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}}" />
<TextBlock Text="« Select a property »"
FontStyle="Italic"
Visibility="{Binding SelectedLeftSideProperty, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}" />
</Grid>
</Button>
</Grid>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
@@ -1,4 +1,5 @@
using System.Reflection;
using System.Linq;
using System.Reflection;
using Artemis.Core;
using Stylet;
@@ -6,15 +7,70 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
{
public class DataBindingViewModel : PropertyChangedBase
{
public DataBindingViewModel(BaseLayerProperty layerProperty, PropertyInfo dataBindingProperty)
private DataBinding _dataBinding;
public DataBindingViewModel(BaseLayerProperty layerProperty, PropertyInfo targetProperty)
{
DisplayName = dataBindingProperty.Name.ToUpper();
LayerProperty = layerProperty;
DataBindingProperty = dataBindingProperty;
TargetProperty = targetProperty;
DisplayName = TargetProperty.Name.ToUpper();
ModifierViewModels = new BindableCollection<DataBindingModifierViewModel>();
DataBinding = layerProperty.DataBindings.FirstOrDefault(d => d.TargetProperty == targetProperty);
}
public string DisplayName { get; }
public BaseLayerProperty LayerProperty { get; }
public PropertyInfo DataBindingProperty { get; }
public PropertyInfo TargetProperty { get; }
public string DisplayName { get; }
public BindableCollection<DataBindingModifierViewModel> ModifierViewModels { get; }
public DataBinding DataBinding
{
get => _dataBinding;
set
{
if (!SetAndNotify(ref _dataBinding, value)) return;
UpdateModifierViewModels();
}
}
public void EnableDataBinding()
{
if (DataBinding != null)
return;
DataBinding = LayerProperty.AddDataBinding(TargetProperty);
}
public void RemoveDataBinding()
{
if (DataBinding == null)
return;
var toRemove = DataBinding;
DataBinding = null;
LayerProperty.RemoveDataBinding(toRemove);
}
public void AddModifier()
{
if (DataBinding == null)
return;
var modifier = new DataBindingModifier(ProfileRightSideType.Dynamic);
DataBinding.AddModifier(modifier);
ModifierViewModels.Add(new DataBindingModifierViewModel(modifier));
}
private void UpdateModifierViewModels()
{
ModifierViewModels.Clear();
if (DataBinding == null)
return;
foreach (var dataBindingModifier in DataBinding.Modifiers)
ModifierViewModels.Add(new DataBindingModifierViewModel(dataBindingModifier));
}
}
}
@@ -31,16 +31,21 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
private void Initialise()
{
DataBindingViewModel = null;
DataBindingsTabsViewModel = null;
var properties = LayerProperty.GetDataBindingProperties();
if (properties.Count == 0)
if (properties == null || properties.Count == 0)
return;
// Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving
// and creating the actual data bindings
if (properties.Count == 1)
DataBindingViewModel = new DataBindingViewModel(LayerProperty, properties.First());
else
{
DataBindingsTabsViewModel = new DataBindingsTabsViewModel();
foreach (var dataBindingProperty in LayerProperty.GetDataBindingProperties())
foreach (var dataBindingProperty in properties)
DataBindingsTabsViewModel.Tabs.Add(new DataBindingViewModel(LayerProperty, dataBindingProperty));
}
}
@@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.Dialogs;
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract;
using Artemis.UI.Shared.Services;
@@ -39,33 +42,48 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
public void OpenBrushSettings()
{
var configurationViewModel = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush.ConfigurationDialog;
var layerBrush = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush;
var configurationViewModel = layerBrush.ConfigurationDialog;
if (configurationViewModel == null)
return;
try
{
var layerBrush = new ConstructorArgument("layerBrush", LayerPropertyGroupViewModel.LayerPropertyGroup.LayerBrush);
var viewModel = (BrushConfigurationViewModel) _kernel.Get(configurationViewModel.Type, layerBrush);
// Limit to one constructor, there's no need to have more and it complicates things anyway
var constructors = configurationViewModel.Type.GetConstructors();
if (constructors.Length != 1)
throw new ArtemisUIException("Brush configuration dialogs must have exactly one constructor");
// Find the BaseLayerBrush parameter, it is required by the base constructor so its there for sure
var brushParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerBrush).IsAssignableFrom(p.ParameterType));
var argument = new ConstructorArgument(brushParameter.Name, layerBrush);
var viewModel = (BrushConfigurationViewModel) _kernel.Get(configurationViewModel.Type, argument);
_windowManager.ShowDialog(new LayerBrushSettingsWindowViewModel(viewModel));
}
catch (Exception e)
{
_dialogService.ShowExceptionDialog("An exception occured while trying to show the brush's settings window", e);
throw;
}
}
public void OpenEffectSettings()
{
var configurationViewModel = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect.ConfigurationDialog;
var layerEffect = LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect;
var configurationViewModel = layerEffect.ConfigurationDialog;
if (configurationViewModel == null)
return;
try
{
var layerEffect = new ConstructorArgument("layerEffect", LayerPropertyGroupViewModel.LayerPropertyGroup.LayerEffect);
var viewModel = (EffectConfigurationViewModel) _kernel.Get(configurationViewModel.Type, layerEffect);
// Limit to one constructor, there's no need to have more and it complicates things anyway
var constructors = configurationViewModel.Type.GetConstructors();
if (constructors.Length != 1)
throw new ArtemisUIException("Effect configuration dialogs must have exactly one constructor");
var effectParameter = constructors.First().GetParameters().First(p => typeof(BaseLayerEffect).IsAssignableFrom(p.ParameterType));
var argument = new ConstructorArgument(effectParameter.Name, layerEffect);
var viewModel = (EffectConfigurationViewModel) _kernel.Get(configurationViewModel.Type, argument);
_windowManager.ShowDialog(new LayerEffectSettingsWindowViewModel(viewModel));
}
catch (Exception e)
@@ -10,6 +10,7 @@ using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Shared.Services;
using MaterialDesignThemes.Wpf;
using Ninject;
@@ -88,7 +89,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
try
{
var plugin = new ConstructorArgument("plugin", Plugin);
// Limit to one constructor, there's no need to have more and it complicates things anyway
var constructors = configurationViewModel.Type.GetConstructors();
if (constructors.Length != 1)
throw new ArtemisUIException("Plugin configuration dialogs must have exactly one constructor");
var pluginParameter = constructors.First().GetParameters().First(p => typeof(Plugin).IsAssignableFrom(p.ParameterType));
var plugin = new ConstructorArgument(pluginParameter.Name, Plugin);
var viewModel = (PluginConfigurationViewModel) _kernel.Get(configurationViewModel.Type, plugin);
_windowManager.ShowDialog(new PluginSettingsWindowViewModel(viewModel, Icon));
}