WPF功能区中的像素化图像/图标?
Pixelated images / icons in in WPF Ribbon?
到目前为止,如果我只想在 XAML 中制作一个带有图像的按钮,我可以这样做:
<Button>
<Button.Content>
<Grid>
<Image Source="myimage.png" Width="45" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="High Quality"/>
</Grid>
</Button.Content>
<Button>
这很有效。现在我正在尝试做 RibbonButtons 和 RibbonApplicationMenuItems,但事情似乎并没有很快变得有意义。
一个 RibbonButton 有一个 LargeImageSource 和一个 SmallImageSource...如果我尝试修改如上所示的内容,两个图像都不会出现。如果我对 Large ImageSources 和 SmallImageSources 使用 48x48 图像,则小图像最终会像素化。什么?我以为我可以指定别名和高质量,并且应用程序应该为我干净地缩放图像。为什么这没有发生?
RibbonApplicationMenuItem 存在类似问题。这个让我指定一个 ImageSource。如果我选择一个大的 PNG,它看起来还不错,但是像素化了(在 Explorer window 中调整大小的相同 PNG 看起来很好),就像它被不正确地拉伸得更小一样。如果我使用嵌入了 16x16、24x24、32x32、48x48 等尺寸的 ICO,它看起来像是使用了 ICO 中最小的图标并且拉伸得更大。
如果我使用与应用程序图标相同的 ICO 文件(它在应用程序顶部看起来不错,因此选择了正确的嵌入式图标),即使使用此 ICO,RibbonApplicationMenuItem 看起来也很差。
显然我在这里遗漏了一些关键的东西。我需要做什么才能让功能区导航上的按钮和项目看起来干净利落?
更多示例代码:
<RibbonApplicationMenuItem Header="Command Text" ImageSource="myimage.png" Command="{Binding MyCommand}" />
<RibbonButton LargeImageSource="myimage.png" Label="Command Text" KeyTip="C" Command="{Binding MyCommand}" />
编辑 2015 年 9 月 24 日:根据 Adeeb Arangodan 的要求,我正在添加更多代码并包含一些图片。
使用的一些原始图像的图片(ICO 和各种尺寸的 PNG):
此外,这里还有更多示例代码。包括整个 window 和多个按钮尝试。如您所见,只有已经正确调整大小的图像 "paste_32.png" 看起来不错。其他所有按钮,无论是使用大于目标尺寸的 ICO 还是 PNG,最终都会变得模糊。
<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>
我是这样做的:
将您的图片添加到项目中
SolutionExplorer->Select package->Add->ExistingItem->选择你的镜像文件
将其设置为内容资源
Select 解决方案资源管理器中的图像。将 属性 'BuildAction' 设置为 'Content'
将 xaml 标记中的功能区图像设置为 SmallImageSource 或 LargeImageSource。我的图片是128*128分辨率的png图片
<RibbonButton Margin="3" LargeImageSource="/Resources/images/Home.png"
Label="Home" ToolTip="Show company detials" Command="{Binding ShowHomeCommand}">
我不确定这是最好的方法(如果有人在 XAML 中有易于使用的方法,我会把另一个解决方案标记为正确答案),但这对我很有效使用具有 只有 一种图像但尺寸不同的 ICO 时。我根据 Nikolay 的回答创建了一个转换器 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();
}
}
我为每个标准尺寸(16、24、32 等)创建了一个转换器,每个转换器都可以单独使用。他们只需要传递一个 ICO 文件的路径。
然后我创建了一个可以传递 "path, size" 形式的字符串的父转换器,因此在 XAML 图像中的 Button 可以使用相同的转换器。
[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();
}
}
我不认为这很优雅,我不确定它是否有效,它(还)不能处理 PNG 或其他图像格式,对于 ICO,它只能处理那些只适用于一张图像的图像(与不同尺寸的多个不同图像相反)通过选择第一项。但它确实允许:
<Fluent:MenuItem.Icon>
<Binding Converter="{StaticResource iconExtractor}" ConverterParameter="/myapplication;component/Images/error.ico, 32"/>
</Fluent:MenuItem.Icon>
到目前为止,如果我只想在 XAML 中制作一个带有图像的按钮,我可以这样做:
<Button>
<Button.Content>
<Grid>
<Image Source="myimage.png" Width="45" RenderOptions.EdgeMode="Aliased" RenderOptions.BitmapScalingMode="High Quality"/>
</Grid>
</Button.Content>
<Button>
这很有效。现在我正在尝试做 RibbonButtons 和 RibbonApplicationMenuItems,但事情似乎并没有很快变得有意义。
一个 RibbonButton 有一个 LargeImageSource 和一个 SmallImageSource...如果我尝试修改如上所示的内容,两个图像都不会出现。如果我对 Large ImageSources 和 SmallImageSources 使用 48x48 图像,则小图像最终会像素化。什么?我以为我可以指定别名和高质量,并且应用程序应该为我干净地缩放图像。为什么这没有发生?
RibbonApplicationMenuItem 存在类似问题。这个让我指定一个 ImageSource。如果我选择一个大的 PNG,它看起来还不错,但是像素化了(在 Explorer window 中调整大小的相同 PNG 看起来很好),就像它被不正确地拉伸得更小一样。如果我使用嵌入了 16x16、24x24、32x32、48x48 等尺寸的 ICO,它看起来像是使用了 ICO 中最小的图标并且拉伸得更大。
如果我使用与应用程序图标相同的 ICO 文件(它在应用程序顶部看起来不错,因此选择了正确的嵌入式图标),即使使用此 ICO,RibbonApplicationMenuItem 看起来也很差。
显然我在这里遗漏了一些关键的东西。我需要做什么才能让功能区导航上的按钮和项目看起来干净利落?
更多示例代码:
<RibbonApplicationMenuItem Header="Command Text" ImageSource="myimage.png" Command="{Binding MyCommand}" />
<RibbonButton LargeImageSource="myimage.png" Label="Command Text" KeyTip="C" Command="{Binding MyCommand}" />
编辑 2015 年 9 月 24 日:根据 Adeeb Arangodan 的要求,我正在添加更多代码并包含一些图片。
使用的一些原始图像的图片(ICO 和各种尺寸的 PNG):
此外,这里还有更多示例代码。包括整个 window 和多个按钮尝试。如您所见,只有已经正确调整大小的图像 "paste_32.png" 看起来不错。其他所有按钮,无论是使用大于目标尺寸的 ICO 还是 PNG,最终都会变得模糊。
<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>
我是这样做的:
将您的图片添加到项目中
SolutionExplorer->Select package->Add->ExistingItem->选择你的镜像文件
将其设置为内容资源
Select 解决方案资源管理器中的图像。将 属性 'BuildAction' 设置为 'Content'
将 xaml 标记中的功能区图像设置为 SmallImageSource 或 LargeImageSource。我的图片是128*128分辨率的png图片
<RibbonButton Margin="3" LargeImageSource="/Resources/images/Home.png" Label="Home" ToolTip="Show company detials" Command="{Binding ShowHomeCommand}">
我不确定这是最好的方法(如果有人在 XAML 中有易于使用的方法,我会把另一个解决方案标记为正确答案),但这对我很有效使用具有 只有 一种图像但尺寸不同的 ICO 时。我根据 Nikolay 的回答创建了一个转换器 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();
}
}
我为每个标准尺寸(16、24、32 等)创建了一个转换器,每个转换器都可以单独使用。他们只需要传递一个 ICO 文件的路径。
然后我创建了一个可以传递 "path, size" 形式的字符串的父转换器,因此在 XAML 图像中的 Button 可以使用相同的转换器。
[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();
}
}
我不认为这很优雅,我不确定它是否有效,它(还)不能处理 PNG 或其他图像格式,对于 ICO,它只能处理那些只适用于一张图像的图像(与不同尺寸的多个不同图像相反)通过选择第一项。但它确实允许:
<Fluent:MenuItem.Icon>
<Binding Converter="{StaticResource iconExtractor}" ConverterParameter="/myapplication;component/Images/error.ico, 32"/>
</Fluent:MenuItem.Icon>