File Open and Save As Dialogs for MFC Applications Using GDI+

Introduction

The CGdiplusFileOpenDialog and CGdiplusFileSaveAsDialog classes offer an easy way to implement File Open and Save As dialogs in MFC applications that use GDI+.

The main features are:

  • They dynamically load information about the supported image decoders/encoders and construct the file filters list.
  • They get the current image encoder identifier (CLSID) to be used in GDI+ functions that require it as an argument.
  • They check and validate the file name and extension.

You can find more details in the class descriptions and demo application.

Hierarchy Chart

The CGdiplusFileDialog Class

CGdiplusFileDialog is derived from the MFC CFileDialog class and is an abstract base class for CGdiplusFileOpenDialog and CGdiplusFileSaveAsDialog. It performs the following tasks:

  • Encapsulates an array containing information about supported decoders/encoders (m_arrCodecInfo)
  • Implements the base function TranslateFilter that converts an MFC-like file filter (using the '|' character as a separator) into an OPENFILENAME structure file filter (using '\0' as a separator)
  • Declares two pure virtual functions, FillCodecInfoArray and ConstructMFCStyleFilter; in the derived classes, the overridden functions perform specific operations
  • Overrides CFileDialog::DoModal; before calling base class function, it
    • Checks whether the GDI+ library is initialised
    • Calls the FillCodecInfoArray, ConstructMFCStyleFilter, and TranslateFilter functions

The CGdiplusFileOpenDialog Class

CGdiplusFileOpenDialog implements the File Open common dialog.

  • Overrides CGdiplusFileDialog::FillCodecInfoArray and calls Gdiplus::GetImageDecoders to fill an array of information about available decoders:
    • Name of the file format (for example, "JPEG")
    • Used extensions (for example, "*.JPG;*.JPEG;*.JPE;*.JFIF")
    • Default extension to be used in Save As dialog (for example, "JPG")
    • Decoder identifier (CLSID)
  • Overrides CGdiplusFileDialog::ConstructMFCStyleFilter that constructs an "MFC style" filter (using a '|' as a separator); an additional "All GDI+ supported" filter item is added

Example

#include "GdiplusFileOpenDialog.h"
// ...
bool CFoo::LoadImage(Image*& pImage)
{
   bool bLoaded = false;
   CGdiplusFileOpenDialog dlgFile;
   if(IDOK == dlgFile.DoModal())
   {
      CString strPathName = dlgFile.GetPathName();
      pImage = Image::FromFile(strPathName.AllocSysString());
      Status status = pImage->GetLastStatus();
      if(Ok == status)
      {
         bLoaded = true;
      }
   }
   return bLoaded;
}

The CGdiplusFileSaveAsDialog Class

CGdiplusFileSaveAsDialog implements the File Save As common dialog.

  • Overrides CGdiplusFileDialog::FillCodecInfoArray and calls Gdiplus::GetImageEncoders to fill an array of information about available encoders:
    • Name of the file format (for example, "BMP")
    • Used extensions (for example, "*.BMP;*.DIB;*.RLE")
    • Default extension to be used in Save As dialog (for example, "BMP")
    • Decoder identifier (CLSID)
  • Overrides CGdiplusFileDialog::ConstructMFCStyleFilter that constructs the "MFC style" filter (using a '|' as a separator)
  • Overrides CFileDialog::OnInitDone and CFileDialog::OnTypeChange to set the default extension and store the default encoder identifier (m_clsid member variable)
  • Implements the public method GetCodecCLSID; the codec CLSID can further be passed to the Gdiplis::Image::Save function
  • Overrides CFileDialog::OnFileNameOK to perform file name extension checking:
    • If the file name has no extension, the default extension and CLSID is used
    • If the file name has an extension and can be found in the codec info array, that extension and the corresponding CLSID will be used instead of the default
    • If the file name has an extension and cannot be found in the codec info array, it returns TRUE and the dialog remains displayed to allow the user to enter another file name

Example

#include "GdiplusFileSaveAsDialog.h"
// ...
bool CFoo::SaveImage(Image* pImage)
{
   bool bSaved = false;
   CGdiplusFileSaveAsDialog dlgFile;
   if(IDOK == dlgFile.DoModal())
   {
      CLSID clsid = dlgFile.GetCodecCLSID();
      CString strPathName = dlgFile.GetPathName();
      Status status = pImage->Save(strPathName.AllocSysString(),
                                   &clsid);
      if(Ok == status)
      {
         bSaved = true;
      }
   }
   return bSaved;
}

File Open and Save As Dialogs for MFC Applications Using GDI+

Demo Application

The demo application is a simple image viewer built on the MDI framework. It demonstrates how to use CGdiplusFileOpenDialog and CGdiplusFileSaveAsDialog.

[4-demo.jpg]

