1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Data model - Support all lists implementing IEnumerable<>

This commit is contained in:
Robert 2020-10-12 19:31:02 +02:00
parent 17d41647b6
commit 3b6753a0ff
9 changed files with 91 additions and 128 deletions

View File

@ -94,5 +94,27 @@ namespace Artemis.Core
{ {
return type.IsValueType ? Activator.CreateInstance(type) : null; return type.IsValueType ? Activator.CreateInstance(type) : null;
} }
/// <summary>
/// Determines whether the given type is a generic enumerable
/// </summary>
public static bool IsGenericEnumerable(this Type type)
{
return type.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
/// <summary>
/// Determines the type of the provided generic enumerable type
/// </summary>
public static Type? GetGenericEnumerableType(this Type type)
{
Type enumerableType = type.GetInterfaces().FirstOrDefault(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
return enumerableType?.GenericTypeArguments[0];
}
} }
} }

View File

@ -93,7 +93,7 @@ namespace Artemis.Core
if (ListPath != null) if (ListPath != null)
{ {
Type listType = ListPath.GetPropertyType()!; Type listType = ListPath.GetPropertyType()!;
ListType = listType.GetGenericArguments()[0]; ListType = listType.GetGenericEnumerableType();
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string); IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
// Create a new root group // Create a new root group
@ -129,10 +129,10 @@ namespace Artemis.Core
if (!Children.Any()) if (!Children.Any())
return false; return false;
if (!(target is IList list)) if (!(target is IEnumerable enumerable))
return false; return false;
IEnumerable<object> objectList = list.Cast<object>(); IEnumerable<object> objectList = enumerable.Cast<object>();
return ListOperator switch return ListOperator switch
{ {
ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)), ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)),
@ -180,14 +180,14 @@ namespace Artemis.Core
DataModelPath listPath = new DataModelPath(null, Entity.ListPath); DataModelPath listPath = new DataModelPath(null, Entity.ListPath);
Type listType = listPath.GetPropertyType()!; Type listType = listPath.GetPropertyType()!;
// Can't check this on an invalid list, if it becomes valid later lets hope for the best // 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)) if (listPath.IsValid && !listPath.PointsToList)
return; return;
ListPath = listPath; ListPath = listPath;
SubscribeToListPath(); SubscribeToListPath();
if (ListPath.IsValid) if (ListPath.IsValid)
{ {
ListType = listType.GetGenericArguments()[0]; ListType = listType.GetGenericEnumerableType();
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string); IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
} }
else else

View File

@ -114,8 +114,14 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets a boolean indicating whether this data model path points to a list /// Gets a boolean indicating whether this data model path points to a list
/// </summary> /// </summary>
public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null && public bool PointsToList
typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType()); {
get
{
Type? type = GetPropertyType();
return type?.IsGenericEnumerable() ?? false;
}
}
internal DataModelPathEntity Entity { get; } internal DataModelPathEntity Entity { get; }

View File

@ -142,79 +142,6 @@ namespace Artemis.Core.DataModelExpansions
return value as T; return value as T;
} }
internal bool ContainsPath(string? path)
{
if (path == null)
return false;
string[] parts = path.Split('.');
Type? current = GetType();
foreach (string part in parts)
{
PropertyInfo? property = current?.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
}
internal Type GetTypeAtPath(string path)
{
if (!ContainsPath(path))
return null;
string[] parts = path.Split('.');
Type current = GetType();
Type result = null;
foreach (string part in parts)
{
PropertyInfo? property = current.GetProperty(part);
current = property.PropertyType;
result = property.PropertyType;
}
return result;
}
internal Type GetListTypeInPath(string path)
{
if (!ContainsPath(path))
return null;
string[] parts = path.Split('.');
Type current = GetType();
int index = 0;
foreach (string part in parts)
{
// Only return a type if the path CONTAINS a list, not if it points TO a list
if (index == parts.Length - 1)
return null;
PropertyInfo? property = current.GetProperty(part);
// For lists, look into the list type instead of the list itself
if (typeof(IList).IsAssignableFrom(property.PropertyType))
return property.PropertyType.GetGenericArguments()[0];
current = property.PropertyType;
index++;
}
return null;
}
internal Type GetListTypeAtPath(string path)
{
if (!ContainsPath(path))
return null;
Type child = GetTypeAtPath(path);
return child.GenericTypeArguments.Length > 0 ? child.GenericTypeArguments[0] : null;
}
#region Events #region Events
/// <summary> /// <summary>

View File

@ -9,31 +9,38 @@ namespace Artemis.UI.Shared
{ {
public class DataModelListViewModel : DataModelVisualizationViewModel public class DataModelListViewModel : DataModelVisualizationViewModel
{ {
private string _count; private string _countDisplay;
private IList _list; private IEnumerable _list;
private int _listCount;
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath) internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
{ {
ListChildren = new BindableCollection<DataModelVisualizationViewModel>(); ListChildren = new BindableCollection<DataModelVisualizationViewModel>();
} }
public IList List public IEnumerable List
{ {
get => _list; get => _list;
set => SetAndNotify(ref _list, value); set => SetAndNotify(ref _list, value);
} }
public BindableCollection<DataModelVisualizationViewModel> ListChildren { get; set; } public int ListCount
public string Count
{ {
get => _count; get => _listCount;
set => SetAndNotify(ref _count, value); set => SetAndNotify(ref _listCount, value);
} }
public string CountDisplay
{
get => _countDisplay;
set => SetAndNotify(ref _countDisplay, value);
}
public BindableCollection<DataModelVisualizationViewModel> ListChildren { get; set; }
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService) public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
{ {
Type listType = DataModelPath.GetPropertyType()?.GenericTypeArguments[0]; Type listType = DataModelPath.GetPropertyType()?.GetGenericEnumerableType();
if (listType == null) if (listType == null)
return null; return null;
@ -42,10 +49,8 @@ namespace Artemis.UI.Shared
viewModel.Update(dataModelUIService); viewModel.Update(dataModelUIService);
// Put an empty value into the list type property view model // Put an empty value into the list type property view model
if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel) if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel) return dataModelListClassViewModel;
{
return dataModelListClassViewModel;
}
if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel) if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel)
{ {
dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType); dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType);
@ -62,7 +67,7 @@ namespace Artemis.UI.Shared
if (Parent != null && !Parent.IsVisualizationExpanded) if (Parent != null && !Parent.IsVisualizationExpanded)
return; return;
List = GetCurrentValue() as IList; List = GetCurrentValue() as IEnumerable;
if (List == null) if (List == null)
return; return;
@ -79,7 +84,9 @@ namespace Artemis.UI.Shared
ListChildren.Add(child); ListChildren.Add(child);
} }
else else
{
child = ListChildren[index]; child = ListChildren[index];
}
if (child is DataModelListPropertiesViewModel dataModelListClassViewModel) if (child is DataModelListPropertiesViewModel dataModelListClassViewModel)
{ {
@ -96,10 +103,18 @@ namespace Artemis.UI.Shared
index++; index++;
} }
while (ListChildren.Count > List.Count) ListCount = index + 1;
while (ListChildren.Count > ListCount)
ListChildren.RemoveAt(ListChildren.Count - 1); ListChildren.RemoveAt(ListChildren.Count - 1);
Count = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}"; CountDisplay = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
}
/// <inheritdoc />
public override string ToString()
{
return $"[List] {DisplayPath ?? Path} - {ListCount} item(s)";
} }
protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType) protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType)
@ -117,11 +132,5 @@ namespace Artemis.UI.Shared
return null; return null;
} }
/// <inheritdoc />
public override string ToString()
{
return $"[List] {DisplayPath ?? Path} - {List?.Count ?? 0} item(s)";
}
} }
} }

