代码之家  ›  专栏  ›  技术社区  ›  SergioL

如何从WPF中的多分辨率.ico文件中选择正确的大小图标?

  •  26
  • SergioL  · 技术社区  · 15 年前

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

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

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

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

    4 回复  |  直到 6 年前
        1
  •  28
  •   g t Omri Btian    9 年前

    我使用简单的标记扩展:

    /// <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}" />
    
        2
  •  4
  •   SergioL    15 年前

    似乎不可能只使用XAML。

        3
  •  1
  •   PeterAllenWebb    15 年前

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

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

    试图在您的查询中找到一些关于图标中图像大小选择的信息…如果我找到任何信息,将返回…

        4
  •  0
  •   Askolein    6 年前

    (基于@nikolay great answer和后续关于绑定的评论)

    你最好创造一个 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" />
    

    然后你可以用 结合 :

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

    PS:在转换器代码中,我们接受参数的所有输入,甚至是丢失或无效的输入。如果像我一样,你喜欢玩实时XAML编辑,这种行为会更方便。