Imagescaling doesn’t look good in the feed

I fill the ribbon (WPF) programmatically based on MS RibbonControlsLibrary (3.5.41019.1). All images (the most noticeable large images) look very bad and "pixly":

Screenshot from 2 Ribbon

  • Top screenshot from the "pixel" tape (image scaling looks bad)
  • The bottom screenshot is taken from the same area in a WinForms-based window with the same source images (image scaling looks good)
  • All images are saved in 32-bit PNG 48x48

I tried to set BitmapScalingMode to "HighQuality" without any effect, the source code:

BitmapImage img = new BitmapImage(); try { Uri uri = new Uri("pack://application:,,,/UIMainWindow;component/Resources/" + iPictureName); img.BeginInit(); img.SetValue(BitmapImage.CacheOptionProperty, BitmapCacheOption.OnLoad); RenderOptions.SetBitmapScalingMode(img, BitmapScalingMode.HighQuality); img.UriSource = uri; img.EndInit(); img.Freeze(); } catch (Exception ex) { throw new Exception("Creation of image failed: " + ex.Message, ex); } 

Question Why does image scaling look bad? And how can I solve this?

+4
source share
4 answers

None of the other answers above worked for me, so I started my own investigation. Resizing images to 32x32 almost eliminates the problem, but we have users with different DPI settings, and the images will not scale correctly on their machines. I should also note that I use System.Windows.Controls.Ribbon, which may behave a little differently than RibbonControlsLibrary.

I found that by default, the ControlTemplate for RibbonButton specifically defined NearestNeighbor as a bitmap scaling parameter. The good news is that it's easy to override this control pattern by dropping the style into your Window resources. A bit of bad news is that the management template is pretty detailed. I reset the default ControlTemplate for the RibbonButton on my system to a file and simply changed the bitmap scaling option.

First, you will need this namespace declaration at the top of the file if you do not already have it:

 xmlns:s="clr-namespace:System;assembly=mscorlib" 

