200 строки
6.8 KiB
C#
200 строки
6.8 KiB
C#
// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
// software and associated documentation files (the "Software"), to deal in the Software
|
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
// substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Windows;
|
|
using System.Windows.Documents;
|
|
using System.Windows.Markup;
|
|
|
|
namespace ICSharpCode.WpfDesign.XamlDom
|
|
{
|
|
/// <summary>
|
|
/// Static class containing helper methods to work with collections (like the XamlParser does)
|
|
/// </summary>
|
|
public static class CollectionSupport
|
|
{
|
|
/// <summary>
|
|
/// Gets if the type is considered a collection in XAML.
|
|
/// </summary>
|
|
public static bool IsCollectionType(Type type)
|
|
{
|
|
return type != typeof(LineBreak) && (
|
|
typeof(IList).IsAssignableFrom(type)
|
|
|| type.IsArray
|
|
|| typeof(IAddChild).IsAssignableFrom(type)
|
|
|| typeof(IDictionary).IsAssignableFrom(type));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets if the collection type <paramref name="col"/> can accepts items of type
|
|
/// <paramref name="item"/>.
|
|
/// </summary>
|
|
public static bool CanCollectionAdd(Type col, Type item)
|
|
{
|
|
var e = col.GetInterface("IEnumerable`1");
|
|
if (e != null && e.IsGenericType) {
|
|
var a = e.GetGenericArguments()[0];
|
|
return a.IsAssignableFrom(item);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets if the collection type <paramref name="col"/> can accept the specified items.
|
|
/// </summary>
|
|
public static bool CanCollectionAdd(Type col, IEnumerable items)
|
|
{
|
|
foreach (var item in items) {
|
|
if (!CanCollectionAdd(col, item.GetType())) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a value to the end of a collection.
|
|
/// </summary>
|
|
public static void AddToCollection(Type collectionType, object collectionInstance, XamlPropertyValue newElement)
|
|
{
|
|
IAddChild addChild = collectionInstance as IAddChild;
|
|
if (addChild != null) {
|
|
if (newElement is XamlTextValue) {
|
|
addChild.AddText((string)newElement.GetValueFor(null));
|
|
} else {
|
|
addChild.AddChild(newElement.GetValueFor(null));
|
|
}
|
|
} else if (collectionInstance is IDictionary) {
|
|
object val = newElement.GetValueFor(null);
|
|
object key = newElement is XamlObject ? ((XamlObject)newElement).GetXamlAttribute("Key") : null;
|
|
if (key == null || (key as string) == "")
|
|
{
|
|
if (val is Style)
|
|
key = ((Style)val).TargetType;
|
|
}
|
|
if (key == null || (key as string) == "")
|
|
key = val;
|
|
((IDictionary)collectionInstance).Add(key, val);
|
|
} else {
|
|
collectionType.InvokeMember(
|
|
"Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
|
|
null, collectionInstance,
|
|
new object[] { newElement.GetValueFor(null) },
|
|
CultureInfo.InvariantCulture);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a value at the specified index in the collection.
|
|
/// </summary>
|
|
public static bool Insert(Type collectionType, object collectionInstance, XamlPropertyValue newElement, int index)
|
|
{
|
|
object value = newElement.GetValueFor(null);
|
|
|
|
// Using IList, with possible Add instead of Insert, was primarily added as a workaround
|
|
// for a peculiarity (or bug) with collections inside System.Windows.Input namespace.
|
|
// See CollectionTests.InputCollectionsPeculiarityOrBug test method for details.
|
|
var list = collectionInstance as IList;
|
|
if (list != null) {
|
|
if (list.Count == index) {
|
|
list.Add(value);
|
|
}
|
|
else {
|
|
list.Insert(index, value);
|
|
}
|
|
return true;
|
|
} else {
|
|
var hasInsert = collectionType.GetMethods().Any(x => x.Name == "Insert");
|
|
|
|
if (hasInsert) {
|
|
collectionType.InvokeMember(
|
|
"Insert", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
|
|
null, collectionInstance,
|
|
new object[] { index, value },
|
|
CultureInfo.InvariantCulture);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a value at the specified index in the collection. A return value indicates whether the Insert succeeded.
|
|
/// </summary>
|
|
/// <returns>True if the Insert succeeded, false if the collection type does not support Insert.</returns>
|
|
internal static bool TryInsert(Type collectionType, object collectionInstance, XamlPropertyValue newElement, int index)
|
|
{
|
|
try {
|
|
return Insert(collectionType, collectionInstance, newElement, index);
|
|
} catch (MissingMethodException) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static readonly Type[] RemoveAtParameters = { typeof(int) };
|
|
|
|
/// <summary>
|
|
/// Removes the item at the specified index of the collection.
|
|
/// </summary>
|
|
/// <returns>True if the removal succeeded, false if the collection type does not support RemoveAt.</returns>
|
|
public static bool RemoveItemAt(Type collectionType, object collectionInstance, int index)
|
|
{
|
|
MethodInfo m = collectionType.GetMethod("RemoveAt", RemoveAtParameters);
|
|
if (m != null) {
|
|
m.Invoke(collectionInstance, new object[] { index });
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an item instance from the specified collection.
|
|
/// </summary>
|
|
public static void RemoveItem(Type collectionType, object collectionInstance, object item)
|
|
{
|
|
collectionType.InvokeMember(
|
|
"Remove", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
|
|
null, collectionInstance,
|
|
new object[] { item },
|
|
CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an item instance from the specified collection.
|
|
/// </summary>
|
|
internal static void RemoveItem(Type collectionType, object collectionInstance, object item, XamlPropertyValue element)
|
|
{
|
|
var dictionary = collectionInstance as IDictionary;
|
|
var xamlObject = element as XamlObject;
|
|
|
|
if (dictionary != null && xamlObject != null) {
|
|
dictionary.Remove(xamlObject.GetXamlAttribute("Key"));
|
|
} else {
|
|
RemoveItem(collectionType, collectionInstance, item);
|
|
}
|
|
}
|
|
}
|
|
}
|