. СЕТЕВЫЕ Базовые 2.0 имеют Path.GetRelativePath
, еще, использование это.
/// <summary>
/// Creates a relative path from one file or folder to another.
/// </summary>
/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
/// <returns>The relative path from the start directory to the end path or <c>toPath</c> if the paths are not related.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="UriFormatException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static String MakeRelativePath(String fromPath, String toPath)
{
if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath");
Uri fromUri = new Uri(fromPath);
Uri toUri = new Uri(toPath);
if (fromUri.Scheme != toUri.Scheme) { return toPath; } // path can't be made relative.
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
}
Если у Вас есть текстовое поле только для чтения, разве Вы не могли бы не сделать его маркировкой и установить AutoEllipsis=true?
альтернативно существуют сообщения с кодом для генерации автозамещающего знака самостоятельно: (это делает это для сетки, необходимо было бы передать i ширина для текстового поля вместо этого. Это не совершенно правильно, поскольку это взламывает от немного больше, чем, необходимо, и я приют; t нашел время для нахождения, где вычисление является неправильным. было бы достаточно легко изменить для удаления первой части каталога, а не последнего, если Вы требуете.
Private Function AddEllipsisPath(ByVal text As String, ByVal colIndex As Integer, ByVal grid As DataGridView) As String
'Get the size with the column's width
Dim colWidth As Integer = grid.Columns(colIndex).Width
'Calculate the dimensions of the text with the current font
Dim textSize As SizeF = MeasureString(text, grid.Font)
Dim rawText As String = text
Dim FileNameLen As Integer = text.Length - text.LastIndexOf("\")
Dim ReplaceWith As String = "\..."
Do While textSize.Width > colWidth
' Trim to make room for the ellipsis
Dim LastFolder As Integer = rawText.LastIndexOf("\", rawText.Length - FileNameLen - 1)
If LastFolder < 0 Then
Exit Do
End If
rawText = rawText.Substring(0, LastFolder) + ReplaceWith + rawText.Substring(rawText.Length - FileNameLen)
If ReplaceWith.Length > 0 Then
FileNameLen += 4
ReplaceWith = ""
End If
textSize = MeasureString(rawText, grid.Font)
Loop
Return rawText
End Function
Private Function MeasureString(ByVal text As String, ByVal fontInfo As Font) As SizeF
Dim size As SizeF
Dim emSize As Single = fontInfo.Size
If emSize = 0 Then emSize = 12
Dim stringFont As New Font(fontInfo.Name, emSize)
Dim bmp As New Bitmap(1000, 100)
Dim g As Graphics = Graphics.FromImage(bmp)
size = g.MeasureString(text, stringFont)
g.Dispose()
Return size
End Function
Я разделил оба из Ваших путей на уровне каталога. Оттуда, найдите точку развилки и работу Вашим путем назад к папке блока, предварительно ожидая a '.. /' каждый раз Вы передаете каталог.
Имеют в виду однако, что полный путь работает везде и обычно легче читать, чем относительный. Я лично не показал бы пользователю относительный путь, если это не было абсолютно необходимо.
Использование:
RelPath = AbsPath.Replace(ApplicationPath, ".")
Если Вы уверены, что Ваш полный путь 2 всегда относительно полного пути, просто удалите первые символы N из path2, где N является длиной path1.
Как Alex Brault указывает, особенно в Windows, полный путь (с буквой диска и всеми) однозначен и часто лучше.
разве Ваш OpenFileDialog не должен использовать регулярную структуру древовидного браузера?
Для получения некоторой номенклатуры на месте RefDir является каталогом, относительно которого Вы хотите определить путь; AbsName является абсолютным путем, который Вы хотите отобразить; и RelPath является получающимся относительным путем.
Выбирают первый из этих вариантов, который соответствует:
Для иллюстрирования последнего правила (который является, конечно, безусловно самым сложным) запустите с:
RefDir = D:\Abc\Def\Ghi
AbsName = D:\Abc\Default\Karma\Crucible
Тогда
LCP = D:\Abc
(RefDir - LCP) = Def\Ghi
(Absname - LCP) = Default\Karma\Crucible
RelPath = ..\..\Default\Karma\Crucible
, В то время как я вводил, DavidK произвел ответ, который предполагает, что Вы не являетесь первыми для необходимости в этой функции и что существует стандартная функция, чтобы сделать это задание. Использование это. , Но нет никакого вреда в способности продумать Ваш путь от первых принципов, также.
За исключением того, что системы Unix не поддерживают буквы дисков (таким образом, все всегда располагается под тем же корневым каталогом, и первый маркер поэтому не важен), та же техника могла использоваться на Unix.
Это - длинный путь вокруг, но Система. Класс uri имеет метод под названием MakeRelativeUri. Возможно, Вы могли использовать это. Это - позор действительно та Система. IO.Path не имеет этого.
Я использовал это в прошлом.
/// <summary>
/// Creates a relative path from one file
/// or folder to another.
/// </summary>
/// <param name="fromDirectory">
/// Contains the directory that defines the
/// start of the relative path.
/// </param>
/// <param name="toPath">
/// Contains the path that defines the
/// endpoint of the relative path.
/// </param>
/// <returns>
/// The relative path from the start
/// directory to the end path.
/// </returns>
/// <exception cref="ArgumentNullException"></exception>
public static string MakeRelative(string fromDirectory, string toPath)
{
if (fromDirectory == null)
throw new ArgumentNullException("fromDirectory");
if (toPath == null)
throw new ArgumentNullException("toPath");
bool isRooted = (Path.IsPathRooted(fromDirectory) && Path.IsPathRooted(toPath));
if (isRooted)
{
bool isDifferentRoot = (string.Compare(Path.GetPathRoot(fromDirectory), Path.GetPathRoot(toPath), true) != 0);
if (isDifferentRoot)
return toPath;
}
List<string> relativePath = new List<string>();
string[] fromDirectories = fromDirectory.Split(Path.DirectorySeparatorChar);
string[] toDirectories = toPath.Split(Path.DirectorySeparatorChar);
int length = Math.Min(fromDirectories.Length, toDirectories.Length);
int lastCommonRoot = -1;
// find common root
for (int x = 0; x < length; x++)
{
if (string.Compare(fromDirectories[x], toDirectories[x], true) != 0)
break;
lastCommonRoot = x;
}
if (lastCommonRoot == -1)
return toPath;
// add relative folders in from path
for (int x = lastCommonRoot + 1; x < fromDirectories.Length; x++)
{
if (fromDirectories[x].Length > 0)
relativePath.Add("..");
}
// add to folders to path
for (int x = lastCommonRoot + 1; x < toDirectories.Length; x++)
{
relativePath.Add(toDirectories[x]);
}
// create relative path
string[] relativeParts = new string[relativePath.Count];
relativePath.CopyTo(relativeParts, 0);
string newPath = string.Join(Path.DirectorySeparatorChar.ToString(), relativeParts);
return newPath;
}
Существует Win32 (C++) функция в shlwapi.dll, который делает точно, что Вы хотите: PathRelativePathTo()
я не знаю ни о каком способе получить доступ к этому от.NET кроме к P/Invoke это, все же.
Немного поздно к вопросу, но мне просто была нужна эта функция также. Я соглашаюсь с DavidK, что, так как существует встроенная API-функция , которая обеспечивает это, необходимо использовать его. Вот управляемая оболочка для него:
public static string GetRelativePath(string fromPath, string toPath)
{
int fromAttr = GetPathAttribute(fromPath);
int toAttr = GetPathAttribute(toPath);
StringBuilder path = new StringBuilder(260); // MAX_PATH
if(PathRelativePathTo(
path,
fromPath,
fromAttr,
toPath,
toAttr) == 0)
{
throw new ArgumentException("Paths must have a common prefix");
}
return path.ToString();
}
private static int GetPathAttribute(string path)
{
DirectoryInfo di = new DirectoryInfo(path);
if (di.Exists)
{
return FILE_ATTRIBUTE_DIRECTORY;
}
FileInfo fi = new FileInfo(path);
if(fi.Exists)
{
return FILE_ATTRIBUTE_NORMAL;
}
throw new FileNotFoundException();
}
private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;
[DllImport("shlwapi.dll", SetLastError = true)]
private static extern int PathRelativePathTo(StringBuilder pszPath,
string pszFrom, int dwAttrFrom, string pszTo, int dwAttrTo);
В ASP.NET Core 2
, если Вы хотите относительный путь к bin\Debug\netcoreapp2.2
, можно использовать следующую комбинацию:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
public class RenderingService : IRenderingService
{
private readonly IHostingEnvironment _hostingEnvironment;
public RenderingService(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public string RelativeAssemblyDirectory()
{
var contentRootPath = _hostingEnvironment.ContentRootPath;
string executingAssemblyDirectoryAbsolutePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string executingAssemblyDirectoryRelativePath = System.IO.Path.GetRelativePath(contentRootPath, executingAssemblyDirectoryAbsolutePath);
return executingAssemblyDirectoryRelativePath;
}
}
Вы хотите использовать метод CommonPath
этого класса RelativePath
. Когда у вас есть общий путь, просто удалите его из пути, который вы хотите отобразить.
Namespace IO.Path
Public NotInheritable Class RelativePath
Private Declare Function PathRelativePathTo Lib "shlwapi" Alias "PathRelativePathToA" ( _
ByVal pszPath As String, _
ByVal pszFrom As String, _
ByVal dwAttrFrom As Integer, _
ByVal pszTo As String, _
ByVal dwAttrTo As Integer) As Integer
Private Declare Function PathCanonicalize Lib "shlwapi" Alias "PathCanonicalizeA" ( _
ByVal pszBuf As String, _
ByVal pszPath As String) As Integer
Private Const FILE_ATTRIBUTE_DIRECTORY As Short = &H10S
Private Const MAX_PATH As Short = 260
Private _path As String
Private _isDirectory As Boolean
#Region " Constructors "
Public Sub New()
End Sub
Public Sub New(ByVal path As String)
_path = path
End Sub
Public Sub New(ByVal path As String, ByVal isDirectory As Boolean)
_path = path
_isDirectory = isDirectory
End Sub
#End Region
Private Shared Function StripNulls(ByVal value As String) As String
StripNulls = value
If (InStr(value, vbNullChar) > 0) Then
StripNulls = Left(value, InStr(value, vbNullChar) - 1)
End If
End Function
Private Shared Function TrimCurrentDirectory(ByVal path As String) As String
TrimCurrentDirectory = path
If Len(path) >= 2 And Left(path, 2) = ".\" Then
TrimCurrentDirectory = Mid(path, 3)
End If
End Function
''' <summary>
''' 3. conforming to general principles: conforming to accepted principles or standard practice
''' </summary>
Public Shared Function Canonicalize(ByVal path As String) As String
Dim sPath As String
sPath = New String(Chr(0), MAX_PATH)
If PathCanonicalize(sPath, path) = 0 Then
Canonicalize = vbNullString
Else
Canonicalize = StripNulls(sPath)
End If
End Function
''' <summary>
''' Returns the most common path between two paths.
''' </summary>
''' <remarks>
''' <para>returns the path that is common between two paths</para>
''' <para>c:\FolderA\FolderB\FolderC</para>
''' c:\FolderA\FolderD\FolderE\File.Ext
'''
''' results in:
''' c:\FolderA\
''' </remarks>
Public Shared Function CommonPath(ByVal path1 As String, ByVal path2 As String) As String
'returns the path that is common between two paths
'
' c:\FolderA\FolderB\FolderC
' c:\FolderA\FolderD\FolderE\File.Ext
'
' results in:
' c:\FolderA\
Dim sResult As String = String.Empty
Dim iPos1, iPos2 As Integer
path1 = Canonicalize(path1)
path2 = Canonicalize(path2)
Do
If Left(path1, iPos1) = Left(path2, iPos2) Then
sResult = Left(path1, iPos1)
End If
iPos1 = InStr(iPos1 + 1, path1, "\")
iPos2 = InStr(iPos2 + 1, path1, "\")
Loop While Left(path1, iPos1) = Left(path2, iPos2)
Return sResult
End Function
Public Function CommonPath(ByVal path As String) As String
Return CommonPath(_path, path)
End Function
Public Shared Function RelativePathTo(ByVal source As String, ByVal isSourceDirectory As Boolean, ByVal target As String, ByVal isTargetDirectory As Boolean) As String
'DEVLIB
' 05/23/05 1:47PM - Fixed call to PathRelativePathTo, iTargetAttribute is now passed to dwAttrTo instead of IsTargetDirectory.
' For Visual Basic 6.0, the fix does not change testing results,
' because when the Boolean IsTargetDirectory is converted to the Long dwAttrTo it happens to contain FILE_ATTRIBUTE_DIRECTORY,
'
Dim sRelativePath As String
Dim iSourceAttribute, iTargetAttribute As Integer
sRelativePath = New String(Chr(0), MAX_PATH)
source = Canonicalize(source)
target = Canonicalize(target)
If isSourceDirectory Then
iSourceAttribute = FILE_ATTRIBUTE_DIRECTORY
End If
If isTargetDirectory Then
iTargetAttribute = FILE_ATTRIBUTE_DIRECTORY
End If
If PathRelativePathTo(sRelativePath, source, iSourceAttribute, target, iTargetAttribute) = 0 Then
RelativePathTo = vbNullString
Else
RelativePathTo = TrimCurrentDirectory(StripNulls(sRelativePath))
End If
End Function
Public Function RelativePath(ByVal target As String) As String
Return RelativePathTo(_path, _isDirectory, target, False)
End Function
End Class
End Namespace