Then put this style in Window resources to override the look of the RibbonButton:

 <Style TargetType="RibbonButton"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="RibbonButton"> <Border BorderThickness="{TemplateBinding Border.BorderThickness}" CornerRadius="{TemplateBinding RibbonControlService.CornerRadius}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="OuterBorder" SnapsToDevicePixels="True"> <Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" CornerRadius="{TemplateBinding RibbonControlService.CornerRadius}" BorderBrush="#00FFFFFF" Name="InnerBorder"> <StackPanel Name="StackPanel"> <Image Source="{TemplateBinding RibbonControlService.LargeImageSource}" Name="PART_Image" Width="32" Height="32" Margin="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=Ribbon, ResourceId=LargeImageMargin}}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="Center" RenderOptions.BitmapScalingMode="HighQuality" /> <Grid Name="Grid" HorizontalAlignment="Center" VerticalAlignment="Center"> <RibbonTwoLineText TextAlignment="Center" LineHeight="13" LineStackingStrategy="BlockLineHeight" Text="{TemplateBinding RibbonControlService.Label}" Name="TwoLineText" Margin="1,1,1,0" HorizontalAlignment="Center" VerticalAlignment="Top" /> </Grid> </StackPanel> </Border> </Border> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding Path=ControlSizeDefinition.ImageSize, RelativeSource={RelativeSource Mode=Self}}" Value="Large"> <Setter Property="FrameworkElement.MinWidth"> <Setter.Value> <s:Double>44</s:Double> </Setter.Value> </Setter> <Setter Property="FrameworkElement.Height"> <Setter.Value> <s:Double>66</s:Double> </Setter.Value> </Setter> <Setter Property="FrameworkElement.MinHeight" TargetName="Grid"> <Setter.Value> <s:Double>26</s:Double> </Setter.Value> </Setter> <Setter Property="RibbonTwoLineText.HasTwoLines" TargetName="TwoLineText"> <Setter.Value> <s:Boolean>True</s:Boolean> </Setter.Value> </Setter> </DataTrigger> <DataTrigger Binding="{Binding Path=ControlSizeDefinition.ImageSize, RelativeSource={RelativeSource Mode=Self}}" Value="Small"> <Setter Property="FrameworkElement.Height"> <Setter.Value> <s:Double>22</s:Double> </Setter.Value> </Setter> <Setter Property="FrameworkElement.Margin" TargetName="PART_Image"> <Setter.Value> <Thickness>1,0,1,0</Thickness> </Setter.Value> </Setter> <Setter Property="Image.Source" TargetName="PART_Image"> <Setter.Value> <Binding Path="SmallImageSource" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="FrameworkElement.Width" TargetName="PART_Image"> <Setter.Value> <s:Double>16</s:Double> </Setter.Value> </Setter> <Setter Property="FrameworkElement.Height" TargetName="PART_Image"> <Setter.Value> <s:Double>16</s:Double> </Setter.Value> </Setter> <Setter Property="FrameworkElement.HorizontalAlignment" TargetName="TwoLineText"> <Setter.Value> <x:Static Member="HorizontalAlignment.Left" /> </Setter.Value> </Setter> <Setter Property="FrameworkElement.Margin" TargetName="TwoLineText"> <Setter.Value> <Thickness>1,1,1,1</Thickness> </Setter.Value> </Setter> <Setter Property="StackPanel.Orientation" TargetName="StackPanel"> <Setter.Value> <x:Static Member="Orientation.Horizontal" /> </Setter.Value> </Setter> </DataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=ControlSizeDefinition.ImageSize, RelativeSource={RelativeSource Mode=Self}}" Value="Small" /> <Condition Binding="{Binding Path=IsInQuickAccessToolBar, RelativeSource={RelativeSource Mode=Self}}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="FrameworkElement.Height"> <Setter.Value> <s:Double>NaN</s:Double> </Setter.Value> </Setter> </MultiDataTrigger> <DataTrigger Binding="{Binding Path=ControlSizeDefinition.IsLabelVisible, RelativeSource={RelativeSource Mode=Self}}" Value="False"> <Setter Property="UIElement.Visibility" TargetName="TwoLineText"> <Setter.Value> <x:Static Member="Visibility.Collapsed" /> </Setter.Value> </Setter> </DataTrigger> <DataTrigger Binding="{Binding Path=ControlSizeDefinition.ImageSize, RelativeSource={RelativeSource Mode=Self}}" Value="Collapsed"> <Setter Property="UIElement.Visibility" TargetName="PART_Image"> <Setter.Value> <x:Static Member="Visibility.Collapsed" /> </Setter.Value> </Setter> </DataTrigger> <Trigger Property="UIElement.IsMouseOver"> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <Binding Path="MouseOverBackground" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <Binding Path="MouseOverBorderBrush" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="InnerBorder"> <Setter.Value> <SolidColorBrush>#80FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>True</s:Boolean> </Trigger.Value> </Trigger> <Trigger Property="UIElement.IsKeyboardFocused"> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <Binding Path="FocusedBackground" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <Binding Path="FocusedBorderBrush" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="InnerBorder"> <Setter.Value> <SolidColorBrush>#80FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>True</s:Boolean> </Trigger.Value> </Trigger> <Trigger Property="ButtonBase.IsPressed"> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <Binding Path="PressedBackground" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <Binding Path="PressedBorderBrush" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="InnerBorder"> <Setter.Value> <SolidColorBrush>#00FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>True</s:Boolean> </Trigger.Value> </Trigger> <Trigger Property="RibbonControlService.IsInControlGroup"> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <Binding Path="Ribbon.BorderBrush" RelativeSource="{RelativeSource Mode=TemplatedParent}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderThickness" TargetName="OuterBorder"> <Setter.Value> <Thickness>0,0,1,0</Thickness> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="OuterBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="InnerBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>True</s:Boolean> </Trigger.Value> </Trigger> <Trigger Property="UIElement.IsEnabled"> <Setter Property="UIElement.Opacity" TargetName="PART_Image"> <Setter.Value> <s:Double>0.5</s:Double> </Setter.Value> </Setter> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <SolidColorBrush>#FF9E9E9E</SolidColorBrush> </Setter.Value> </Setter> <Trigger.Value> <s:Boolean>False</s:Boolean> </Trigger.Value> </Trigger> <DataTrigger Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True"> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.MenuTextBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <SolidColorBrush>#00FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <SolidColorBrush>#00FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="OuterBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> </DataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlLightBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True" /> <Condition Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=Self}}" Value="False" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource Mode=TemplatedParent}, FallbackValue=false}" Value="True" /> <Condition Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=Self}}" Value="False" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlLightBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=Self}}" Value="True" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlLightBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="OuterBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="InnerBorder"> <Setter.Value> <SolidColorBrush>#00FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightTextBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsKeyboardFocused, RelativeSource={RelativeSource Mode=Self}}" Value="True" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlLightBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="OuterBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="InnerBorder"> <Setter.Value> <SolidColorBrush>#00FFFFFF</SolidColorBrush> </Setter.Value> </Setter> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightTextBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsPressed, RelativeSource={RelativeSource Mode=Self}}" Value="True" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Panel.Background" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlDarkBrushKey}" /> </Setter.Value> </Setter> <Setter Property="Border.CornerRadius" TargetName="OuterBorder"> <Setter.Value> <CornerRadius>0,0,0,0</CornerRadius> </Setter.Value> </Setter> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.HighlightTextBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsInControlGroup, RelativeSource={RelativeSource Mode=Self}}" Value="True" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="Border.BorderBrush" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.ControlLightLightBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=Self}}" Value="False" /> <Condition Binding="{Binding Path=(SystemParameters.HighContrast)}" Value="True" /> </MultiDataTrigger.Conditions> <Setter Property="TextElement.Foreground" TargetName="OuterBorder"> <Setter.Value> <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" /> </Setter.Value> </Setter> </MultiDataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> 
+1
source

Try using vector graphics . there are tools that you could use to create the xaml equivalent of this image, you can take a look at this article , it can help a little.

0
source

What operating system are you using? - WPF is not like XP (not a problem with Vista and 7). I believe this is due to how the default value for anti-aliasing differs between XP and newer operating systems.

Set the width and height in XAML to 48 to match the image size (also check the stretch setting).

With 48x48, 32-bit PNG images should look normal.

Since the tape is not stretched, the vector graphics are redundant.

If the above does not help, try setting the pixel alignment of the device and setting LayoutRounding .

As I said in my comment, you can resize images to the size of the display, however it must be set explicitly in XAML to make sure that it is the same.

You can also get WPF to resize the image by setting the height or width to 32 and using ScaleTransform .

0
source

I had the same problem and created my own usercontrol to solve this problem.

Here is how I did it:

 <ribbon:Ribbon> <ribbon:RibbonTab Header="File"> <ribbon:RibbonGroup Header="File"> <views:ImageButton Command="{Binding LoadCommand}" Caption="Open" SourceImage="/Images/save.png"/> </ribbon:RibbonGroup> </ribbon:RibbonTab> </ribbon:Ribbon> 

ImageButton.xaml custom image control

 <UserControl Name="control" <Button Command="{Binding Command, ElementName=control}" Style="{x:Null}"> <StackPanel> <infrastructure:AutoGreyableImage Width="32" Source="{Binding SourceImage, ElementName=control}"/> <TextBlock Text="{Binding Caption, ElementName=control}"/> </StackPanel> </Button> 

ImageButton.xaml.cs custom image control

 public partial class ImageButton : UserControl { public static DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(ImageButton)); public static DependencyProperty SourceProperty = DependencyProperty.Register( "SourceImage", typeof(string), typeof(ImageButton)); public static DependencyProperty CaptionProperty = DependencyProperty.Register( "Caption", typeof(string), typeof(ImageButton)); public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public string SourceImage { get { return (string)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } public string Caption { get { return (string)GetValue(CaptionProperty); } set { SetValue(CaptionProperty, value); } } public ImageButton() { InitializeComponent(); } } 

For AutogreyableImage I used this post

This is a copy of the class

  using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; /// <summary> /// Class used to have an image that is able to be gray when the control is not enabled. /// Author: Thomas LEBRUN (http://blogs.developpeur.org/tom) /// </summary> public class AutoGreyableImage : Image { /// <summary> /// Initializes a new instance of the <see cref="AutoGreyableImage"/> class. /// </summary> static AutoGreyableImage() { // Override the metadata of the IsEnabled property. IsEnabledProperty.OverrideMetadata(typeof(AutoGreyableImage), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGreyScaleImageIsEnabledPropertyChanged))); } /// <summary> /// Called when [auto grey scale image is enabled property changed]. /// </summary> /// <param name="source">The source.</param> /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private static void OnAutoGreyScaleImageIsEnabledPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs args) { var autoGreyScaleImg = source as AutoGreyableImage; var isEnable = Convert.ToBoolean(args.NewValue); if (autoGreyScaleImg != null) { if (!isEnable) { // Get the source bitmap var bitmapImage = new BitmapImage(new Uri(autoGreyScaleImg.Source.ToString())); // Convert it to Gray autoGreyScaleImg.Source = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0); // Create Opacity Mask for greyscale image as FormatConvertedBitmap does not keep transparency info autoGreyScaleImg.OpacityMask = new ImageBrush(bitmapImage); } else { // Set the Source property to the original value. autoGreyScaleImg.Source = ((FormatConvertedBitmap)autoGreyScaleImg.Source).Source; // Reset the Opcity Mask autoGreyScaleImg.OpacityMask = null; } } } } 

I hope this helps you and other people to come.

0
source

Source: https://habr.com/ru/post/1415161/


All Articles