There\'s a part in my apps that displays the file path loaded by the user through OpenFileDialog. It\'s taking up too much space to display the whole path, but I don\'t want
.NET Core 2.0 has Path.GetRelativePath which can be used like so:
var relativePath = Path.GetRelativePath(
@"C:\Program Files\Dummy Folder\MyProgram",
@"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");
In the above example, the relativePath variable is equal to Data\datafile1.dat.
@Dave's solution does not work when the file paths do not end with a forward slash character (/) which can happen if the path is a directory path. My solution fixes that problem and also makes use of the Uri.UriSchemeFile constant instead of hard coding "FILE".
///
/// Creates a relative path from one file or folder to another.
///
/// Contains the directory that defines the start of the relative path.
/// Contains the path that defines the endpoint of the relative path.
/// The relative path from the start directory to the end path.
/// or is null .
///
///
public static string GetRelativePath(string fromPath, string toPath)
{
if (string.IsNullOrEmpty(fromPath))
{
throw new ArgumentNullException("fromPath");
}
if (string.IsNullOrEmpty(toPath))
{
throw new ArgumentNullException("toPath");
}
Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath));
Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath));
if (fromUri.Scheme != toUri.Scheme)
{
return toPath;
}
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
}
private static string AppendDirectorySeparatorChar(string path)
{
// Append a slash only if the path is a directory and does not have a slash.
if (!Path.HasExtension(path) &&
!path.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
return path + Path.DirectorySeparatorChar;
}
return path;
}
There is a Windows API called PathRelativePathToA that can be used to find a relative path. Please note that the file or directory paths that you pass to the function must exist for it to work.
var relativePath = PathExtended.GetRelativePath(
@"C:\Program Files\Dummy Folder\MyProgram",
@"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");
public static class PathExtended
{
private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;
private const int MaximumPath = 260;
public static string GetRelativePath(string fromPath, string toPath)
{
var fromAttribute = GetPathAttribute(fromPath);
var toAttribute = GetPathAttribute(toPath);
var stringBuilder = new StringBuilder(MaximumPath);
if (PathRelativePathTo(
stringBuilder,
fromPath,
fromAttribute,
toPath,
toAttribute) == 0)
{
throw new ArgumentException("Paths must have a common prefix.");
}
return stringBuilder.ToString();
}
private static int GetPathAttribute(string path)
{
var directory = new DirectoryInfo(path);
if (directory.Exists)
{
return FILE_ATTRIBUTE_DIRECTORY;
}
var file = new FileInfo(path);
if (file.Exists)
{
return FILE_ATTRIBUTE_NORMAL;
}
throw new FileNotFoundException(
"A file or directory with the specified path was not found.",
path);
}
[DllImport("shlwapi.dll", SetLastError = true)]
private static extern int PathRelativePathTo(
StringBuilder pszPath,
string pszFrom,
int dwAttrFrom,
string pszTo,
int dwAttrTo);
}