Chuck Savage
Chuck Savage

Reputation: 11945

Display icon over select images in wpf listbox

I've looked at some related answers (Content of a Button Style appears only in one Button instance, and Images only showing in last ListBoxItem), but can't seem to get their answers to work in my example.

My app wpf stack is relatively complex.

I've a UserControl within another window. Within the UserControl, I've a ListBox with nested elements ListBox.ItemTemplate > DataTemplate > Border > Grid > StackPanel

Within the StackPanel is a TextBlock, followed by an Image and a StackPanel.ToolTip

I'm wanting to place an icon over the Image, so I've further obfuscated the image by putting it in a Grid, and adding a ViewBox accessed via a Control Template (as suggested in the above links), so that the ViewBox is centered on the image. Here's the Grid:

<Grid>
    <Image RenderOptions.BitmapScalingMode="HighQuality"
Height="{Binding ElementName=_this, Path=ThumbSize.Height}"
>
        <gif:ImageBehavior.AnimatedSource>
            <MultiBinding Converter="{c:ImageConverter}">
                <Binding Path="ThumbLocation" />
                <Binding Path="FullName" />
            </MultiBinding>
        </gif:ImageBehavior.AnimatedSource>
    </Image>
    <Control Template="{StaticResource PlaySymbol}" Visibility="{Binding PlayVisible}" />
</Grid>

The ViewBox's ControlTemplate is in the UserControl.Resources up at the top

<ControlTemplate x:Key="PlaySymbol" TargetType="{x:Type Control}">
    <Viewbox Stretch="Uniform" 
                    RenderTransformOrigin="0.5,0.5" 
                    Opacity="0.75"
                x:Shared="False"
                    >
        <Viewbox.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
            </TransformGroup>
        </Viewbox.RenderTransform>
        <ContentControl Content="{StaticResource appbar_control_play}" />
    </Viewbox>
</ControlTemplate>

The appbar_control_play is in the Resources directory in an Icons.xaml file.

<Canvas x:Key="appbar_control_play" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
    <Path Width="20.5832" Height="31.6667" Canvas.Left="30.0833" Canvas.Top="22.1667" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z "/>
</Canvas>

The goal is to only display the icon for 'play' on movies. I've set the PlayVisible to return the proper visibility for movies, and not for other files. Yet, it is only displaying for the last movie. I've heard that this is the case for controls only able to have one parent. I've tried setting x:Shared="False" on the ViewBox, but to no avail.

The app works, but I've recently decided to add movies to the listing and want to display the play icon over their thumbnails, but not the other items. It seems simple on the outset, but I've yet to figure out what is needed.

Any help would be appreciated, otherwise I feel I may have to resort to overlaying the icon on the actual thumbnails of the movies.

Upvotes: 0

Views: 527

Answers (2)

BionicCode
BionicCode

Reputation: 28948

It looks like the problem is not related to the Viewbox but the image resource appbar_control_play it references.

There is no need to add the Viewbox via a Control. Just add it directly to the DataTemplate.

Generally prefer a ContentControl over a templated Control if you wish to display content.

The x:Shared attribute is only required on a UIElement that is not part of a template but defined in a ResourceDictionary. For example, when you define the Viewbox in as a resource, you must set the x:Shared attribute to false. Otherwise it is only allowed to appear once in the visual tree.

  1. In case the image resource is an image file, a proper DataTemplate could look as followed:
<DataTemplate>
  <StackPanel>
    <Grid>
      <Image Source="path to image" />
      <Image Source="path to overlay icon"
             Stretch="UniformToFill"
             Width="50"
             Height="50" />
    </Grid>
  </StackPanel>
</DataTemplate>
  1. In case the icon is a XAML resource like a Geometry or a Segoe MDL2 Assets font icon, the DataTemplate should look as followed:

App.xaml

<Application.Resources>
  <Viewbox x:Key="PlayIcon" x:Shared="False">
    <TextBlock FontFamily="Segoe MDL2 Assets"
               Text="&#xE768;" />
  </Viewbox>

  <Viewbox x:Key="appbar_control_play"
           x:Shared="False">
    <Path Width="20.5832"
          Height="31.6667"
          Stretch="Fill"
          Fill="{Binding RelativeSource={RelativeSource AncestroType=ContentControl}, Path=For4ground}"
          Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z " />
  </Viewbox>
</Application.Resources>

MyControl.xaml

<ListBox.ItemTemplate>
  <DataTemplate>
    <StackPanel>
      <Grid>
        <Image Source="path to image" />
        <ContentControl Content="{StaticResource appbar_control_play}"
                        Width="50"
                        Height="50"
                        Foreground="Pink" />
      </Grid>
    </StackPanel>
  </DataTemplate>
</ListBox.ItemTemplate>

Upvotes: 1

aybe
aybe

Reputation: 16652

Not sure what's happening on your side but the following just works:

<Window x:Class="abc.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:system="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="Window1">
    <Grid>
        <ListBox>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <DataTemplate.Resources>
                        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
                    </DataTemplate.Resources>

                    <Border Width="64" Height="64" BorderBrush="Red" BorderThickness="1">
                        <Grid>
                            <TextBlock Text="{Binding}"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center" />

                            <Rectangle Fill="DeepSkyBlue"
                                       HorizontalAlignment="Right"
                                       VerticalAlignment="Bottom"
                                       Margin="2"
                                       Width="16"
                                       Height="16"
                                       Visibility="{Binding Converter={StaticResource BooleanToVisibilityConverter}}"
                                       x:Name="Button" />
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <system:Boolean>True</system:Boolean>
            <system:Boolean>False</system:Boolean>
            <system:Boolean>True</system:Boolean>
            <system:Boolean>False</system:Boolean>
            <system:Boolean>True</system:Boolean>
            <system:Boolean>False</system:Boolean>
        </ListBox>
    </Grid>
</Window>

enter image description here

Upvotes: 0

Related Questions