【发布时间】:2010-12-18 08:44:08
【问题描述】:
我有一个固定长度的字段,我想在其中显示路径信息。我认为在 .NET 中有一种方法可以通过插入省略号来缩写长路径名以适应固定长度的字段,例如“.. ....\myfile.txt"。我一辈子都找不到这种方法。
【问题讨论】:
我有一个固定长度的字段,我想在其中显示路径信息。我认为在 .NET 中有一种方法可以通过插入省略号来缩写长路径名以适应固定长度的字段,例如“.. ....\myfile.txt"。我一辈子都找不到这种方法。
【问题讨论】:
TextRenderer.MeasureText 显示缩写路径
我遇到了与 OP 相同的困境,因为我需要一个解决方案来显示一个简短的路径,而不需要太多麻烦,以便我的 UI 可以轻松地显示路径的主要部分。最终解决方案:像这样使用TextRenderer的MeasureText方法:
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) 更改为压缩字符串。这看起来很奇怪,因为没有明确指定 ref 或 out 方法参数关键字并且字符串是不可变的。然而,它肯定是这样的。我假设字符串的指针是通过不安全的代码更新到新的压缩字符串的。
作为另一个出租人说明,由于我希望压缩字符串压缩路径,因此我还使用了TextFormatFlags.PathEllipsis 格式化选项。
我发现在控件上创建一个小的扩展方法很方便,这样任何控件(如 TextBox 和 Label)都可以将其文本设置为压缩路径:
public static void SetTextAsCompactedPath(this Control control, string path)
{
control.Text = GetCompactedString(path, control.Font, control.Width);
}
对于紧凑路径以及一些可以使用的 WinApi 调用,有一些自制的解决方案。
您可以使用 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();
}
还有更多解决方案涉及正则表达式和路径智能解析以确定省略号的放置位置。这是我看到的一些:
【讨论】:
如果有人正在寻找相同的东西。我制作了这个函数:
/// <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;
}
【讨论】:
从这个Coding Horror blog post关于缩短长文件路径有一个Windows API调用PathCompactPathEx你可以使用。
【讨论】:
PathCompactPathEx pinvoke 示例有问题。我试过他的代码,如果你运行几次它就会崩溃。 pinvoke.com 具有相同的代码,但在使用它之前初始化了字符串构建器的大小,并带有 // NOTE: You need to create the builder with the required capacity before calling function. 的注释以及指向 msdn.microsoft.com/en-us/library/aa446536.aspx 的参考链接。我认为这是一个重要的区别。见pinvoke.com version of the code。
您正在考虑StringTrimming.EllipsisPath 枚举常量,它可以与StringFormat 一起使用,以绘制使用Graphics.DrawString 的修剪路径。但是,.Net 本身没有返回修剪路径的方法。
【讨论】:
我不知道自动执行此操作的方法,但您可以轻松创建一个使用 Substring() 和 LastIndexOf("\") 或 System.IO.Path.GetFileName() 方法来获取的方法文件名,然后在前面加上你的点。
【讨论】:
怎么样:
string longPath = @"c:\somewhere\myfile.txt";
string shortPath = @"..\" + Path.GetFileName(longPath);
【讨论】:
我在这里找到了一个从 TextBox 派生的易于使用的类: EllipsisTextBox ,它封装了 StringTrimming.EllipsisPath。为我工作!
【讨论】: