I have started to move various common Image
s into a ResourceDictionary
and noticed an odd behavior in my WPF application. If the Image
is used in a MenuItem
and in a Button
on a ToolBar
, when I open the Menu
the image disappears on the Button
.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Image x:Key="NewImage"
Source="/SomeApplication;component/Resources/NewDocumentHS.png"
Stretch="None"/>
<!-- ... -->
Relevant XAML from the Window
:
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New"
Command="{Binding NewCommand}"
Icon="{DynamicResource NewImage}" />
<!-- ... -->
<ToolBarTray>
<ToolBar>
<Button Command="{Binding NewCommand}"
Content="{DynamicResource NewImage}" />
I assume this is a caveat of resources in a ResourceDictionary
, but I am unable to discover the appropriate fix for this. Behavior occurs with both StaticResource
and DynamicResource
. It also doesn't appear to be affected by if the ResourceDictionary
stands on its own or if it is merged with others. No other resource shares that key either.
Edit: Additionally, adding PresentationOptions:Freeze="True"
to the images did not change the situation.
The Image class is a visual, so it can only appear in the visual tree in one location. Therefore, you cannot share it among multiple MenuItems/Buttons/etc.
You can however share the ImageSource (i.e. Image.Source) value.
In WPF, I believe you can use x:Shared="False" to force WPF to create a new instance for each request though.
You cannot use an Image control in multiple places, it can only be appear in the Visual Tree at one place, so if the call to the resource is made the image is being snatched from the previous owner.
Edit: x:Shared="False"
Is obviously a better solution than all of my suggestions below, i wonder why such an important property does not show up in the Intellisense -_-
This behaviour is a bit of a pain, i normally use to predefine a IconStyle
and the BitmapImages
for the Source of the images but create new Images for every MenuItem
where i might need it.
You can also create a DataTemplate for your Icon:
Resources:
<Style x:Key="IconImageStyle" TargetType="{x:Type Image}">
<Setter Property="MaxWidth" Value="16"/>
<Setter Property="MaxHeight" Value="16"/>
</Style>
<DataTemplate x:Key="Icon_Close_Template">
<Image Style="{StaticResource IconImageStyle}"
Source="pack://application:,,,/Images/Close.ico"/>
</DataTemplate>
Usage:
<Menu>
<MenuItem Header="File">
<MenuItem Header="Close">
<MenuItem.Icon>
<ContentPresenter ContentTemplate="{StaticResource Icon_Close_Template}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Close">
<MenuItem.Icon>
<ContentPresenter ContentTemplate="{StaticResource Icon_Close_Template}"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
Since templates are created via factories this will work, still significantly inflates the XAML though...
To get around this you could for example write a markup extension, this one is very simple and only copies the values of the Source
and Style
properties, you could also use reflection or other means to create a complete copy:
[MarkupExtensionReturnType(typeof(object))]
public class IconExtension : MarkupExtension
{
private Image icon;
public Image Icon
{
get { return icon; }
set { icon = value; }
}
public IconExtension() { }
public IconExtension(Image icon)
{
Icon = icon;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (Icon == null) throw new ArgumentNullException("Icon");
return new Image() { Source = Icon.Source, Style = Icon.Style };
}
}
Can be used like this:
<Style x:Key="IconImageStyle" TargetType="{x:Type Image}">
<Setter Property="MaxWidth" Value="16"/>
<Setter Property="MaxHeight" Value="16"/>
</Style>
<Image x:Key="Icon_Close" Style="{StaticResource IconImageStyle}" Source="pack://application:,,,/Images/Close.ico"/>
<!-- ... -->
<MenuItem Header="File">
<MenuItem Header="Close" Icon="{m:Icon {StaticResource Icon_Close}}"/>
<MenuItem Header="Close" Icon="{m:Icon {StaticResource Icon_Close}}"/>
</MenuItem>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With