1. Herfried K. Wagner’s VB.Any
  2. .NET
  3. Frequently Asked Questions

Compacting a path to a certain number of pixels

Compacting a path to a certain number of pixels
<URL:https://dotnet.currifex.org/dotnet/faqs/compactpath/en/>
----------------------------------------------------------------------------

Compacting a path to a certain number of pixels

The function 'CompactPath' can be used to compact a path for being
displayed on a 'Graphics' object with a specific font:

\\\
Private Declare Auto Function PathCompactPath Lib "shlwapi.dll" ( _
    ByVal hDC As IntPtr, _
    ByVal lpszPath As String, _
    ByVal dx As Int32 _
) As Boolean

Public Function CompactPath( _
    ByVal Path As String, _
    ByVal Destination As Graphics, _
    ByVal MaxWidth As Integer _
) As String
    Dim hDC As IntPtr = Destination.GetHdc()
    Dim Success As Boolean = PathCompactPath(hDC, Path, MaxWidth)
    Destination.ReleaseHdc(hDC)
    If Success Then
        Dim p As Integer = Strings.InStr(Path, ControlChars.NullChar) - 1
        If p > 0 Then
            Return Strings.Left(Path, p)
        Else
            Return Path
        End If
    Else
        Throw New Exception("String does not fit into space.")
    End If
End Function
///

Usage:

\\\
Dim g As Graphics = Me.CreateGraphics()
Try
    MsgBox( _
        CompactPath( _
            "C:\Program Files\Some Company\Some Product\Data\File.txt", _
            g, _
            180 _
        ) _
    )
Catch ex As Exception
    MsgBox(ex.Message)
End Try
g.Dispose()
///

Catching the exception is a very costly operation, returning a Boolean
variable that indicates the success of the operation would be the better
approach when calling the method several times.

If the compacted path string is not needed, there is a managed alternative
to the code shown above that can be used to draw the compacted path to a
'Graphics' object.  The listing below contains a skeleton of an
implementation for a label control displaying a compacted path.  In order to
use this control in a real-world application, objects need to be disposed
and some other properties need to be taken into account when drawing the
text.  Basing the control on 'System.Windows.Forms.Label' would be possible
too:

\\\
Imports System.Drawing
Imports System.Windows.Forms

''' <summary>
'''   Provides a simple label control for displaying a compacted path.
''' </summary>
Public Class TrimmedLabel
    Inherits Control

    Private m_ForeColorBrush As SolidBrush = New SolidBrush(Me.ForeColor)
    Private m_StringFormat As StringFormat = _
        New StringFormat(StringFormatFlags.NoWrap)

    ''' <summary>
    '''   Creates a new instance of <c>PathLabel</c>.
    ''' </summary>
    Public Sub New()
        Me.SetStyle( _
            ControlStyles.AllPaintingInWmPaint Or _
            ControlStyles.DoubleBuffer Or _
            ControlStyles.ResizeRedraw, _
            True _
        )
        Me.UpdateStyles()
        m_StringFormat.Trimming = StringTrimming.EllipsisPath
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        MyBase.OnPaintBackground(e)
        e.Graphics.DrawString( _
            Me.Text, _
            Me.Font, _
            m_ForeColorBrush, _
            RectangleF.op_Implicit(Me.ClientRectangle), _
            m_StringFormat _
        )
    End Sub

    Protected Overrides Sub OnForeColorChanged(ByVal e As EventArgs)
        m_ForeColorBrush = New SolidBrush(Me.ForeColor)
        MyBase.OnForeColorChanged(e)
    End Sub

    Protected Overrides Sub OnTextChanged(ByVal e As EventArgs)
        Me.Invalidate()
        MyBase.OnTextChanged(e)
    End Sub
End Class
///