Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set MenuItem's Icon using ItemContainerStyle

I'm following the example here of binding a MenuItem to a data object.

<Menu Grid.Row="0" KeyboardNavigation.TabNavigation="Cycle"
      ItemsSource="{Binding Path=MenuCommands}">  
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=DisplayName}"/>
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=Commands}"/>
            <Setter Property="MenuItem.Command" Value="{Binding Path=Command}"/>
            <Setter Property="MenuItem.Icon" Value="{Binding Path=Icon}"/>
        </Style>
    </Menu.ItemContainerStyle>                
</Menu>

It all works swimmingly except the MenuItem's icon shows up as the string System.Drawing.Bitmap. The bitmap in question is returned by the data object from a compiled resource.

internal static System.Drawing.Bitmap folder_page
{
    get
    {
        object obj = ResourceManager.GetObject("folder_page", resourceCulture);
        return ((System.Drawing.Bitmap)(obj));
    }
}

What am I doing wrong?

like image 377
dkackman Avatar asked Nov 24 '09 12:11

dkackman


1 Answers

Kent (of course) has the right answer. But I thought I would go ahead and post the code for the converter that converts from the System.Drawing.Bitmap (Windows Forms) to a System.Windows.Windows.Media.BitmapSource (WPF) ... as this is a common problem/question.

This takes three steps:

  1. Use an image converter in your binding.
  2. Create the converter.
  3. Declare the converter in your resources.

Here is how you would use an image converter in your binding:

<Setter
    Property="MenuItem.Icon"
    Value="{Binding Path=Icon, Converter={StaticResource imageConverter}}"
/>

And, here is the code for the converter (put it into a file called ImageConverter.cs) and add it to your project:

[ValueConversion(typeof(Image), typeof(string))]
public class ImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        BitmapSource bitmapSource;

        IntPtr bitmap = ((Bitmap)value).GetHbitmap();
        try
        {
            bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
        }
        finally
        {
            DeleteObject(bitmap);
        }

        return bitmapSource;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }

    [DllImport("gdi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    static extern int DeleteObject(IntPtr o);
}

Here is how you declare it in your resources section (note the local namespace that you will have to add):

<Window
    x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication2"
>
    <Window.Resources>
        <local:ImageConverter x:Key="imageConverter"/>
    </Window.Resources>
    <!-- some xaml snipped for clarity -->
</Window>

And that's it!


Update

After doing a quick search for similar questions, I noticed that Lars Truijens pointed out here that the previous converter implementation leaks. I have updated the converter code above ... so that it doesn't leak.

For more information on the cause of the leak, see the remarks section on this MSDN link.

like image 159
cplotts Avatar answered Nov 14 '22 05:11

cplotts