Working with Metafiles

Metafiles contain information about how an image was createdincluding lists of graphics operationsrather than storing the image in pixel format. Graphics operations in a metafile are stored as records, which can be controlled (recorded and played back) individually.

The Metafile class provides functionality to work with different metafile formats including Windows Metafile Format (WMF), Enhanced Metafile Format (EMF), and an extension to Enhanced Metafile Format (EMF+). The Metafile class provides about 40 overloaded forms of its constructor.

Loading and viewing a metafile is similar to viewing a bitmap. An application can load a metafile from a stream, string, or IntPtr instance with different formats and locations. The simplest way to load and view a metafile is to pass the file name in the Metafile constructor and call DrawImage.

GDI+ and Metafiles

Even though GDI+ is capable of reading both WMF and EMF files, it creates only EMF files. EMF files that contain GDI+ records are called EMF+ files.

The Metafile class is derived from the Image class and has no methods and properties besides those inherited from the Image class.

Let's create an application to test metafile functionality. We will create a Windows application and add a MainMenu control to the form. Then we'll add a menu item to MainMenu to test the code in this and subsequent sections.

As Listing 8.4 shows, first we create a Graphics object using this.CreateGraphics. Then we create a Metafile object from a file and use DrawImage to view it.

Listing 8.4 Viewing a metafile

private void ViewFile_Click(object sender,
 System.EventArgs e)
 // Create a Graphics object
 Graphics g = this.CreateGraphics();
 // Create a Metafile object from a file name
 Metafile curMetafile = new Metafile("mtfile.wmf");
 // Draw metafile using DrawImage
 g.DrawImage(curMetafile, 0, 0) ;
 // Dispose of object

Figure 8.3 shows the output from Listing 8.4.

Figure 8.3. Viewing a metafile


8.2.1 Metafile Class Method

As mentioned already, the Metafile class provides a long list of overloaded constructors. It also provides three methods: GetHenhmetafile, GetMetafileHeader, and PlayRecord.

GetHenhmetafile returns a window handle to a metafile. GetMetafileheader, which has five overloaded forms, returns a metafile header in the form of a MetafileHeader object. PlayRecord plays (reads and displays) an extended metafile.

8.2.2 Creating Metafiles Programmatically

The Metafile object can create a metafile programmatically. Three simple steps are required to create a metafile:

  1. Creating a Metafile object with a file name
  2. Using FromImage to create a Graphics object from the Metafile object
  3. Adding graphics lines and shapes

Now let's create a metafile programmatically. In Listing 8.5 we use GetHdc to get the handle to a device context (HDC), and we use this handle to create a metafile called newFile.wmf. After creating the metafile, we use the FillRectangle, FillEllipse, and DrawString methods to add a rectangle, an ellipse, and a string, respectively. Calling these methods adds records describing the respective objects to the metafile. Finally, we release the objects.

Listing 8.5 Creating a metafile

