Search code examples
wpfimagebitmapscale

Scale down a Bitmap in WPF with best quality


How can I convert this GDI code to WPF code?

Icon bigIcon32x32 = null;
                    bigIcon32x32 = Icon.ExtractAssociatedIcon("c:\\test.docx");                    

                    Bitmap bm = bigIcon32x32.ToBitmap();

                    Bitmap thumb16x16 = new Bitmap(16, 16);
                    Graphics graphics = Graphics.FromImage(thumb16x16);
                    graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    graphics.DrawImage(bm, new Rectangle(0, 0, 16, 16), new Rectangle(0, 0, bm.Width, bm.Height), GraphicsUnit.Pixel);

                    graphics.Dispose();
                    bm.Dispose(); 
                    thumb16x16.Dispose(); 

It seems I have to use the ToBitmap() methode but from then on I want to use WPF only.

In the end I want to display small 16x16 pixel Images in a WPF DataGrid`s Column via Binding.


Solution

  • To show a bitmap in a DataGrid cell you can use a DataGridTemplateColumn with a DataTemplate using the IValueConverter to display an image in a DataGrid cell.

    You can play with properties of BmpBitmapDecoder to achieve as good an image as possible.

    Here is the definition for the DataGrid in XAML:
    1- I have three columns in the DataGrid with the first one being the image.
    2- I set Path=. because all I wanted to do was load the image from the converter.
    3- The DataGrid binds to a Customers collection in a ViewModel, and I included the definitions of those for completeness at the end.

    <Window x:Class="ContextMenuNotFiring.Views.MainView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:Helpers="clr-namespace:ContextMenuNotFiring.Helpers" 
      Title="Main Window" Height="400" Width="800">
      <Window.Resources>
        <Helpers:ImageConverter  x:Key="imgConv"/>
      </Window.Resources>
      <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <DataGrid
           Grid.Row="0"
           IsSynchronizedWithCurrentItem="True"
           Background="Transparent" 
           AutoGenerateColumns="False"
           ItemsSource="{Binding Customers}">
         <DataGrid.Columns>
            <DataGridTemplateColumn
               Header="Icon" 
               Width="SizeToHeader">
               <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                      <Image Source="{Binding Path=., Converter={StaticResource imgConv}}" />
                  </DataTemplate>
               </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn 
               Header="First Name" 
               Width="SizeToHeader"
               Binding="{Binding FirstName}" />
            <DataGridTextColumn 
               Header="Last Name" 
               Width="SizeToCells"
               Binding="{Binding LastName}" />
           </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </Window>
    

    Here is the converter that does a onetime lookup of the Word Doc's associated icon. If you want to handle multiple icons store the BitmapFrame references in a Dictionary and use the "value" input parameter to select which image to display.

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Globalization;
    using System.IO;
    using System.Windows.Data;
    using System.Windows.Media.Imaging;
    
    namespace ContextMenuNotFiring.Helpers
    {
      [ValueConversion(typeof(object), typeof(BitmapSource))]
      public sealed class ImageConverter : IValueConverter
      {
        private static BitmapFrame _bitmapFrame = null;
    
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
          try
          {
            if (_bitmapFrame == null)
            {
              using (Icon bigIcon32x32 = Icon.ExtractAssociatedIcon("c:\\temp\\test.docx"))
              {
                using (Bitmap bm = bigIcon32x32.ToBitmap())
                {
                  MemoryStream finalStream = new MemoryStream();
                  {
                    bm.Save(finalStream, ImageFormat.Bmp);
                    BmpBitmapDecoder bitmapDecoder = new BmpBitmapDecoder(finalStream,
                                BitmapCreateOptions.None, BitmapCacheOption.None);
                    _bitmapFrame = bitmapDecoder.Frames[0];
    
                  }
                }
              }
            }
    
            return _bitmapFrame;
          }
          catch
          {
            return Binding.DoNothing;
          }
        }
    
        public object ConvertBack(object value, Type targetType,
                                  object parameter, CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
    }
    

    The View Model loads the following collection of Customers my View Model constructor.

    private List<Customer> _customers = new List<Customer>():
    public List<Customer> Customers
    {
       get
       {
          return _customers;
       }
    }
    
    public class Customer
    {
      public String FirstName { get; set; }
      public String LastName { get; set; }
    }