【问题标题】:How do you select the right size icon from a multi-resolution .ico file in WPF?如何从 WPF 中的多分辨率 .ico 文件中选择合适大小的图标?
【发布时间】:2009-06-04 17:44:20
【问题描述】:

如果我有一个多分辨率图标文件 (.ico),我如何确保 WPF 选择正确大小的图标文件?设置图像的宽度和高度会强制它,还是 WPF 只是调整 ico 文件中第一个图标的大小?

这是我目前正在使用的(它有效,但如果发生这种情况,我想避免调整大小)。

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

如果可能的话,我想在 Xaml 中声明它,而不必编写代码。

【问题讨论】:

    标签: wpf icons


    【解决方案1】:

    我为此使用了简单的标记扩展:

    /// <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}"/>
    

    <Image Stretch="None"
           Source="{common:Icon /ControlsTester;component/icons/custom-reports.ico, 16}" />
    

    【讨论】:

    • 我犯了一个错误。您不能通过绑定在标记扩展中设置属性(因此不能是 )但是您可以轻松编写类似的转换器,可以与绑定一起使用:
    • 当我遇到奇怪的 WPF 问题时,我喜欢它......并且我找到了 StackOverflow 问题和答案......带有优雅的解决方案。非常好。 +1
    • 例如,在 ListBox 上使用多个图标时,这会影响性能吗? 缓存解码的图标会很好,因此每个图标大小都需要一个解码。
    【解决方案2】:

    (基于@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 编辑,这种行为会更方便。

    【讨论】:

      【解决方案3】:

      似乎只使用 Xaml 是不可能的。

      【讨论】:

      • 这真的很不幸,但感谢残酷的诚实,总比没有回答要好。
      【解决方案4】:

      如果您问的原因是图标看起来模糊,请查看这篇关于我用来解决该问题的主题的非常好的文章:http://blogs.msdn.com/dwayneneed/archive/2007/10/05/blurry-bitmaps.aspx

      您必须使用自定义控件,不仅要精确调整图标的大小,还要确保它与像素网格完全一致。只有这样,您才能避免插值,从而避免模糊。

      尝试在您的查询中查找有关图标中图像大小选择的一些信息...如果我找到任何信息会回帖...

      【讨论】:

      • 嗯,它只是在图像重新缩放的意义上模糊。我真的很好奇如何从 Xaml 中的多图像、多分辨率 .ico 文件中选择正确的图像。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多