如何在.NET中显示缩写的路径名

时间:2009-11-19 15:42:50

标签: .net path filenames

我有一个固定长度的字段我想显示路径信息。我想在.NET中有一种方法可以通过插入省略号来缩写长路径名以适应固定长度字段,例如“.. .... \ myfile.txt的”。我不能为我的生活找到这种方法。

7 个答案:

答案 0 :(得分:17)

使用TextRendererMeasureText

显示缩略路径

我遇到了与OP相同的困境,因为我需要一个解决方案来显示一条没有太多麻烦的缩写路径,以便我的UI可以轻松地显示路径的主要部分。最终解决方案:使用TextRendererMeasureText方法,如下所示:

public static string GetCompactedString(
   string stringToCompact, Font font, int maxWidth)
{
   // Copy the string passed in since this string will be
   // modified in the TextRenderer's MeasureText method
   string compactedString = string.Copy(stringToCompact);
   var maxSize = new Size(maxWidth, 0);
   var formattingOptions = TextFormatFlags.PathEllipsis 
                         | TextFormatFlags.ModifyString;
   TextRenderer.MeasureText(compactedString, font, maxSize, formattingOptions);
   return compactedString;
}

重要说明:传递TextFormatFlags.ModifyString格式化选项实际上会导致MeasureText方法将字符串参数(compactedString)更改为压缩字符串。这似乎很奇怪,因为没有指定明确的refout方法参数关键字,并且字符串是不可变的。但绝对是这样的。我假设字符串的指针通过不安全的代码更新为新的压缩字符串。

作为另一个出租人注意,由于我希望压缩字符串压缩路径,我还使用了TextFormatFlags.PathEllipsis格式化选项。

我发现在控件上创建一个小扩展方法很方便,所以任何控件(如TextBox和Label)都可以将它们的文本设置为压缩路径:

public static void SetTextAsCompactedPath(this Control control, string path)
{
   control.Text = GetCompactedString(path, control.Font, control.Width);
}

压缩路径的其他方法:

有紧凑型路径的本土解决方案以及可以使用的一些WinApi调用。

PathCompactPathEx - 根据字符中所需的字符串长度压缩字符串

您可以使用pinvoke调用PathCompactPathEx根据您希望限制的字符数压缩字符串。请注意,这不会考虑字体以及字符串在屏幕上显示的宽度。

代码来源PInvoke:http://www.pinvoke.net/default.aspx/shlwapi.pathcompactpathex

[DllImport("shlwapi.dll", CharSet=CharSet.Auto)]
static extern bool PathCompactPathEx(
   [Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);

public static string CompactPath(string longPathName, int wantedLength)
{
   // NOTE: You need to create the builder with the 
   //       required capacity before calling function.
   // See http://msdn.microsoft.com/en-us/library/aa446536.aspx
   StringBuilder sb = new StringBuilder(wantedLength + 1);
   PathCompactPathEx(sb, longPathName, wantedLength + 1, 0);
   return sb.ToString();
}

其他解决方案

还有更多的解决方案,包括正则表达式和路径的智能解析,以确定放置省略号的位置。这些是我看到的一些:

答案 1 :(得分:6)

如果有人在寻找同样的事情。我把这个功能搞定了:

/// <summary>
/// Shortens a file path to the specified length
/// </summary>
/// <param name="path">The file path to shorten</param>
/// <param name="maxLength">The max length of the output path (including the ellipsis if inserted)</param>
/// <returns>The path with some of the middle directory paths replaced with an ellipsis (or the entire path if it is already shorter than maxLength)</returns>
/// <remarks>
/// Shortens the path by removing some of the "middle directories" in the path and inserting an ellipsis. If the filename and root path (drive letter or UNC server name)     in itself exceeds the maxLength, the filename will be cut to fit.
/// UNC-paths and relative paths are also supported.
/// The inserted ellipsis is not a true ellipsis char, but a string of three dots.
/// </remarks>
/// <example>
/// ShortenPath(@"c:\websites\myproject\www_myproj\App_Data\themegafile.txt", 50)
/// Result: "c:\websites\myproject\...\App_Data\themegafile.txt"
/// 
/// ShortenPath(@"c:\websites\myproject\www_myproj\App_Data\theextremelylongfilename_morelength.txt", 30)
/// Result: "c:\...gfilename_morelength.txt"
/// 
/// ShortenPath(@"\\myserver\theshare\myproject\www_myproj\App_Data\theextremelylongfilename_morelength.txt", 30)
/// Result: "\\myserver\...e_morelength.txt"
/// 
/// ShortenPath(@"\\myserver\theshare\myproject\www_myproj\App_Data\themegafile.txt", 50)
/// Result: "\\myserver\theshare\...\App_Data\themegafile.txt"
/// 
/// ShortenPath(@"\\192.168.1.178\theshare\myproject\www_myproj\App_Data\themegafile.txt", 50)
/// Result: "\\192.168.1.178\theshare\...\themegafile.txt"
/// 
/// ShortenPath(@"\theshare\myproject\www_myproj\App_Data\", 30)
/// Result: "\theshare\...\App_Data\"
/// 
/// ShortenPath(@"\theshare\myproject\www_myproj\App_Data\themegafile.txt", 35)
/// Result: "\theshare\...\themegafile.txt"
/// </example>
public static string ShortenPath(string path, int maxLength)
{
    string ellipsisChars = "...";
    char dirSeperatorChar = Path.DirectorySeparatorChar;
    string directorySeperator = dirSeperatorChar.ToString();

    //simple guards
    if (path.Length <= maxLength)
    {
        return path;
    }
    int ellipsisLength = ellipsisChars.Length;
    if (maxLength <= ellipsisLength)
    {
        return ellipsisChars;
    }


    //alternate between taking a section from the start (firstPart) or the path and the end (lastPart)
    bool isFirstPartsTurn = true; //drive letter has first priority, so start with that and see what else there is room for

    //vars for accumulating the first and last parts of the final shortened path
    string firstPart = "";
    string lastPart = "";
    //keeping track of how many first/last parts have already been added to the shortened path
    int firstPartsUsed = 0;
    int lastPartsUsed = 0;

    string[] pathParts = path.Split(dirSeperatorChar);
    for (int i = 0; i < pathParts.Length; i++)
    {
        if (isFirstPartsTurn)
        {
            string partToAdd = pathParts[firstPartsUsed] + directorySeperator;
            if ((firstPart.Length + lastPart.Length + partToAdd.Length + ellipsisLength) > maxLength)
            {
                break;
            }
            firstPart = firstPart + partToAdd;
            if (partToAdd == directorySeperator)
            {
                //this is most likely the first part of and UNC or relative path 
                //do not switch to lastpart, as these are not "true" directory seperators
                //otherwise "\\myserver\theshare\outproject\www_project\file.txt" becomes "\\...\www_project\file.txt" instead of the intended "\\myserver\...\file.txt")
            }
            else
            {
                isFirstPartsTurn = false;
            }
            firstPartsUsed++;
        }
        else
        {
            int index = pathParts.Length - lastPartsUsed - 1; //-1 because of length vs. zero-based indexing
            string partToAdd = directorySeperator + pathParts[index];
            if ((firstPart.Length + lastPart.Length + partToAdd.Length + ellipsisLength) > maxLength)
            {
                break;
            }
            lastPart = partToAdd + lastPart;
            if (partToAdd == directorySeperator)
            {
                //this is most likely the last part of a relative path (e.g. "\websites\myproject\www_myproj\App_Data\")
                //do not proceed to processing firstPart yet
            }
            else
            {
                isFirstPartsTurn = true;
            }
            lastPartsUsed++;
        }
    }

    if (lastPart == "")
    {
        //the filename (and root path) in itself was longer than maxLength, shorten it
        lastPart = pathParts[pathParts.Length - 1];//"pathParts[pathParts.Length -1]" is the equivalent of "Path.GetFileName(pathToShorten)"
        lastPart = lastPart.Substring(lastPart.Length + ellipsisLength + firstPart.Length - maxLength, maxLength - ellipsisLength - firstPart.Length);
    }

    return firstPart + ellipsisChars + lastPart;
}

Originial post with (a little) background here

答案 2 :(得分:4)

通过缩短长文件路径的Coding Horror blog post,您可以使用Windows API调用PathCompactPathEx

答案 3 :(得分:1)

您正在考虑StringTrimming.EllipsisPath枚举常量,该常量可以与StringFormat一起使用绘制使用Graphics.DrawString修剪的路径。但是,.Net本身没有返回修剪路径的方法。

答案 4 :(得分:0)

我不知道自动执行此操作的方法,但您可以轻松创建一个iether使用Substring()和LastIndexOf(“\”)或System.IO.Path.GetFileName()方法来获取文件名,然后加上你的点。

答案 5 :(得分:0)

怎么样:

string longPath = @"c:\somewhere\myfile.txt";
string shortPath = @"..\" + Path.GetFileName(longPath);

答案 6 :(得分:0)

我发现了一个易于使用的类,它来自TextBox: EllipsisTextBox ,它封装了StringTrimming.EllipsisPath。适合我!