StdAfx.h includes and declarations

typedef unsigned __int32 ULONG_PTR;
#include <afxtempl.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;

Notes

  • For newer versions than VC++6.0, or if you have installed the platform SDK update, remove the definition of ULONG_PTR.
  • (only VC++ 6.0) Be sure that the gdiplus.lib location is found in the Tools/Option/Directories/Library files; alternatively, you can provide the full path and file name in the pragma comment directive.

Initializing and releasing GDI+

You must call GdiplusStartup before making any other GDI+ calls, and call GdiplusShutdown when you have finished using GDI+.

class CDemoApp : public CWinApp
{
// ...
// Attributes
protected:
   ULONG_PTR m_gdiplusToken;
// ...
// Implementation
protected:
   bool InitGdiplus();
   void TermGdiplus();
// ...
};
// Called from CDemoApp::InitInstance
bool CDemoApp::InitGdiplus()
{
   GdiplusStartupInput gdiplusStartupInput;
   Status status = GdiplusStartup(&m_gdiplusToken,
                                  &gdiplusStartupInput,
                                  NULL);
   return (Ok == status);
}
// Called from CDemoApp::ExitInstance
void CDemoApp::TermGdiplus()
{
   GdiplusShutdown(m_gdiplusToken);
}

Using CGdiplusFileOpenDialog

Remove the default mapping of the ID_FILE_OPEN command that calls the base class message handler and map the command to be handled in a CWinApp-derived class.

void CDemoApp::OnFileOpen()
{
   CGdiplusFileOpenDialog dlgFile;
   if(IDOK == dlgFile.DoModal())
   {
      OpenDocumentFile(dlgFile.GetPathName());
   }
}

Override the CDocument::OnOpenDocument virtual function.

class CDemoDoc : public CDocument
{
// ...
// Attributes
protected:
   Image* m_pImage;
// Operations
public:
   Image* GetImage() {return m_pImage;}
// ...
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDemoDoc)
public:
   virtual BOOL OnOpenDocument(LPCTSTR pszPathName);
//}}AFX_VIRTUAL
// ...
};

BOOL CDemoDoc::OnOpenDocument(LPCTSTR pszPathName) 
{
   SafeDeleteImage();
   m_pImage = Image::FromFile(CString(pszPathName).AllocSysString());
   return (Ok == m_pImage->GetLastStatus());
}

Using CGdiplusFileSaveAsDialog

Map the ID_FILE_SAVE_AS command in the CDocument-derived class.
After CGdiplusFileSaveAsDialog::DoModal returns, call GetPathName and GetCodecCLSID to be passed to the Image::Save method.

void CDemoDoc::OnFileSaveAs()
{
   CGdiplusFileSaveAsDialog dlgFile;
   if(IDOK == dlgFile.DoModal())
   {
      CLSID clsid = dlgFile.GetCodecCLSID();
      CString strPathName = dlgFile.GetPathName();
      Status status = m_pImage->Save(strPathName.AllocSysString(),
                                     &clsid);
      if(Ok != status)
      {
         TRACE2(_T("\nFailed to save image in '%s' file\n")
                _T("GDI+ Error: %u"),
                strPathName,
                status);
      }
   }
}

Drawing the image

In CDemoView::OnDraw, use a Gdiplus::Graphics object to draw the image.
Note that is just an example and is designed to be as simple as possible.

void CDemoView::OnDraw(CDC* pDC)
{
   CDemoDoc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   Image* pImage = pDoc->GetImage();
   if(NULL != pImage)
   {
      Graphics graphics(*pDC);
      Status status = graphics.GetLastStatus();
      if(Ok == status)
      {
         graphics.DrawImage(pImage, 0, 0,
                            pImage->GetWidth(), pImage->GetHeight());
      }
   }
}

Final Notes

  • gdiplus.dll is included in Windows XP and Windows Server 2003
  • gdiplus.dll is required as a redistributable for applications that run on Microsoft Windows NT 4.0-SP6/2000/98/Me
  • If you redistribute gdiplus.dll, it is recommended that you place it in the application folder.

Downloads



About the Author

Ovidiu Cucu

Graduated at "Gh. Asachi" Technical University - Iasi, Romania. Programming in C++ using Microsoft technologies since 1995. Microsoft MVP awardee since 2006. Moderator and article reviewer at Codeguru.com, the number one developer site. Co-founder of Codexpert.ro, a website dedicated to Romanian C++ developers.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Intelligent N+X Redundancy, Placement Affinities, & Future Proofing in the Virtualized Data Center Virtualization brought about the ability to simplify business continuity management in IT. Workload portability and data replication capabilities mean that physical infrastructure failures no longer need impact application services, and they can rapidly be recovered even in the event of complete site failure. However, Enterprises and Service Providers face new challenges ensuring they have enough compute …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds