diff --git a/Changelog.md b/Changelog.md index aabf3dfe8..760c7c40e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -123,6 +123,7 @@ - [#688](../../issues/688) - Backstage and StartScreen closing when pressing Alt - [#698](../../issues/698) - Submenus in the application menu are not opened each time - [#704](../../issues/704) - CheckBox.Header - InvalidCastException + - [#705](../../issues/705) - ApplicationMenu header can't be set to text - [#714](../../issues/714) - ResizeMode="NoResize" and ShowInTaskbar="False" causes crash on startup - [#722](../../issues/722) - NullReferenceException in KeyTipService.OnAdornerChainTerminated - [#730](../../issues/730) - Add null check for Application.Current to ThemeManager (thanks @Evangelink) diff --git a/Fluent.Ribbon.Tests/Converters/ObjectToImageConverterTests.cs b/Fluent.Ribbon.Tests/Converters/ObjectToImageConverterTests.cs new file mode 100644 index 000000000..0a217291c --- /dev/null +++ b/Fluent.Ribbon.Tests/Converters/ObjectToImageConverterTests.cs @@ -0,0 +1,45 @@ +namespace Fluent.Tests.Converters +{ + using System; + using System.Linq; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Markup; + using System.Windows.Media; + using Fluent.Converters; + using NUnit.Framework; + + [TestFixture] + public class ObjectToImageConverterTests + { + [Test] + public void TestDynamicResource() + { + var fluentRibbonImagesApplicationmenuResourceKey = (object)"Fluent.Ribbon.Images.ApplicationMenu"; + + var expressionType = typeof(ResourceReferenceExpressionConverter).Assembly.GetType("System.Windows.ResourceReferenceExpression"); + + var expression = Activator.CreateInstance(expressionType, fluentRibbonImagesApplicationmenuResourceKey); + + var convertedValue = StaticConverters.ObjectToImageConverter.Convert(new object[] + { + expression, // value to convert + new ApplicationMenu() // target visual + }, null, null, null); + + Assert.That(convertedValue, Is.Not.Null); + Assert.That(convertedValue, Is.InstanceOf()); + + var convertedImageValue = (Image)convertedValue; + Assert.That(convertedImageValue.Source, Is.InstanceOf()); + + var drawingImage = (DrawingImage)convertedImageValue.Source; + Assert.That(drawingImage.Drawing, Is.InstanceOf()); + + var drawingGroup = (DrawingGroup)drawingImage.Drawing; + + Assert.That(drawingGroup.Children.Cast().Select(x => x.Geometry.ToString()), + Is.EquivalentTo(((DrawingGroup)((DrawingImage)Application.Current.FindResource(fluentRibbonImagesApplicationmenuResourceKey)).Drawing).Children.Cast().Select(x => x.Geometry.ToString()))); + } + } +} \ No newline at end of file diff --git a/Fluent.Ribbon/Converters/ObjectToImageConverter.cs b/Fluent.Ribbon/Converters/ObjectToImageConverter.cs index c2f706fa2..8f9c06fad 100644 --- a/Fluent.Ribbon/Converters/ObjectToImageConverter.cs +++ b/Fluent.Ribbon/Converters/ObjectToImageConverter.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; + using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Data; @@ -360,6 +361,46 @@ public static ImageSource CreateImageSource(object value, Visual targetVisual, S return ExtractImageSource(icon, targetVisual, desiredSize); } + // !!! Danger zone ahead !!! + // !!! Please do not copy that code somewhere and blame me for failures !!! + // Hack to get the value from resource expressions + { + if (targetVisual is null == false // to get values for resource expressions we need a DependencyObject + && value is Expression expression) + { + var type = expression.GetType(); + var method = type.GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (method != null) + { + var valueFromExpression = method.Invoke(expression, new object[] + { + targetVisual, + // to get values from resource expressions we need a DependencyProperty, so just pass a random one + RibbonProperties.SizeProperty + }); + + return CreateImageSource(valueFromExpression, targetVisual, desiredSize); + } + } + + if (value.GetType().InheritsFrom("DeferredReference")) + { + var type = value.GetType(); + var method = type.GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + if (method != null) + { + var valueFromDeferredReference = method.Invoke(value, new object[] + { + null + }); + + return CreateImageSource(valueFromDeferredReference, targetVisual, desiredSize); + } + } + } + return null; } diff --git a/Fluent.Ribbon/Internal/TypeHelper.cs b/Fluent.Ribbon/Internal/TypeHelper.cs new file mode 100644 index 000000000..ebcac0a3e --- /dev/null +++ b/Fluent.Ribbon/Internal/TypeHelper.cs @@ -0,0 +1,26 @@ +namespace Fluent.Internal +{ + using System; + + internal static class TypeHelper + { + public static bool InheritsFrom(this Type type, string typeName) + { + var currentType = type; + + do + { + if (currentType.Name == typeName) + { + return true; + } + + currentType = currentType.BaseType; + } + while (currentType != null + && currentType != typeof(object)); + + return false; + } + } +} \ No newline at end of file diff --git a/Fluent.Ribbon/Themes/Controls/ApplicationMenu.xaml b/Fluent.Ribbon/Themes/Controls/ApplicationMenu.xaml index a4904b0cd..4b8b91769 100644 --- a/Fluent.Ribbon/Themes/Controls/ApplicationMenu.xaml +++ b/Fluent.Ribbon/Themes/Controls/ApplicationMenu.xaml @@ -19,7 +19,7 @@ Height="23" Background="{TemplateBinding Background}"> @@ -129,7 +129,7 @@ - + \ No newline at end of file