private void CreateMetaFile_Click(object sender,
 System.EventArgs e)
 Metafile curMetafile = null;
 // Create a Graphics object
 Graphics g = this.CreateGraphics();
 // Get HDC
 IntPtr hdc = g.GetHdc();
 // Create a rectangle
 Rectangle rect = new Rectangle(0, 0, 200, 200);
 // Use HDC to create a metafile with a name
 curMetafile =
 new Metafile("newFile.wmf", hdc);
 catch(Exception exp)
 // Create a Graphics object from the Metafile object
 Graphics g1 = Graphics.FromImage(curMetafile);
 // Set smoothing mode
 g1.SmoothingMode = SmoothingMode.HighQuality;
 // Fill a rectangle on the Metafile object
 g1.FillRectangle(Brushes.Green, rect);
 rect.Y += 110;
 // Draw an ellipse on the Metafile object
 LinearGradientBrush lgBrush =
 new LinearGradientBrush(
 rect, Color.Red, Color.Blue, 45.0f);
 g1.FillEllipse(lgBrush, rect);
 // Draw text on the Metafile object
 rect.Y += 110;
 g1.DrawString("MetaFile Sample",
 new Font("Verdana", 20),
 lgBrush, 200, 200,
 // Release objects

Running the code in Listing 8.5 will create a new metafile in your application's folder. Figure 8.4 shows the image described by the metafile.

Figure 8.4. A metafile created programmatically


As mentioned earlier, after creating a metafile, you can view it as you would any other image, using the DrawImage method of the Graphics class.


Using the same approach, you can easily create a metafile editor similar to GDI+Painter, in which you can draw graphics objects and save them as metafiles. You can even change the GDI+Painter application code to do so.


8.2.3 Enhanced Metafiles

Using enhanced metafiles, you can add personalized data to a metafile as defined in the MSDN documentation:

The enhanced Windows metafile (EMF) format contains a comment mechanism for embedding data within the metafile. This comment mechanism is used to embed GDI+ records within an EMF file. Applications that cannot read or recognize the comment data skip the comment records and render the records they do understand. If the EMF+ file is played back by GDI+, then the GDI+ records are used to render the metafile; otherwise, the GDI records (if present) are used.

There are three types of EMFs: EMF only, EMF+ dual, and EMF+ only. The EmfType enumeration is used to find out the type of EMF programmatically. This enumeration provides three members: EmfOnly, EmfPlusDual, and EmfPlusOnly. The EmfOnly and EmfPlusDual types of records can be played by both GDI and GDI+; EmfPlusOnly types of records can be played only by GDI+.

You can use the Metafile object constructors to specify the type of EMF you want to create. The following code creates an EMF+ dual metafile:

Metafile curMetafile =
new Metafile(hdc, EmfType.EmfPlusDual,


8.2.4 How Metafiles Work

The EnumerateMetafile method can be used to read and play back records of a metafile one by one. Each record is sent to Graphics.EnumerateMetafileProc, which is used to read the data for a record. This method has many overloaded forms.

Graphics.EnumerateMetafileProc takes five parameters and is defined as follows:

public delegate bool Graphics.EnumerateMetafileProc(
 EmfPlusRecordType recordType,
 int flags,
 int dataSize,
 IntPtr data,
 PlayRecordCallback callbackData


GDI GDI+ Record

Each metafile record describes a command that is capable of drawing, filling, or changing the graphics state of a surface. For example, clearing a graphics object, drawing a rectangle, filling an ellipse, creating a graphics container, and ending a graphics container are all examples of records. After creating a metafile programmatically, if you call DrawRectangle, one record will be added to the metafile. When you play back the metafile, GDI+ reads the record (DrawRectangle) and draws a rectangle.

The EmfPlusRecordType enumeration defines the available metafile record types.

Whereas recordType is of type EmfPlusRecordType enumeration and specifies the type of metafile, the flags parameter is a set of flags that specify attributes of the record. The dataSize parameter represents the number of bytes in the record data, and data is an array of bytes that contains the record data. The callbackData parameter is a PlayRecordCallback delegate supplied by the .NET Framework to play a record of metafile data.

Listing 8.6 reads records from a metafile and displays data for these records individually. In the EnumMetaCB callback, we check whether the record type is FillEllipse, FillRects, DrawEllipse, or DrawRects and display the corresponding data.

Listing 8.6 Reading metafile records

private void EnumerateMetaFile_Click(object sender,
 System.EventArgs e)
 // Create a Graphics object
 Graphics g = this.CreateGraphics();
 // Create a Metafile object from a file
 Metafile curMetafile = new Metafile("mtfile.wmf");
 // Set EnumerateMetafileProc property
 Graphics.EnumerateMetafileProc enumMetaCB =
 new Graphics.EnumerateMetafileProc(EnumMetaCB);
 // Enumerate metafile
 new Point(0, 0), enumMetaCB);
 // Dispose of objects
private bool EnumMetaCB(EmfPlusRecordType recordType,
 int flags, int dataSize,
 IntPtr data, PlayRecordCallback callbackData)
 string str = "";
 // Play only EmfPlusRecordType.FillEllipse records
 if (recordType == EmfPlusRecordType.FillEllipse
 || recordType == EmfPlusRecordType.FillRects
 || recordType == EmfPlusRecordType.DrawEllipse
 || recordType == EmfPlusRecordType.DrawRects )
 str = "Record type:"+ recordType.ToString()+
 ", Flags:"+ flags.ToString()+
 ", DataSize:"+ dataSize.ToString()+
 ", Data:"+data.ToString() ;
 return true;

Figure 8.5 shows the output from Listing 8.6. Our program displays the record type, flag, data size, and data. The record in this example contains only FillRectangle methods. If more records are used to create a metafile, you will see messages for the various record types.

Figure 8.5. Reading metafile records


8.2.5 Reading a Metafile Header

A metafile header contains attributes such as type, size, and version of a metafile. It is represented by the MetafileHeader class. GetMetafileHeader returns a metafile header and has many overloaded methods.

The MetafileHeader class has the eight methods listed in Table 8.4.

Table 8.4. MetafileHeader methods




Returns true if a metafile is device-dependent.


Returns true if a metafile is in the Windows EMF format.


Returns true if a metafile is in the Windows EMF or EMF+ format.


Returns true if a metafile is in the Windows EMF+ format.


Returns true if a metafile is in the dual EMF format, which supports both the enhanced and the enhanced plus format.


Returns true if a metafile supports only the Windows EMF+ format.


Returns true if a metafile is in the Windows WMF format.


Returns true if a metafile is in the Windows placeable WMF format.

Properties of the MetafileHeader class represent various attributes of metafiles, including size, version, and type, as Table 8.5 shows. All of these properties are read-only.

Reading metafile attributes is simple: Create a Metafile object, get its header attributes using GetMetafileHeader, and display the value of these attributes in a message box. Listing 8.7 reads metafile header attributes, including type, bounds, size, and version.

Listing 8.7 Reading metafile header attributes

private void MetafileHeaderInfo_Click(object sender,
 System.EventArgs e)
 // Create a Metafile object
 Metafile curMetafile = new Metafile("mtfile.wmf");
 // Get metafile header
 MetafileHeader header = curMetafile.GetMetafileHeader();
 // Read metafile header attributes
 string mfAttributes = "";
 mfAttributes += "Type :"+ header.Type.ToString();
 mfAttributes += ", Bounds:"+ header.Bounds.ToString();
 mfAttributes += ", Size:"+ header.MetafileSize.ToString();
 mfAttributes += ", Version:"+ header.Version.ToString();
 // Display message box
 // Dispose of object

Table 8.5. MetafileHeader properties




Gets the bounds of a metafile in the form of a rectangle.


Gets the horizontal resolution, in dots per inch, of a metafile in the form of a rectangle.


Gets the vertical resolution, in dots per inch, of a metafile in the form of a rectangle.


Gets the size, in bytes, of an enhanced metafile plus header file.


Gets the logical horizontal resolution, in dots per inch, of a metafile.


Gets the logical vertical resolution, in dots per inch, of a metafile.


Gets the size, in bytes, of a metafile.


Gets the type of a metafile.


Gets the version number of a metafile.


Gets the WMF header of a metafile.

Figure 8.6 shows the output from Listing 8.7.

Figure 8.6. Reading metafile header attributes


GDI+: The Next-Generation Graphics Interface

Your First GDI+ Application

The Graphics Class

Working with Brushes and Pens

Colors, Fonts, and Text

Rectangles and Regions

Working with Images

Advanced Imaging

Advanced 2D Graphics



Developing GDI+ Web Applications

GDI+ Best Practices and Performance Techniques

GDI Interoperability

Miscellaneous GDI+ Examples

Appendix A. Exception Handling in .NET

GDI+ Programming with C#
GDI+ Programming with C#
ISBN: 073561265X
Year: 2003
Pages: 145 © 2008-2020.
If you may any questions please contact us: