In a simple view I want to display two buttons, that have as contents images, that are provided via resources in an extra style.xaml that is loaded in the App.xaml.
<BitmapImage x:Key="AddIcon" UriSource="pack://application:,,,/WpfTestBench;component/Images/plus.png"></BitmapImage>
<BitmapImage x:Key="RemoveIcon" UriSource="pack://application:,,,/WpfTestBench;component/Images/minus.png"></BitmapImage>
<Style x:Key="AddButtonWithIconStyle" TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="{DynamicResource AddIcon}" Stretch="None"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="RemoveButtonWithIconStyle" TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="{DynamicResource RemoveIcon}" Stretch="None"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Setter.Value>
</Setter>
</Style>
In my view I use the styles like this:
<DockPanel Margin="0,0,5,0" DockPanel.Dock="Bottom" LastChildFill="False" >
<Button Command="{Binding DeleteCommand}" DockPanel.Dock="Right"
Style="{StaticResource RemoveButtonWithIconStyle}"/>
<Button Command="{Binding AddCommand}" DockPanel.Dock="Right" Margin="5,0"
Style="{StaticResource AddButtonWithIconStyle}"/>
</DockPanel>
So far this looks good. But when I add another view into this view via a ContentPresenter, that has basically the same content (again with the above described button styles) the display of the first two buttons (of the parent view) does not work anymore. All I get ist two small circles with the button functionality.
<ContentPresenter Content="{Binding SomeEmbeddedViewModel, UpdateSourceTrigger=PropertyChanged}"/>
Why does this happen? Does the ContentPresenter somehow prevent to share the resources?
The core reason of this behavior is that an element can only appear once in the visual tree.
First of all, resources in a ResourceDictionary
are by default shared, i.e. every time you fetch a resource with a particular key, you always get the same instance. In your case, you always get the same instance of Style
, which also means that there's always only one instance of Image
(for each style).
So if you apply the same style to two buttons, the framework tries to put the same Image
instance in two different places, which is not allowed. To avoid that, upon attempt to load the image into the visual tree, if it already is in the visual tree, it is unloaded from the previous location first.
That's why the image is only visible in the last button (in order in which they were loaded).
There are basically two solutions to this problem.
1. Disable resource sharing
You can disable the resource sharing so that each time you fetch a resource from a ResourceDictionary
you get a new instance of the resource. In order to do that, you set x:Shared="False"
on your resource:
<Style x:Key="AddButtonWithIconStyle" x:Shared="False" TargetType="{x:Type Button}">
(...)
</Style>
2. Use ContentTemplate
in your style
Instead of putting the image as the content of the button, you could define a ContentTemplate
, which is realized separately for each button it is applied to (so each button gets its own instance of the image):
<Style x:Key="RemoveButtonWithIconStyle" TargetType="{x:Type Button}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{DynamicResource RemoveIcon}" Stretch="None"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Personally I'd advise you to use the second solution, since that's exactly the purpose of templates in WPF.