How to display Windows Metafile?
Asked Answered
E

2

11

I need to display a Windows Metafile (EMF) using WPF, how can I do?

Edit:

I'd to keep the image vector-based.

Eightfold answered 13/4, 2012 at 19:41 Comment(1)
See also this question.Delora
N
15

Take a look at the 3rd party library Ab2d.ReadWmf.

Update #1: Overview

First off, this post states that Microsoft does not intend support EMF files in WPF. That doesn't mean it can't be done, just that they will not support them.

Looking at the Wikipedia page about the WMF/EMF format I see that it describes EMF as:

Essentially, a WMF file stores a list of function calls that have to be issued to the Windows Graphics Device Interface (GDI) layer to display an image on screen. Since some GDI functions accept pointers to callback functions for error handling, a WMF file may erroneously include executable code.

If you've worked with WPF much you know that WPF is fundamentally different than GDI. A quick overview is available here. This means that you'll need to read in your EMF file and translate the GDI calls to WPF calls. Here's a thread where they discuss the process. That sounds like a lot of work to me.

Luckily, Microsoft provides an interface for reading in Windows Metafiles. Take a look at this thread for an example and the documentation available here, but this will only get you half way there since it's not a WPF Visual. At this point I think the easiest solution would be to create a WinForms control in your WPF app and host it inside a WindowsFormsHost control.

Update #2: Code Sample

To display an EMF file in a WPF application:

  1. Create a WinForms UserControl
  2. Load your EMF file into a MetaFile object and draw it in the OnPaint handler.
  3. Add a reference to the WindowsFormsIntegration library
  4. Host your WinForms control inside a WindowsFormsHost element

UserControl:

public partial class UserControl1 : UserControl
{
     private Metafile metafile1;

     public UserControl1()
     {
         InitializeComponent();
         metafile1 = new Metafile(@"C:\logo2.emf");
     }

     protected override void OnPaint(PaintEventArgs e)
     {
         e.Graphics.DrawImage(metafile1, 0, 0);
     }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:app="clr-namespace:WpfApplication1" 
    Title="MainWindow" Height="200" Width="200">

     <Grid>
         <WindowsFormsHost>
             <app:UserControl1/>
         </WindowsFormsHost>
     </Grid>
 </Window>
Nutgall answered 13/4, 2012 at 19:49 Comment(2)
Plese update your answer with significant examples or referenses, generic links are not useful.Eightfold
+1 for your last update, can you show how create the Forms solution to display the EMF?Eightfold
S
12

Here is a utility function that loads an EMF file and converts it into a WPF BitmapSource:

public static class Emfutilities
{
        public static BitmapSource ToBitmapSource(string path)
        {
            using (System.Drawing.Imaging.Metafile emf = new System.Drawing.Imaging.Metafile(path))
            using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(emf.Width, emf.Height))
            {
                bmp.SetResolution(emf.HorizontalResolution, emf.VerticalResolution);
                using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
                {
                    g.DrawImage(emf, 0, 0);
                    return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                }
            }
        }
}

You simply use it like this:

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // img is of Image type for example
            img.Source = Emfutilities.ToBitmapSource("SampleMetafile.emf");
        }
    }

}

The drawback is you will need to reference System.Drawing.dll (GDI+) into your WPF application, but that should not be a big issue.

Stromberg answered 19/6, 2012 at 14:5 Comment(3)
It works +1. But EMF is for vector graphics, BitmapSource (as you know) is raster. How can i fix this?Eightfold
You didn't specify in your question you wanted to keep the image vector-based. You would need to convert the EMF records to pure XAML for that. This is hard work (probably CPU intensive as well) and does not comes as a free lunch :-) See Dan Busha's answer for a conversion tool.Stromberg
Note: you can also use the nuget package: "System.Drawing.Common" if "System.Drawing.Imaging" isn't supported in your solution.Sawhorse

© 2022 - 2024 — McMap. All rights reserved.