10

In WPF, I'm programmatically adding a context menu to a control.

    var contextMenu = new ContextMenu();
    contextMenu.Items.Add(new MenuItem { Header = "Copy All", Icon  = FindResource("CopyImage") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy All with Headers", Icon = FindResource("CopyImage") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy Selected", Icon = FindResource("CopyImage") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy Selected with Headers", Icon = FindResource("CopyImage") });

CopyImage is defined in my application resource.

<Image x:Key="CopyImage" Source="../Images/copy.png"/>

At runtime, only the last menu item shows the icon. The other three menu items do not.

enter image description here

Does anyone have an explanation for this behavior?

Igor Pashchuk
  • 2,455
  • 2
  • 22
  • 29

5 Answers5

8

Take a look at this article.

It explains that an Image can only be used in one place at a time. That would explain why it only ended up on the most recent assignment you made in code. Instead, define a BitmapImage and then create a new image using the BitmapImage as the source for each menu item.

From other article:

To do this, create a BitmapSource as a resource somewhere:

<BitmapImage x:Key="MyImageSource" UriSource="../Media/Image.png" />

Then, in your code, use something like:

<Image Source="{StaticResource MyImageSource}" />
Community
  • 1
  • 1
Nathan A
  • 11,059
  • 4
  • 47
  • 63
  • I have the same issue. I tried to put a `TextBox` instead of an Image in the `Icon` property... same problem. – Shachar Har-Shuv Feb 26 '18 at 14:36
  • This does not really solve the issue because it makes the assumption you are using a bitmap. I was using a rectangle with a border around it and it seems the only true way is to create a converter which returns a new UI Element object for each item in the menu. This way you can also make the converter return different types depending on the binding. – kenjara Jul 14 '20 at 12:47
4

Each UI Element can only be placed in one location in the visual tree. You can't use the same Image control on multiple MenuItem's. You need to create separate Image controls for each MenuItem. Otherwise each time you assign it to a new MenuItem, you are just moving it from one to the next.

<Image x:Key="CopyImage1" Source="../Images/copy.png"/>
<Image x:Key="CopyImage2" Source="../Images/copy.png"/>
<Image x:Key="CopyImage3" Source="../Images/copy.png"/>
<Image x:Key="CopyImage4" Source="../Images/copy.png"/>

var contextMenu = new ContextMenu();
    contextMenu.Items.Add(new MenuItem { Header = "Copy All", Icon  = FindResource("CopyImage1") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy All with Headers", Icon = FindResource("CopyImage2") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy Selected", Icon = FindResource("CopyImage3") });
    contextMenu.Items.Add(new MenuItem { Header = "Copy Selected with Headers", Icon = FindResource("CopyImage4") });
Glen Thomas
  • 10,190
  • 5
  • 33
  • 65
3

Try this, Icon = new BitmapImage(new Uri("images/copy.png", UriKind.Relative))

var contextMenu = new ContextMenu();
contextMenu.Items.Add(new MenuItem { Header = "Copy All", Icon  = new BitmapImage(new Uri("images/copy.png", UriKind.Relative)) });
contextMenu.Items.Add(new MenuItem { Header = "Copy All with Headers", Icon = new BitmapImage(new Uri("images/copy.png", UriKind.Relative)) });
contextMenu.Items.Add(new MenuItem { Header = "Copy Selected", Icon = new BitmapImage(new Uri("images/copy.png", UriKind.Relative)) });
contextMenu.Items.Add(new MenuItem { Header = "Copy Selected with Headers", Icon = new BitmapImage(new Uri("images/copy.png", UriKind.Relative)) });
NASSER
  • 5,900
  • 7
  • 38
  • 57
0

All answers were helpful. Here is what I ended up doing based on the pointers from @NathanA:

var contextMenu = new ContextMenu();
contextMenu.Items.Add(new MenuItem
{
    Header = "Copy All",
    Icon = new Image {Source = FindResource("CopyImageSource") as ImageSource}
});
contextMenu.Items.Add(new MenuItem
{
    Header = "Copy All with Headers",
    Icon = new Image {Source = FindResource("CopyImageSource") as ImageSource}
});
contextMenu.Items.Add(new MenuItem
{
    Header = "Copy Selected",
    Icon = new Image {Source = FindResource("CopyImageSource") as ImageSource}
});
contextMenu.Items.Add(new MenuItem
{
    Header = "Copy Selected with Headers",
    Icon = new Image {Source = FindResource("CopyImageSource") as ImageSource}
});

And this in the resource dictionary:

<BitmapImage x:Key="CopyImageSource" UriSource="../Images/copy.png"/>
Igor Pashchuk
  • 2,455
  • 2
  • 22
  • 29
-1

For a solution that works with any type and remains flexible I used a converter.

This gives you the flexibility to return completely different components depending on your result. In my case though I was only binding to a hex value.

XAML

<ContextMenu d:DataContext="{d:DesignInstance Type=applicantDetails:ApplicantSelections}" x:Key="HeatContextMenu" DataContext="{x:Static applicantDetails:ApplicantSelections.StaticRefApplicantSelections}" ItemsSource="{Binding TodoHeats}">
                    <ContextMenu.ItemContainerStyle>
                        <Style TargetType="MenuItem">
                            <Setter Property="Icon" Value="{Binding HexColour, Converter={StaticResource HeatMenuItemConverter}}" >
                            </Setter>
                        </Style>
                    </ContextMenu.ItemContainerStyle>
                    <ContextMenu.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Description}" d:DataContext="{d:DesignInstance Type=todoManager:TodoHeat}"></TextBlock>
                        </DataTemplate>
                    </ContextMenu.ItemTemplate>
                </ContextMenu>

Converter

class HeatMenuItemConverter : IValueConverter
{
    /// <summary>
    /// Converts the specified value.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <param name="targetType">Type of the target.</param>
    /// <param name="parameter">The parameter.</param>
    /// <param name="culture">The culture.</param>
    /// <returns></returns>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Border border = new Border();
        border.BorderBrush = Brushes.Black;
        Rectangle rectangle = new Rectangle();
        border.Child = rectangle;
        rectangle.Height = 50;
        rectangle.Width = 50;

        try
        {
            if (value != null && value is string hexCode)
            {
                rectangle.Fill = (SolidColorBrush)new BrushConverter().ConvertFromString(hexCode);
            }
        }
        catch (Exception e)
        {
            // ignored
        }

        return border;
    }

    /// <summary>
    /// Converts it back.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <param name="targetType">Type of the target.</param>
    /// <param name="parameter">The parameter.</param>
    /// <param name="culture">The culture.</param>
    /// <returns>Converted Value</returns>
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}
kenjara
  • 402
  • 10
  • 18