diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/BitmapIconExtension.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/BitmapIconExtension.cs
new file mode 100644
index 00000000000..68ded4e2cea
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/BitmapIconExtension.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Markup;
+
+namespace Microsoft.Toolkit.Uwp.UI.Extensions
+{
+ ///
+ /// Custom which can provide values.
+ ///
+ [Bindable]
+ [MarkupExtensionReturnType(ReturnType = typeof(BitmapIcon))]
+ public sealed class BitmapIconExtension : MarkupExtension
+ {
+ ///
+ /// Gets or sets the representing the image to display.
+ ///
+ public Uri Source { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to display the icon as monochrome.
+ ///
+ public bool ShowAsMonochrome { get; set; }
+
+ ///
+ protected override object ProvideValue()
+ {
+ return new BitmapIcon
+ {
+ ShowAsMonochrome = ShowAsMonochrome,
+ UriSource = Source
+ };
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconExtension.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconExtension.cs
new file mode 100644
index 00000000000..8394c244fea
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconExtension.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Windows.UI.Text;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Markup;
+using Windows.UI.Xaml.Media;
+
+namespace Microsoft.Toolkit.Uwp.UI.Extensions
+{
+ ///
+ /// Custom which can provide values.
+ ///
+ [Bindable]
+ [MarkupExtensionReturnType(ReturnType = typeof(FontIcon))]
+ public class FontIconExtension : MarkupExtension
+ {
+ ///
+ /// Gets or sets the representing the icon to display.
+ ///
+ public string Glyph { get; set; }
+
+ ///
+ /// Gets or sets the size of the icon to display.
+ ///
+ public double FontSize { get; set; }
+
+ ///
+ /// Gets or sets the font family to use to display the icon. If , "Segoe MDL2 Assets" will be used.
+ ///
+ public FontFamily FontFamily { get; set; }
+
+ ///
+ /// Gets or sets the thickness of the icon glyph.
+ ///
+ public FontWeight FontWeight { get; set; } = FontWeights.Normal;
+
+ ///
+ /// Gets or sets the font style for the icon glyph.
+ ///
+ public FontStyle FontStyle { get; set; } = FontStyle.Normal;
+
+ ///
+ /// Gets or sets a value indicating whether automatic text enlargement, to reflect the system text size setting, is enabled.
+ ///
+ public bool IsTextScaleFactorEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the icon is mirrored when the flow direction is right to left.
+ ///
+ public bool MirroredWhenRightToLeft { get; set; }
+
+ ///
+ protected override object ProvideValue()
+ {
+ var fontIcon = new FontIcon
+ {
+ Glyph = Glyph,
+ FontFamily = FontFamily ?? new FontFamily("Segoe MDL2 Assets"),
+ FontWeight = FontWeight,
+ FontStyle = FontStyle,
+ IsTextScaleFactorEnabled = IsTextScaleFactorEnabled,
+ MirroredWhenRightToLeft = MirroredWhenRightToLeft
+ };
+
+ if (FontSize > 0)
+ {
+ fontIcon.FontSize = FontSize;
+ }
+
+ return fontIcon;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconSourceExtension.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconSourceExtension.cs
new file mode 100644
index 00000000000..6e909bd1cb2
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/FontIconSourceExtension.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Windows.UI.Text;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Markup;
+using Windows.UI.Xaml.Media;
+
+namespace Microsoft.Toolkit.Uwp.UI.Extensions
+{
+ ///
+ /// Custom which can provide values.
+ ///
+ [Bindable]
+ [MarkupExtensionReturnType(ReturnType = typeof(FontIconSource))]
+ public class FontIconSourceExtension : MarkupExtension
+ {
+ ///
+ /// Gets or sets the representing the icon to display.
+ ///
+ public string Glyph { get; set; }
+
+ ///
+ /// Gets or sets the size of the icon to display.
+ ///
+ public double FontSize { get; set; }
+
+ ///
+ /// Gets or sets the font family to use to display the icon. If , "Segoe MDL2 Assets" will be used.
+ ///
+ public FontFamily FontFamily { get; set; }
+
+ ///
+ /// Gets or sets the thickness of the icon glyph.
+ ///
+ public FontWeight FontWeight { get; set; } = FontWeights.Normal;
+
+ ///
+ /// Gets or sets the font style for the icon glyph.
+ ///
+ public FontStyle FontStyle { get; set; } = FontStyle.Normal;
+
+ ///
+ /// Gets or sets a value indicating whether automatic text enlargement, to reflect the system text size setting, is enabled.
+ ///
+ public bool IsTextScaleFactorEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the icon is mirrored when the flow direction is right to left.
+ ///
+ public bool MirroredWhenRightToLeft { get; set; }
+
+ ///
+ protected override object ProvideValue()
+ {
+ var fontIcon = new FontIconSource
+ {
+ Glyph = Glyph,
+ FontFamily = FontFamily ?? new FontFamily("Segoe MDL2 Assets"),
+ FontWeight = FontWeight,
+ FontStyle = FontStyle,
+ IsTextScaleFactorEnabled = IsTextScaleFactorEnabled,
+ MirroredWhenRightToLeft = MirroredWhenRightToLeft
+ };
+
+ if (FontSize > 0)
+ {
+ fontIcon.FontSize = FontSize;
+ }
+
+ return fontIcon;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/NullableBool.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/NullableBool.cs
index cb1831ea4ab..e3a5224dfa8 100644
--- a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/NullableBool.cs
+++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/NullableBool.cs
@@ -2,11 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;
diff --git a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDevice.cs b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDevice.cs
index 21fdf480344..866a54383a1 100644
--- a/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDevice.cs
+++ b/Microsoft.Toolkit.Uwp.UI/Extensions/Markup/OnDevice.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using Windows.ApplicationModel;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;
diff --git a/UnitTests/Extensions/Test_BitmapIconExtensionMarkupExtension.cs b/UnitTests/Extensions/Test_BitmapIconExtensionMarkupExtension.cs
new file mode 100644
index 00000000000..7eba199289f
--- /dev/null
+++ b/UnitTests/Extensions/Test_BitmapIconExtensionMarkupExtension.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Linq;
+using Microsoft.Toolkit.Uwp.UI.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Markup;
+
+namespace UnitTests.Extensions
+{
+ [TestClass]
+ public class Test_BitmapIconExtensionMarkupExtension
+ {
+ [TestCategory("BitmapIconExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_BitmapIconExtension_MarkupExtension_ProvideImage()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("RootButton") as Button;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(Button)} control in tree.");
+
+ var item = ((MenuFlyout)button.Flyout)?.Items?.FirstOrDefault() as MenuFlyoutItem;
+
+ Assert.IsNotNull(button, $"Could not find the target {nameof(MenuFlyoutItem)} control.");
+
+ var icon = item.Icon as BitmapIcon;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(BitmapIcon)} element in button.");
+
+ Assert.AreEqual(icon.UriSource, new Uri("ms-resource:///Files/Assets/StoreLogo.png"), "Expected ms-resource:///Files/Assets/StoreLogo.png uri.");
+ Assert.AreEqual(icon.ShowAsMonochrome, false, "Expected icon not to be monochrome");
+ }
+
+ [TestCategory("BitmapIconExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_BitmapIconExtension_MarkupExtension_ProvideImageAndMonochrome()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("RootButton") as Button;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(Button)} control in tree.");
+
+ var item = ((MenuFlyout)button.Flyout)?.Items?.FirstOrDefault() as MenuFlyoutItem;
+
+ Assert.IsNotNull(button, $"Could not find the target {nameof(MenuFlyoutItem)} control.");
+
+ var icon = item.Icon as BitmapIcon;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(BitmapIcon)} element in button.");
+
+ Assert.AreEqual(icon.UriSource, new Uri("ms-resource:///Files/Assets/StoreLogo.png"), "Expected ms-resource:///Files/Assets/StoreLogo.png uri.");
+ Assert.AreEqual(icon.ShowAsMonochrome, true, "Expected icon to be monochrome");
+ }
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/Extensions/Test_FontIconExtensionMarkupExtension.cs b/UnitTests/Extensions/Test_FontIconExtensionMarkupExtension.cs
new file mode 100644
index 00000000000..57021acb75e
--- /dev/null
+++ b/UnitTests/Extensions/Test_FontIconExtensionMarkupExtension.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Windows.UI.Text;
+using Microsoft.Toolkit.Uwp.UI.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Markup;
+
+namespace UnitTests.Extensions
+{
+ [TestClass]
+ public class Test_FontIconExtensionMarkupExtension
+ {
+ [TestCategory("FontIconExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconExtension_MarkupExtension_ProvideSegoeMdl2Asset()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as AppBarButton;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree.");
+
+ var icon = button.Icon as FontIcon;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE105", "Expected icon glyph to be E105.");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe MDL2 Assets", "Expected font family to be Segoe MDL2 Assets");
+ }
+
+ [TestCategory("FontIconExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconExtension_MarkupExtension_ProvideSegoeUI()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as AppBarButton;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree.");
+
+ var icon = button.Icon as FontIcon;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE14D", "Expected icon glyph to be E14D.");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe UI", "Expected font family to be Segoe UI");
+ }
+
+ [TestCategory("FontIconExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconExtension_MarkupExtension_ProvideCustomFontIcon()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as AppBarButton;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(AppBarButton)} control in tree.");
+
+ var icon = button.Icon as FontIcon;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE14D", "Expected icon glyph to be E14D.");
+ Assert.AreEqual(icon.FontSize, 7.0, "Expected font size of 7");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe MDL2 Assets", "Expected font family to be Segoe UI");
+ Assert.AreEqual(icon.FontWeight, FontWeights.Bold, "Expected bold font weight");
+ Assert.AreEqual(icon.FontStyle, FontStyle.Italic, "Expected italic font style");
+ Assert.AreEqual(icon.IsTextScaleFactorEnabled, true, "Expected IsTextScaleFactorEnabled set to true");
+ Assert.AreEqual(icon.MirroredWhenRightToLeft, true, "Expected MirroredWhenRightToLeft set to true");
+ }
+ }
+}
diff --git a/UnitTests/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs b/UnitTests/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs
new file mode 100644
index 00000000000..1c1c81852a7
--- /dev/null
+++ b/UnitTests/Extensions/Test_FontIconSourceExtensionMarkupExtension.cs
@@ -0,0 +1,113 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using Windows.UI.Text;
+using Microsoft.Toolkit.Uwp.UI.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Markup;
+
+namespace UnitTests.Extensions
+{
+ [TestClass]
+ public class Test_FontIconSourceExtensionMarkupExtension
+ {
+ [TestCategory("FontIconSourceExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconSourceExtension_MarkupExtension_ProvideSegoeMdl2Asset()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as MockSwipeItem;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree.");
+
+ var icon = button.IconSource as FontIconSource;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE105", "Expected icon glyph to be E105.");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe MDL2 Assets", "Expected font family to be Segoe MDL2 Assets");
+ }
+
+ [TestCategory("FontIconSourceExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconSourceExtension_MarkupExtension_ProvideSegoeUI()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as MockSwipeItem;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree.");
+
+ var icon = button.IconSource as FontIconSource;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE14D", "Expected icon glyph to be E14D.");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe UI", "Expected font family to be Segoe UI");
+ }
+
+ [TestCategory("FontIconSourceExtensionMarkupExtension")]
+ [UITestMethod]
+ public void Test_FontIconSourceExtension_MarkupExtension_ProvideCustomFontIcon()
+ {
+ var treeroot = XamlReader.Load(@"
+
+") as FrameworkElement;
+
+ var button = treeroot.FindChildByName("Check") as MockSwipeItem;
+
+ Assert.IsNotNull(button, $"Could not find the {nameof(MockSwipeItem)} control in tree.");
+
+ var icon = button.IconSource as FontIconSource;
+
+ Assert.IsNotNull(icon, $"Could not find the {nameof(FontIcon)} element in button.");
+
+ Assert.AreEqual(icon.Glyph, "\uE14D", "Expected icon glyph to be E14D.");
+ Assert.AreEqual(icon.FontSize, 7.0, "Expected font size of 7");
+ Assert.AreEqual(icon.FontFamily.Source, "Segoe MDL2 Assets", "Expected font family to be Segoe UI");
+ Assert.AreEqual(icon.FontWeight, FontWeights.Bold, "Expected bold font weight");
+ Assert.AreEqual(icon.FontStyle, FontStyle.Italic, "Expected italic font style");
+ Assert.AreEqual(icon.IsTextScaleFactorEnabled, true, "Expected IsTextScaleFactorEnabled set to true");
+ Assert.AreEqual(icon.MirroredWhenRightToLeft, true, "Expected MirroredWhenRightToLeft set to true");
+ }
+ }
+
+ [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402", Justification = "Mock control for tests")]
+ public class MockSwipeItem : Control
+ {
+ public IconSource IconSource
+ {
+ get => (IconSource)GetValue(IconSourceProperty);
+ set => SetValue(IconSourceProperty, value);
+ }
+
+ public static readonly DependencyProperty IconSourceProperty =
+ DependencyProperty.Register(
+ nameof(IconSource),
+ typeof(IconSource),
+ typeof(MockSwipeItem),
+ new PropertyMetadata(default(IconSource)));
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj
index b4208dc4fef..acabdafd438 100644
--- a/UnitTests/UnitTests.csproj
+++ b/UnitTests/UnitTests.csproj
@@ -136,9 +136,12 @@
+
+
+