Introduction

Often in applications, there is a need to show a filename, for example in a read only edit control. However, the filename maybe larger than can be accommodated in the edit control. Many Microsoft applications show the filename "compacted", whereby part of the path is replaced by ellipsis ("..."). The need arose to do the same for the application that I am currently developing. My solution, presented below, was to create my own class, based on the MFC CEdit control, to handle the filename appropriately.

Path compaction

A short investigation into how to go about compacting the path led me to the function PathCompactPath(). This function is part of the shlwapi.dll, which means that it is not available on systems running Windows 95 or Windows NT unless Internet Explorer 4.,0 or greater is installed. Fortunately, this covered all of the users of our system, and I would imagine it covers most users of 32 bit Windows operating systems.

Using the function is exceedingly simple. It takes three parameters, an HDC, the filename to compact, and the amount of space the text is to fit into. Therefore, to compact the path sufficiently to fit into the edit control, we simply use the following code :

CString CFilenameEdit::GetDisplayName()
{
// Get the DC for the control, and the rectangle into which we draw text
CDC * pDC = GetDC();
CRect rectClient;
GetRect(&rectClient);
// Select the correct font into the DC
CFont * pFontOld = pDC->SelectObject(GetFont());
// Get the display name, and truncate it to fit into the control// Note that we make the buffer larger than max path // in case we have some bizarre// situation where a load of ellipsis are inserted // and it overruns the buffer// Member variable containing filename; set by caller
CString strDisplayName = m_strFilename;
PathCompactPath(pDC->GetSafeHdc(),
strDisplayName.GetBuffer(_MAX_PATH + 1 + BUFFER_EXTRA),
rectClient.Width());
strDisplayName.ReleaseBuffer();
// Clean up!
pDC->SelectObject(pFontOld);
ReleaseDC(pDC);
return strDisplayName;
}

Note that we have to select the correct font (obtained by calling CEdit::GetFont()) into the DC, before using the function, otherwise the incorrect font is used when determining the text length.

A concern was that, the function would return a string longer than _MAX_PATH characters, in which case we would suffer buffer overrun. This could happen in the case that the ellipsis (essentially three period characters) had a shorter text extent than two characters, and hence two characters in the string were replaced by three. Hence, the call to CString::GetBuffer() requests a slightly larger buffer, just in case!

Using the control

CFilenameEdit, derived from CEdit, is designed to be read only, the idea being that the filename can be set by the parent application, say for example when a user selects a file from a CFileDialog.

The control has just two public functions, SetFilename() and GetFilename, which set and retrieve the filename to be displayed. The call to SetFilename() maintains an internal copy of the full filename, and sets the control text to the compacted filename, obtained using the code above. GetFilename retrieves the full filename, regardless of how it is compacted in the control.

Because the control text will differ from the filename that is to be displayed, the normal DDX_Text routines are no good - when the text is loaded into the control, the full filename will be placed in the control. Similarly, if the compacted filename is displayed in the control, this is exactly what the DDX_Text routine will return when saving the content of the control. Hence, in addition to the control, there is also a DDX_??? routine, DDX_Filename, which calls the SetFilename() and GetFilename() functions accordingly.

As a visual aid to the user, a tooltip control is created. When the mouse hovers over the control, the tooltip displays the complete path.

Demonstration project

The demonstration project has two edit controls, one of which has been subclassed to provide the CFilenameEdit functionality. The other is a standard edit control, which can be used to compare the contents of the controls when a filename is placed into them.

The Browse button allows a file to be chosen, the name of which will be shown in both edit controls. The Get Content button displays the filename shown in the filename edit control, proving that the DDX_Filename routine works correctly.

Conclusion

The control as it stands is exactly what was need for the application I am currently developing. There are so many things that could be added to it, and maybe when I get chance I will add more. Other articles here show how to make the control accept files that have been dragged and dropped onto it, and how to build a Browse button into the control - both ideas that could be incorporated to enhance this control.

At this point, I really ought to pass on my thanks to Roger Allen, whose snippet of information about setting the font in the DC before calling PathCompactPath made the result somewhat more accurate!

Share

About the Author

I started computer programming on the Spectrum (writing nothing more complicated than "Hello World" and a few programs that tunelessly Beeped ad infinitum) but then progressed to slightly more serious programming on the Amiga.

After A-Levels in Maths, Physics and Chemistry, I went to the University of East Anglia, Norwich, and studied beer, women and Computing Science.
Some years after graduating, I still have an appreciation of Computing Science, but as I am now married, my other studies are frowned upon.

Since graduating, I have worked on many diverse projects in areas including call centres, logistics, architecture and engineering, and heritage.

Comments and Discussions

hey hi
i tried your code it working.
But the problem is that if the text or the path size is increase its truncating the drive location also. for example \\D:\\sarfarz\........\.........\.....\sar.mov fot this the out is like this .....\\.......\...\sar.mov.

In some cases CFilenameEdit::PreTranslateMessage() is never called, which causes the tool tip to never show (e.g. as per MSDN article (ID 126874) "PRB: Modal Dialog Box Prevents Calls to PreTranslateMessage" at http://support.microsoft.com/default.aspx?scid=kb;en-us;126874[^]). To fix this problem, add the following message handlers to CFilenameEdit:

Thanks for your comment - which has really set me thinking.
The problem is that the user could quite validly select an area of the text in the edit control, and copy that. Equally, they could select the whole content and copy that.
The this is that I don't think I can make the assumption that the user doesn't want to copy anything other than what is shown in the control.
Maybe the normal right click menu could be extended to include an item allowing the user to copy the full path as opposed to the selected content of the control though.

You can use a static control with the SS_PATHELLIPSIS style set. Doc's say you need NT or later for this, however I'm sure it works on Win98 etc. I use SS_PATHELLIPSIS in ED4W (see sig) and it works well for me. I also use PathCompactPath() and PathCompactPathEx() in places.

"Windows NT or later: Replaces characters in the middle of the string with ellipses so that the result fits in the specified rectangle. If the string contains backslash (\) characters, SS_PATHELLIPSIS preserves as much as possible of the text after the last backslash. Compare with SS_ENDELLIPSIS and SS_WORDELLIPSIS."