2

So far, if I've wanted to just make a button with an image in my XAML, I could do something like this:

<Button>
    <Button.Content>
        <Grid>
            <Image Source="myimage.png" Width="45" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="High Quality"/>
        </Grid>
    </Button.Content>
<Button>

This worked well. Now I'm trying to do RibbonButtons and RibbonApplicationMenuItems, and things don't seem to quickly make sense.

A RibbonButton has a LargeImageSource and a SmallImageSource... If I try modifying the content like as shown above, neither of the images appear. If I use a 48x48 image for both Large and SmallImageSources, the small one ends up pixelated. What? I thought I can specify aliasing and high quality, and that the application is supposed to scale the image cleanly for me. Why doesn't this happen?

Similar issue with the RibbonApplicationMenuItem. This one lets me specify an ImageSource. If I pick a large PNG, it looks okay-ish, but is pixelated (the same PNG resized in an Explorer window looks fine), like it is being improperly stretched smaller. If I use an ICO that has 16x16, 24x24, 32x32, 48x48, etc. embedded, it looks like the smallest icon in the ICO is used and the stretched larger.

If I use the same ICO file that the application icon is using (which looks fine on at the top of the application, so that picked the right embedded icon), even with this ICO the RibbonApplicationMenuItem looks poor.

Obviously I'm missing some key things here. What do I need to do so that my buttons and items on my ribbon navigation look clean and sharp?

More example code:

<RibbonApplicationMenuItem Header="Command Text" ImageSource="myimage.png" Command="{Binding MyCommand}" />
<RibbonButton LargeImageSource="myimage.png" Label="Command Text" KeyTip="C" Command="{Binding MyCommand}" />

EDIT 9/24/15: Per Adeeb Arangodan's request, I'm adding more code and including some pictures.

Pictures of some of the original images used (ICOs, and PNGs of various sizes):

enter image description here

Also, here is more example code. Included is the entire window, and multiple button attempts. As you can see, only the image "paste_32.png" which is already sized correctly, appears to look nice. Every other button, whether using an ICO or PNG that is larger than the target size, ends up blurry.

<Window x:Class="RibbonDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary">
    <Grid>
        <r:Ribbon>
            <r:Ribbon.HelpPaneContent>
                <r:RibbonButton SmallImageSource="pack://application:,,,/RibbonDemo;component/Images/help.ico" />
            </r:Ribbon.HelpPaneContent>

            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu >
                    <r:RibbonApplicationMenuItem Header="Error" ImageSource="pack://application:,,,/RibbonDemo;component/Images/error.ico" />
                    <r:RibbonApplicationMenuItem Header="Print" ImageSource="pack://application:,,,/RibbonDemo;component/Images/print.ico" />
                    <r:RibbonApplicationMenuItem Header="Vendor" ImageSource="pack://application:,,,/RibbonDemo;component/Images/batch_logo.png" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="HighQuality" />
                    <r:RibbonApplicationMenuItem Header="Paste" ImageSource="pack://application:,,,/RibbonDemo;component/Images/paste_32.png" />
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>

            <r:RibbonTab Header="Home">

                <r:RibbonGroup Header="Home">
                    <r:RibbonMenuButton 
                        LargeImageSource="pack://application:,,,/RibbonDemo;component/Images/error.ico"
                        SmallImageSource="pack://application:,,,/RibbonDemo;component/Images/error.ico" Label="Error">
                    </r:RibbonMenuButton>
                    <r:RibbonButton LargeImageSource="pack://application:,,,/RibbonDemo;component/Images/print.ico" Label="Help"/>
                    <r:RibbonButton LargeImageSource="pack://application:,,,/RibbonDemo;component/Images/batch_logo.png" Label="Vendor" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="HighQuality"/>
                    <r:RibbonButton LargeImageSource="pack://application:,,,/RibbonDemo;component/Images/paste_32.png" Label="Paste"/>
                </r:RibbonGroup>
            </r:RibbonTab>
        </r:Ribbon>
    </Grid>
</Window>
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
ohioDeveloper
  • 366
  • 3
  • 13
  • Maybe you should also show your code about `RibbonButtons` and `RibbonApplicationMenuItems` – Il Vic Aug 30 '15 at 10:16
  • I've added some more of my code. One image I have tried using is a 128x128 PNG that according to paint.net has a resolution of 96 pixels/inch. It appears rendered in Button/MenuItem looking jagged. – ohioDeveloper Aug 31 '15 at 12:36

2 Answers2

0

This is how I did this:

  1. Add your image to the project

    SolutionExplorer->Select package->Add->ExistingItem->choose your image file


  1. Set it as a content resource

    Select the image in solution explorer. Set the property 'BuildAction' to 'Content'


  1. Set the Ribbon image in xaml markup as SmallImageSource or LargeImageSource. My image is a png image with 128*128 resolution

     <RibbonButton Margin="3" LargeImageSource="/Resources/images/Home.png" 
          Label="Home" ToolTip="Show company detials" Command="{Binding ShowHomeCommand}">
    
Adeeb Arangodan
  • 152
  • 1
  • 11
  • I just tried this with a 128x128 pixel PNG with 96 pixels/inch resolution, with 'BuildAction' set to 'Resource'. It still comes out looking jagged and poor. I then tried setting the 'BuildAction' to 'Content' and ran my application again, and I get "A first chance exception of System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll'. – ohioDeveloper Aug 31 '15 at 12:36
  • I forgot to specify configure 'Copy to Output Directory'. I don't get the XAML error now, but the image is still jagged. This appears to have had no effect. – ohioDeveloper Aug 31 '15 at 12:47
  • Can you post a screenshot of your RibbonButton and the original image used? Please do post your code too. – Adeeb Arangodan Sep 01 '15 at 13:33
  • I have added screenshots of the original images, how it ends up looking in my application, and a full MainWindow.xaml to show what I am doing to the original thread. – ohioDeveloper Sep 24 '15 at 13:30
  • The icon which your are using will appear blurry when scaled down because of the icon pixels. You cannot scale down that icon less than a minimum dimension. Otherwise it would not appear sharp. You are not missing any key things in wpf. It is not a wpf issue – Adeeb Arangodan Oct 02 '15 at 10:03
  • I opened the error.ico file in IcoFX, and within it there are multiple sizes: 256x256, 48x48, 32x32, 24x24, 16x16, at various bit depths. If I use the icon as the application icon, Explorer is able to size this properly (select the right size?) for the top-left corner of the application, and for the Windows Taskbar. I had assumed (wrongly) that my application would pick the appropriate size. Do I need to do something extra in the XAML so that it would pick the closest size, instead of scaling the largest one? A converter, maybe? Or perhaps this just isn't how ImageSource and ICOs work? – ohioDeveloper Oct 02 '15 at 19:28
0

I'm not sure this is the best way (and will mark another solution as the right answer if someone has a method that is easy to use in the XAML), but this was effective for me when using ICOs that have only one type of image, but different sizes. I created a Converter based on Nikolay's answer here.

[ValueConversion(typeof(string), typeof(ImageSource))]
public class IconExtractor32 : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string path = parameter.ToString();
        var decoder = BitmapDecoder.Create(new Uri(path.ToString()), BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand);

        // It is assumed that if multiple icons of the same size are available,
        // that the first ones encountered are those intended for use.
        var result = decoder.Frames.First(f => f.Width == 32);
        if (result == default(BitmapFrame))
            result = decoder.Frames.OrderBy(f => f.Width).First();
        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I created one of these converters for each standard size (16, 24, 32, etc.) that could each be used standalone. They just need to be passed a path to the ICO file.

I then created a parent converter that could be passed a string in the form of "path, size", so that in the XAML image for a Button could use the same converter.

[ValueConversion(typeof(string), typeof(ImageSource))]
public class IconExtractor : IValueConverter
{
    private IconExtractor32 extractor32 = new IconExtractor32();

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string source = parameter.ToString();
        string[] values = source.Split(',').Select(sValue => sValue.Trim()).ToArray();

        if (2 != values.Count())
        {
            throw new NotImplementedException();
        }
        else
        {
            string path = "pack://application:,,," + values[0];

            switch (values[1])
            {
                case "32":
                    return extractor32.Convert(value, targetType, path, culture);
                default:
                    throw new NotImplementedException();
            }
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I don't think of this as elegant, I'm not sure it is efficient, it does not (yet) handle PNGs or other image formats, and for ICOs it can only handle those that are for one image (as opposed to multiple different images in various sizes) by picking the First item. But it does allow:

<Fluent:MenuItem.Icon>
    <Binding Converter="{StaticResource iconExtractor}" ConverterParameter="/myapplication;component/Images/error.ico, 32"/>
</Fluent:MenuItem.Icon>
Community
  • 1
  • 1
ohioDeveloper
  • 366
  • 3
  • 13