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()
{
CDC * pDC = GetDC();
CRect rectClient;
GetRect(&rectClient);
CFont * pFontOld = pDC->SelectObject(GetFont());
CString strDisplayName = m_strFilename;
PathCompactPath(pDC->GetSafeHdc(),
strDisplayName.GetBuffer(_MAX_PATH + 1 + BUFFER_EXTRA),
rectClient.Width());
strDisplayName.ReleaseBuffer();
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!