namespace Fluent.Tests.Misc { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using Fluent.Helpers; using NUnit.Framework; using Ribbon = Fluent.Ribbon; using RibbonControl = Fluent.RibbonControl; [TestFixture] public class LogicalTreeTests { [Test] [TestCaseSource(nameof(GetTypesWithImplementedInterface), new object[] { typeof(IRibbonControl) })] public void LogicalTreeShouldWorkForIcon(Type controlType) { if (typeof(MenuItem).IsAssignableFrom(controlType)) { TestLogicalTree(controlType, MenuItem.IconProperty); } else { TestLogicalTree(controlType, RibbonControl.IconProperty); } } [Test] [TestCaseSource(nameof(GetTypesWithImplementedInterface), new object[] { typeof(ILargeIconProvider) })] public void LogicalTreeShouldWorkForLargeIcon(Type controlType) { TestLogicalTree(controlType, LargeIconProviderProperties.LargeIconProperty); } [Test] [TestCaseSource(nameof(GetTypesWithImplementedInterface), new object[] { typeof(IHeaderedControl) })] public void LogicalTreeShouldWorkForHeader(Type controlType) { if (typeof(HeaderedItemsControl).IsAssignableFrom(controlType)) { TestLogicalTree(controlType, HeaderedItemsControl.HeaderProperty); } else { TestLogicalTree(controlType, RibbonControl.HeaderProperty); } } [Test] [TestCaseSource(nameof(GetTypesThatMustHaveLogicalChildSupport))] public void CheckLogicalChildSupport(KeyValuePair item) { var controlType = item.Key; var dependencyProperty = item.Value; var control = (DependencyObject)Activator.CreateInstance(controlType, true); Assert.That(control, Is.Not.Null); if (excludedTypesForLogicalChildSupportTest.Contains(controlType)) { Assert.That(control is ILogicalChildSupport, Is.False, "Type must NOT implement ILogicalChildSupport"); return; } else { Assert.That(control is ILogicalChildSupport, Is.True, "Type must implement ILogicalChildSupport"); } var metadata = dependencyProperty.GetMetadata(control); if (excludedPropertiesForLogicalChildSupportTest.Contains(dependencyProperty)) { Assert.That(metadata.PropertyChangedCallback != LogicalChildSupportHelper.OnLogicalChildPropertyChanged, "PropertyChangedCallback must not be LogicalChildSupportHelper.OnLogicalChildPropertyChanged"); } else { Assert.That(metadata.PropertyChangedCallback == LogicalChildSupportHelper.OnLogicalChildPropertyChanged, "PropertyChangedCallback must be LogicalChildSupportHelper.OnLogicalChildPropertyChanged"); } if (dependencyProperty.ReadOnly) { var dependencyPropertykeyFieldName = dependencyProperty.Name + "PropertyKey"; var dependencyPropertyKeyField = controlType.GetField(dependencyPropertykeyFieldName, BindingFlags.Static | BindingFlags.NonPublic); Assert.That(dependencyPropertyKeyField, Is.Not.Null, $"Field \"{dependencyPropertykeyFieldName}\" must exist."); var dependencyPropertyKey = (DependencyPropertyKey)dependencyPropertyKeyField.GetValue(null); TestLogicalTree(controlType, dependencyProperty, dependencyPropertyKey); } else { TestLogicalTree(controlType, dependencyProperty); } } private static IEnumerable GetTypesWithImplementedInterface(Type type) { return typeof(Ribbon).Assembly.GetTypes() .Where(x => type.IsAssignableFrom(x) && x.IsAbstract == false); } private static readonly Type[] excludedTypesForLogicalChildSupportTest = { typeof(LargeIconProviderProperties), typeof(GalleryItem) }; private static readonly DependencyProperty[] excludedPropertiesForLogicalChildSupportTest = { GalleryItem.CommandParameterProperty, RibbonGroupBox.LauncherCommandParameterProperty, RibbonGroupBox.LauncherToolTipProperty, SplitButton.CommandParameterProperty, SplitButton.DropDownToolTipProperty }; private static IEnumerable> GetTypesThatMustHaveLogicalChildSupport() { foreach (var keyValuePair in GetDependencyPropertiesWithPropertyTypeObject()) { yield return keyValuePair; } } private static IEnumerable> GetDependencyPropertiesWithPropertyTypeObject() { var types = typeof(Ribbon).Assembly.GetTypes() .Where(x => typeof(DependencyObject).IsAssignableFrom(x) && x.IsAbstract == false); foreach (var type in types) { var properties = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) .Where(x => typeof(DependencyProperty).IsAssignableFrom(x.FieldType)) .Select(x => x.GetValue(null)) .Cast(); foreach (var dependencyProperty in properties) { if (dependencyProperty.PropertyType == typeof(object)) { yield return new KeyValuePair(type, dependencyProperty); } } } } private static void TestLogicalTree(Type controlType, DependencyProperty property, DependencyPropertyKey propertyKey = null) { var control = (DependencyObject)Activator.CreateInstance(controlType, true); Assert.That(control, Is.Not.Null); { var value = new object(); SetValue(value); { var children = LogicalTreeHelper.GetChildren(control); if (excludedPropertiesForLogicalChildSupportTest.Contains(property)) { Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } else { Assert.That(children, Does.Contain(value), "Logical children must contain the value."); } } SetValue(null); { var children = LogicalTreeHelper.GetChildren(control); Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } } { var value = new DependencyObject(); SetValue(value); { var children = LogicalTreeHelper.GetChildren(control); if (excludedPropertiesForLogicalChildSupportTest.Contains(property)) { Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } else { Assert.That(children, Does.Contain(value), "Logical children must contain the value."); } } { var parent = LogicalTreeHelper.GetParent(value); Assert.That(parent, Is.Null, "Dependency-Objects don't support logical parents."); } SetValue(null); { var children = LogicalTreeHelper.GetChildren(control); Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } } { var value = new FrameworkElement(); SetValue(value); { var children = LogicalTreeHelper.GetChildren(control); if (excludedPropertiesForLogicalChildSupportTest.Contains(property)) { Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } else { Assert.That(children, Does.Contain(value), "Logical children must contain the value."); } } { var parent = LogicalTreeHelper.GetParent(value); if (excludedPropertiesForLogicalChildSupportTest.Contains(property)) { Assert.That(parent, Is.Not.EqualTo(control), "Parent should match."); } else { Assert.That(parent, Is.EqualTo(control), "Parent should match."); } } SetValue(null); { var children = LogicalTreeHelper.GetChildren(control); Assert.That(children, Does.Not.Contain(value), "Logical children must NOT contain the value."); } } void SetValue(object value) { if (propertyKey is null) { control.SetValue(property, value); } else { control.SetValue(propertyKey, value); } } } } }