I'm trying to move some XAML defining MenuItem into a Style.
I've got the following working XAML:
<Menu x:Name="menu" Height="19" Margin="10,10,10.333,0" VerticalAlignment="Top">
<MenuItem Header="_Open">
<MenuItem.Icon>
<Viewbox>
<ContentControl Content="{DynamicResource appbar.folder.open}" RenderTransformOrigin="0.5,0.5">
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
</MenuItem.Icon>
</MenuItem>
</Menu>
where the resource looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Key="appbar.folder.open" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
</Canvas>
</ResourceDictionary>
As a sidenote, i also don't know how to get rid of the explicit scaling. It seems to be a minor problem but i would appreciate if this could be solved as well.
Anyway, to move as much as possible of this somewhat too expressive definition into a Style, I created code for an attached property of type Visual
namespace extensions
{
public class AttachedProperties
{
public static readonly DependencyProperty VisualIconProperty =
DependencyProperty.RegisterAttached("VisualIcon",
typeof(System.Windows.Media.Visual), typeof(AttachedProperties),
new PropertyMetadata(default(System.Windows.Media.Visual)));
public static void SetVisualIcon(UIElement element, System.Windows.Media.Visual value)
{
element.SetValue(VisualIconProperty, value);
}
public static System.Windows.Media.Visual GetVisualIcon(UIElement element)
{
return (System.Windows.Media.Visual)element.GetValue(VisualIconProperty);
}
}
}
redefined the menu item
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle}"
extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
and tried to create a Style which reads the VisualIcon property to set the Icon content
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="MenuItem.Icon">
<Setter.Value>
<Viewbox>
<ContentControl RenderTransformOrigin="0.5,0.5">
<ContentControl.Content>
<!-- this would work but doesn't reference the VisualIcon property.. <DynamicResource ResourceKey="appbar.folder.open"/> -->
<!-- these do not -->
<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource FindAncestor, AncestorType=MenuItem}"/>-->
<!--<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource TemplatedParent}"/>-->
<!--<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource Self}"/>-->
</ContentControl.Content>
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
</Setter.Value>
</Setter>
</Style>
but failed. Referencing the resource with a DynamicResource and a static key works, but i cannot get any binding using the attached property to work.
Since I'm a WPF beginner, I'm not sure if this is a good approach anyway.
[EDIT1]
I tested all the provided solutions. Grek40's answer didn't display any icons for me, neither in design view nor during runtime; maybe I did something completely wrong.
The second approach in grx70's second answer it the most promising for me as it reliably displays the icon in the design view as well as during runtime. However, it seems like there's a difference between Win7 and Win10 i don't understand. I tested the identical source (on a external drive) on Win7 and Win10. For Win10, it looks nice, but Win7 draws the icons way too big.
(Note: The reason is give in this comment)
Here's the window testcode:
<Window.Resources>
<Style x:Key="MenuItemStyle" TargetType="controls:MenuItemEx">
<Setter Property="MenuItem.Icon">
<Setter.Value>
<Viewbox>
<ContentControl RenderTransformOrigin="0.5,0.5">
<ContentControl.Content>
<Binding Path="(extensions:AttachedProperties.VisualIcon)" RelativeSource="{RelativeSource FindAncestor, AncestorType=MenuItem}"/>
</ContentControl.Content>
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MenuItemStyle2" TargetType="MenuItem">
<Setter Property="uihelpers:MenuItemHelper.IsEnabled" Value="True" />
<Setter Property="MenuItem.Icon">
<Setter.Value>
<Viewbox>
<ContentControl RenderTransformOrigin="0.5,0.5"
Content="{Binding
Path=(uihelpers:MenuItemHelper.MenuItem).(extensions:AttachedProperties.VisualIcon),
RelativeSource={RelativeSource AncestorType=Viewbox}}">
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MenuItemStyle3" TargetType="{x:Type MenuItem}">
<Style.Resources>
<DataTemplate x:Key="MenuItemStyle3dt" DataType="{x:Type Style}">
<Path Style="{Binding}"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</DataTemplate>
</Style.Resources>
</Style>
<Style x:Key="appbar.folder.open.style3.local" TargetType="{x:Type Path}">
<Setter Property="Fill" Value="Black" />
<Setter Property="Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
</Style>
<extensions:PathStyle x:Key="appbar.folder.open.style3b">
<Setter Property="Path.HorizontalAlignment" Value="Center" />
<Setter Property="Path.VerticalAlignment" Value="Center" />
<Setter Property="Path.Stretch" Value="Uniform" />
<Setter Property="Path.Fill" Value="Black" />
<Setter Property="Path.Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
</extensions:PathStyle>
<DataTemplate DataType="{x:Type extensions:PathStyle}">
<Path Style="{Binding}" />
</DataTemplate>
<Canvas x:Key="appbar.folder.open.style4.local" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
</Canvas>
<Viewbox x:Key="MenuItemStyle4.Icon" x:Shared="False">
<ContentControl Content="{Binding Path=Tag,RelativeSource={RelativeSource AncestorType=MenuItem}}" RenderTransformOrigin="0.5,0.5">
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
<Style x:Key="MenuItemStyle4" TargetType="MenuItem">
<Setter Property="Icon" Value="{StaticResource MenuItemStyle4.Icon}"/>
</Style>
</Window.Resources>
<Grid>
<Menu x:Name="menu" Height="19" Margin="10,10,10.333,0" VerticalAlignment="Top">
<MenuItem Header="_File">
<controls:MenuItemEx Header="_Open"
Style="{StaticResource MenuItemStyle}"
extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle2}"
extensions:AttachedProperties.VisualIcon="{DynamicResource appbar.folder.open}" />
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle3}"
Icon="{DynamicResource appbar.folder.open.style3}" />
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle3}"
Icon="{DynamicResource appbar.folder.open.style3.local}" />
<MenuItem Header="_Open" Icon="{DynamicResource appbar.folder.open.style3b}" />
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle4}"
Tag="{DynamicResource appbar.folder.open}" />
<MenuItem Header="_Open"
Style="{StaticResource MenuItemStyle4}"
Tag="{DynamicResource appbar.folder.open.style4.local}" />
<MenuItem Header="_Save">
<MenuItem.Icon>
<Viewbox>
<ContentControl Content="{DynamicResource appbar.save}" RenderTransformOrigin="0.5,0.5">
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</ContentControl.RenderTransform>
</ContentControl>
</Viewbox>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<Menu x:Name="menu2" Height="19" Margin="9,32,11.333,0" VerticalAlignment="Top" ItemContainerStyle="{StaticResource MenuItemStyle4}">
<MenuItem Header="_File">
<MenuItem Header="_Open"
Tag="{DynamicResource appbar.folder.open.style4.local}" />
<MenuItem Header="_Open"
Tag="{DynamicResource appbar.folder.open}" />
</MenuItem>
</Menu>
</Grid>
and here are the global resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Key="appbar.folder.open" x:Shared="False" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="26" Canvas.Left="19" Canvas.Top="24" Stretch="Fill" Fill="#FF000000" Data="F1 M 19,50L 28,34L 63,34L 54,50L 19,50 Z M 19,28.0001L 35,28C 36,25 37.4999,24.0001 37.4999,24.0001L 48.75,24C 49.3023,24 50,24.6977 50,25.25L 50,28L 53.9999,28.0001L 53.9999,32L 27,32L 19,46.4L 19,28.0001 Z "/>
</Canvas>
<Style x:Key="appbar.folder.open.style3" TargetType="{x:Type Path}">
<Setter Property="Fill" Value="Black" />
<Setter Property="Data" Value="M0,26 L9,10 L44,10 L35,26 Z M0,4 L16,4 C17,1 18.5,0 18.5,0 L29.75,0 C30.3,0 31,0.7 31,1.25 L31,4 L34,4 L34,8 L8,8 L0,22.4 Z" />
</Style>
</ResourceDictionary>
and
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Key="appbar.save" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="34.8333" Height="34.8333" Canvas.Left="20.5833" Canvas.Top="20.5833" Stretch="Fill" Fill="#FF000000" Data="F1 M 20.5833,20.5833L 55.4167,20.5833L 55.4167,55.4167L 45.9167,55.4167L 45.9167,44.3333L 30.0833,44.3333L 30.0833,55.4167L 20.5833,55.4167L 20.5833,20.5833 Z M 33.25,55.4167L 33.25,50.6667L 39.5833,50.6667L 39.5833,55.4167L 33.25,55.4167 Z M 26.9167,23.75L 26.9167,33.25L 49.0833,33.25L 49.0833,23.75L 26.9167,23.75 Z "/>
</Canvas>
</ResourceDictionary>
which are merged in app.xaml:
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="icons/appbar.folder.open.xaml"/>
<ResourceDictionary Source="icons/appbar.save.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The reason the icons are offset is that i took them more or less 1:1 from github.com/Templarian/WindowsIcons and was hoping that because they're provided in this format (i also tried to save them as brushes, first with Inkscape and then with Expression Design) it would be common to use them like this.