Как выбрать значок нужного размера из файла .ico с несколькими разрешениями в WPF?

Если у меня есть файл значка с несколькими разрешениями (.ico), как я могу гарантировать, что WPF выберет правильный размер? Принуждает ли установка ширины и высоты изображения или WPF просто изменяет размер первого значка в файле ico?

Это то, что я использую в настоящее время (это работает, но я бы хотел избежать изменения размера, если это происходит).

<MenuItem.Icon>
    <Image Source="MyIcons.ico" Width="16" Height="16"  />
</MenuItem.Icon>

Я хотел бы объявить это в Xaml, если это возможно, без необходимости кодировать его.


person SergioL    schedule 04.06.2009    source источник


Ответы (4)


Для этого я использую простое расширение разметки:

/// <summary>
/// Simple extension for icon, to let you choose icon with specific size.
/// Usage sample:
/// Image Stretch="None" Source="{common:Icon /Controls;component/icons/custom.ico, 16}"
/// Or:
/// Image Source="{common:Icon Source={Binding IconResource}, Size=16}"
/// </summary> 
public class IconExtension : MarkupExtension
{
    private string _source;

    public string Source
    {
        get
        {
            return _source;
        }
        set
        {
            // Have to make full pack URI from short form, so System.Uri recognizes it.
           _source = "pack://application:,,," + value;
        }
    }

    public int Size { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var decoder = BitmapDecoder.Create(new Uri(Source), 
                                           BitmapCreateOptions.DelayCreation,
                                           BitmapCacheOption.OnDemand);

        var result = decoder.Frames.SingleOrDefault(f => f.Width == Size);
        if (result == default(BitmapFrame))
        {
            result = decoder.Frames.OrderBy(f => f.Width).First();
        }

        return result;
    }

    public IconExtension(string source, int size)
    {
        Source = source;
        Size = size;
    }

    public IconExtension() { }
}

Использование XAML:

<Image Stretch="None"
       Source="{common:Icon Source={Binding IconResource},Size=16}"/>

or

<Image Stretch="None"
       Source="{common:Icon /ControlsTester;component/icons/custom-reports.ico, 16}" />
person Nikolay    schedule 11.08.2011
comment
Я делаю ошибку. Вы не можете установить свойство в расширении разметки с привязкой (поэтому не может быть ‹Image Stretch=None Source={common:Icon Source={Binding IconResource},Size=16}/›). Но вы можете легко написать аналогичный конвертер, который можно использовать с привязкой: ‹Image Stretch=None Source={Binding Path=IconPath, Converter={StaticResource IconConverter}, ConverterParameter=128}›‹/Image› - person Nikolay; 11.08.2011
comment
Мне нравится, когда у меня возникает странная проблема с WPF... и я нахожу вопрос и ответ StackOverflow... с элегантным решением. Очень хорошо. +1 - person cplotts; 11.02.2016
comment
Влияет ли это на производительность, например, при использовании нескольких значков на ListBox? Было бы неплохо кэшировать декодированный значок, поэтому для каждого размера значка требуется одно декодирование. - person JobaDiniz; 05.07.2017

Невозможно использовать только Xaml.

person SergioL    schedule 06.07.2009
comment
Это действительно к сожалению, но спасибо за жестокую честность, это лучше, чем вообще никакого ответа. - person jpierson; 12.05.2010

(на основе отличного ответа @Nikolay и последующего комментария о привязке)

Вероятно, вам будет лучше создать Converter вместо MarkupExtension, чтобы вы могли использовать Binding. Используя ту же логику, что и @Nikolay

    /// <summary>
    /// Forces the selection of a given size from the ICO file/resource. 
    /// If the exact size does not exists, selects the closest smaller if possible otherwise closest higher resolution.
    /// If no parameter is given, the smallest frame available will be selected
    /// </summary>
    public class IcoFileSizeSelectorConverter : IValueConverter
    {
        public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var size = string.IsNullOrWhiteSpace(parameter?.ToString()) ? 0 : System.Convert.ToInt32(parameter);

            var uri = value?.ToString()?.Trim();
            if (string.IsNullOrWhiteSpace(uri))
                return null;

            if (!uri.StartsWith("pack:"))
                uri = $"pack://application:,,,{uri}";

            var decoder = BitmapDecoder.Create(new Uri(uri),
                                              BitmapCreateOptions.DelayCreation,
                                              BitmapCacheOption.OnDemand);

            var result = decoder.Frames.Where(f => f.Width <= size).OrderByDescending(f => f.Width).FirstOrDefault()
                ?? decoder.Frames.OrderBy(f => f.Width).FirstOrDefault();

            return result;
        }

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

Затем вы должны создать ресурс из вашего класса преобразователя где-нибудь в ResourceDictionary, как обычно:

<localConverters:IcoFileSizeSelectorConverter x:Key="IcoFileSizeSelector" />

И тогда вы можете использовать Binding:

<Image Source="{Binding Path=IconResource, Converter={StaticResource IcoFileSizeSelector}, ConverterParameter=16}" />

PS: в коде конвертера мы принимаем все входные данные для параметра, даже отсутствующие или недействительные. Такое поведение более удобно, если вы, как и я, любите играть с живым редактированием XAML.

person Askolein    schedule 17.02.2019

Если причина, по которой вы спрашиваете, заключается в том, что значок кажется вам размытым, ознакомьтесь с этой очень хорошей статьей по теме, которую я использовал для решения этой проблемы: http://blogs.msdn.com/dwayneneed/archive/2007/10/05/blurry-bitmaps.aspx

Вам придется использовать настраиваемый элемент управления, который не только точно определяет размер значка, но и гарантирует, что он точно совпадает с пиксельной сеткой. Только тогда вы избежите интерполяции и, следовательно, размытости.

Пытаюсь найти информацию по вашему запросу о выборе размера изображения в значках... отпишусь, если найду...

person PeterAllenWebb    schedule 04.06.2009
comment
Ну, это только размыто в том смысле, что изображение было перемасштабировано. Мне просто очень интересно, как выбрать правильное изображение из файла .ico с несколькими изображениями и несколькими разрешениями в Xaml. - person SergioL; 05.06.2009