View File

@ -133,7 +133,9 @@ namespace Artemis.UI.Shared
} }
if (looseMatch) if (looseMatch)
IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) || t == typeof(Enum) && type.IsEnum); IsMatchingFilteredTypes = filteredTypes.Any(t => t.IsCastableFrom(type) ||
t == typeof(Enum) && type.IsEnum ||
t == typeof(IEnumerable<>) && type.IsGenericEnumerable());
else else
IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum); IsMatchingFilteredTypes = filteredTypes.Any(t => t == type || t == typeof(Enum) && type.IsEnum);
} }
@ -260,7 +262,7 @@ namespace Artemis.UI.Shared
// For primitives, create a property view model, it may be null that is fine // For primitives, create a property view model, it may be null that is fine
if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string)) if (propertyType.IsPrimitive || propertyType.IsEnum || propertyType == typeof(string))
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth}; return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {Depth = depth};
if (typeof(IList).IsAssignableFrom(propertyType)) if (propertyType.IsGenericEnumerable())
return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth}; return new DataModelListViewModel(DataModel, this, dataModelPath) {Depth = depth};
// For other value types create a child view model // For other value types create a child view model
if (propertyType.IsClass || propertyType.IsStruct()) if (propertyType.IsClass || propertyType.IsStruct())

View File

@ -86,7 +86,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
public void Initialize() public void Initialize()
{ {
TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule());
TargetSelectionViewModel.FilterTypes = new[] {typeof(IList)}; TargetSelectionViewModel.FilterTypes = new[] {typeof(IEnumerable<>)};
TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188)); TargetSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
TargetSelectionViewModel.Placeholder = "Select a list"; TargetSelectionViewModel.Placeholder = "Select a list";
TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected; TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected;

View File

@ -4,7 +4,6 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.DataModelExpansions;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract; using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -83,19 +82,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
public DelegateCommand SelectOperatorCommand { get; } public DelegateCommand SelectOperatorCommand { get; }
public void Dispose()
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStatic();
DisposeRightSideDynamic();
}
public override void Delete() public override void Delete()
{ {
base.Delete(); base.Delete();
@ -239,5 +225,18 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
RightSideSelectionViewModel = null; RightSideSelectionViewModel = null;
} }
} }
public void Dispose()
{
if (LeftSideSelectionViewModel != null)
{
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
LeftSideSelectionViewModel.Dispose();
LeftSideSelectionViewModel = null;
}
DisposeRightSideStatic();
DisposeRightSideDynamic();
}
} }
} }

View File

@ -69,12 +69,10 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold"> <TextBlock Grid.Column="0" Margin="0 0 5 0" FontWeight="Bold" Text="[List]"/>
[List]
</TextBlock>
<TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" /> <TextBlock Grid.Column="1" Text="{Binding PropertyDescription.Name}" ToolTip="{Binding PropertyDescription.Description}" />
<TextBlock Grid.Column="2" <TextBlock Grid.Column="2"
Text="{Binding Count, Mode=OneWay}" Text="{Binding CountDisplay, Mode=OneWay}"
FontFamily="Consolas" FontFamily="Consolas"
HorizontalAlignment="Right" /> HorizontalAlignment="Right" />
</Grid> </Grid>