[Peek]Support for special folders like Recycle Bin and My PC (#33310)

* Peek support for special folders

* Renamed ThisComputer->ThisPC

* Used different variable name to avoid spellcheck issues

* Made label of empty fields hidden

* Removed ThisPC special handling and last modified date of recycle bin
This commit is contained in:
Ani 2024-06-14 16:40:25 +02:00 committed by GitHub
parent fc32fe29ba
commit bc0811e6a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 599 additions and 54 deletions

View File

@ -85,7 +85,7 @@
<UsePrecompiledHeaders Condition="'$(TF_BUILD)' != ''">false</UsePrecompiledHeaders>
<!-- Change this to bust the cache -->
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202310210737</MSBuildCacheCacheUniverse>
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202406130737</MSBuildCacheCacheUniverse>
<!--
Visual Studio telemetry reads various ApplicationInsights.config files and other files after the project is finished, likely in a detached process.

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Peek.Common.Helpers;
public static class PathHelper
{
public static bool IsUncPath(string path) => Uri.TryCreate(path, UriKind.Absolute, out Uri? uri) && uri != null && uri.IsUnc;
}

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
namespace Peek.Common.Helpers;
public static class ThreadHelper
{
public static void RunOnSTAThread(ThreadStart action)
{
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
action();
}
else
{
Thread staThread = new(action);
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();
}
}
}

View File

@ -24,6 +24,8 @@ namespace Peek.Common.Models
public string Name { get; init; }
public string ParsingName => string.Empty;
public string Path { get; init; }
public string Extension => System.IO.Path.GetExtension(Path).ToLower(CultureInfo.InvariantCulture);

View File

@ -11,19 +11,15 @@ using Windows.Storage;
namespace Peek.Common.Models
{
public class FolderItem : IFileSystemItem
public class FolderItem(string path, string name, string parsingName) : IFileSystemItem
{
private StorageFolder? storageFolder;
public FolderItem(string path, string name)
{
Path = path;
Name = name;
}
public string Name { get; init; } = name;
public string Name { get; init; }
public string ParsingName { get; init; } = parsingName;
public string Path { get; init; }
public string Path { get; init; } = path;
public string Extension => string.Empty;
@ -38,7 +34,7 @@ namespace Peek.Common.Models
{
try
{
storageFolder = await StorageFolder.GetFolderFromPathAsync(Path);
storageFolder = string.IsNullOrEmpty(Path) ? null : await StorageFolder.GetFolderFromPathAsync(Path);
}
catch (Exception ex)
{

View File

@ -17,25 +17,24 @@ namespace Peek.Common.Models
{
get
{
DateTime? dateModified;
try
{
dateModified = System.IO.File.GetCreationTime(Path);
return string.IsNullOrEmpty(Path) ? null : System.IO.File.GetCreationTime(Path);
}
catch
{
dateModified = null;
return null;
}
return dateModified;
}
}
public string Extension { get; }
public string Name { get; init; }
public string Name { get; }
public string Path { get; init; }
public string ParsingName { get; }
public string Path { get; }
public ulong FileSizeBytes => PropertyStoreHelper.TryGetUlongProperty(Path, PropertyKey.FileSizeBytes) ?? 0;

View File

@ -0,0 +1,57 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.SpecialFolderInformationalPreviewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid ColumnSpacing="24">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Icon" Width="Auto" />
<ColumnDefinition x:Name="FileInfo" Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="FileIcon"
Grid.Column="0"
Width="180"
Height="180"
Source="{x:Bind Source.IconPreview, Mode=OneWay}" />
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
Spacing="4">
<TextBlock
MaxLines="3"
Style="{StaticResource TitleTextBlockStyle}"
Text="{x:Bind Source.FileName, Mode=OneWay}"
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind Source.FileName, Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatFileType(Source.FileType), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatFileType(Source.FileType), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatFileSize(Source.FileSize), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatFileSize(Source.FileSize), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{x:Bind FormatFileDateModified(Source.DateModified), Mode=OneWay}">
<ToolTipService.ToolTip>
<ToolTip Content="{x:Bind FormatFileDateModified(Source.DateModified), Mode=OneWay}" />
</ToolTipService.ToolTip>
</TextBlock>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Peek.Common.Helpers;
using Peek.FilePreviewer.Models;
namespace Peek.FilePreviewer.Controls;
public sealed partial class SpecialFolderInformationalPreviewControl : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(SpecialFolderPreviewData),
typeof(SpecialFolderInformationalPreviewControl),
new PropertyMetadata(null));
public SpecialFolderPreviewData? Source
{
get { return (SpecialFolderPreviewData)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public SpecialFolderInformationalPreviewControl()
{
InitializeComponent();
}
public string FormatFileType(string? fileType) => FormatField("UnsupportedFile_FileType", fileType);
public string FormatFileSize(string? fileSize) => FormatField("UnsupportedFile_FileSize", fileSize);
public string FormatFileDateModified(string? fileDateModified) => FormatField("UnsupportedFile_DateModified", fileDateModified);
private static string FormatField(string resourceId, string? fieldValue)
{
return string.IsNullOrWhiteSpace(fieldValue) ? string.Empty : ReadableStringHelper.FormatResourceString(resourceId, fieldValue);
}
}

View File

@ -0,0 +1,25 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<UserControl
x:Class="Peek.FilePreviewer.Controls.SpecialFolderPreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Peek.FilePreviewer.Controls"
xmlns:conv="using:Peek.Common.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prev="using:Peek.FilePreviewer.Previewers"
mc:Ignorable="d">
<Grid
Margin="24"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<controls:SpecialFolderInformationalPreviewControl
x:Name="InformationPreview"
Source="{x:Bind Source, Mode=OneWay}"
Visibility="{x:Bind IsVisibleIfStatesMatch(LoadingState, prev:PreviewState.Loaded), Mode=OneWay}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Peek.Common.Converters;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers;
namespace Peek.FilePreviewer.Controls;
[INotifyPropertyChanged]
public sealed partial class SpecialFolderPreview : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(SpecialFolderPreviewData),
typeof(SpecialFolderPreview),
new PropertyMetadata(null));
public static readonly DependencyProperty LoadingStateProperty = DependencyProperty.Register(
nameof(LoadingState),
typeof(PreviewState),
typeof(SpecialFolderPreview),
new PropertyMetadata(PreviewState.Uninitialized));
public SpecialFolderPreviewData? Source
{
get { return (SpecialFolderPreviewData)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public PreviewState? LoadingState
{
get { return (PreviewState)GetValue(LoadingStateProperty); }
set { SetValue(LoadingStateProperty, value); }
}
public SpecialFolderPreview()
{
InitializeComponent();
}
public Visibility IsVisibleIfStatesMatch(PreviewState? a, PreviewState? b) => VisibilityConverter.Convert(a == b);
}

View File

@ -83,6 +83,12 @@
Source="{x:Bind DrivePreviewer.Preview, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(DrivePreviewer, Previewer.State), Mode=OneWay}" />
<controls:SpecialFolderPreview
x:Name="SpecialFolderPreview"
LoadingState="{x:Bind SpecialFolderPreviewer.State, Mode=OneWay}"
Source="{x:Bind SpecialFolderPreviewer.Preview, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(SpecialFolderPreviewer, Previewer.State), Mode=OneWay}" />
<controls:UnsupportedFilePreview
x:Name="UnsupportedFilePreview"
LoadingState="{x:Bind UnsupportedFilePreviewer.State, Mode=OneWay}"

View File

@ -53,6 +53,7 @@ namespace Peek.FilePreviewer
[NotifyPropertyChangedFor(nameof(ArchivePreviewer))]
[NotifyPropertyChangedFor(nameof(ShellPreviewHandlerPreviewer))]
[NotifyPropertyChangedFor(nameof(DrivePreviewer))]
[NotifyPropertyChangedFor(nameof(SpecialFolderPreviewer))]
[NotifyPropertyChangedFor(nameof(UnsupportedFilePreviewer))]
private IPreviewer? previewer;
@ -105,6 +106,8 @@ namespace Peek.FilePreviewer
public IDrivePreviewer? DrivePreviewer => Previewer as IDrivePreviewer;
public ISpecialFolderPreviewer? SpecialFolderPreviewer => Previewer as ISpecialFolderPreviewer;
public IUnsupportedFilePreviewer? UnsupportedFilePreviewer => Previewer as IUnsupportedFilePreviewer;
public IFileSystemItem Item

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml.Media;
namespace Peek.FilePreviewer.Models;
public partial class SpecialFolderPreviewData : ObservableObject
{
[ObservableProperty]
private ImageSource? iconPreview;
[ObservableProperty]
private string? fileName;
[ObservableProperty]
private string? fileType;
[ObservableProperty]
private string? fileSize;
[ObservableProperty]
private string? dateModified;
}

View File

@ -21,6 +21,8 @@
<None Remove="Controls\BrowserControl.xaml" />
<None Remove="Controls\DriveControl.xaml" />
<None Remove="Controls\ShellPreviewHandlerControl.xaml" />
<None Remove="Controls\SpecialFolderPreview\SpecialFolderInformationalPreviewControl.xaml" />
<None Remove="Controls\SpecialFolderPreview\SpecialFolderPreview.xaml" />
<None Remove="Controls\UnsupportedFilePreview\FailedFallbackPreviewControl.xaml" />
<None Remove="Controls\UnsupportedFilePreview\InformationalPreviewControl.xaml" />
<None Remove="FilePreview.xaml" />
@ -42,6 +44,27 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<COMReference Include="Shell32">
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
<COMReference Include="SHDocVw">
<VersionMinor>1</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>eab22ac0-30c1-11cf-a7eb-0000c05bae0b</Guid>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\FilePreviewCommon\FilePreviewCommon.csproj" />
@ -71,6 +94,9 @@
<Page Update="Controls\ArchiveControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Controls\SpecialFolderPreview\SpecialFolderInformationalPreviewControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Peek.FilePreviewer.Models;
namespace Peek.FilePreviewer.Previewers;
public interface ISpecialFolderPreviewer : IPreviewer
{
public SpecialFolderPreviewData? Preview { get; }
}

View File

@ -53,6 +53,10 @@ namespace Peek.FilePreviewer.Previewers
{
return new DrivePreviewer(item);
}
else if (SpecialFolderPreviewer.IsItemSupported(item))
{
return new SpecialFolderPreviewer(item);
}
// Other previewer types check their supported file types here
return CreateDefaultPreviewer(item);

View File

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using Peek.Common;
using Peek.Common.Models;
namespace Peek.FilePreviewer.Previewers;
public enum KnownSpecialFolder
{
None,
RecycleBin,
}
public static class KnownSpecialFolders
{
private static readonly Lazy<IReadOnlyDictionary<string, KnownSpecialFolder>> FoldersByParsingNameDict = new(GetFoldersByParsingName);
public static IReadOnlyDictionary<string, KnownSpecialFolder> FoldersByParsingName => FoldersByParsingNameDict.Value;
private static Dictionary<string, KnownSpecialFolder> GetFoldersByParsingName()
{
var folders = new (KnownSpecialFolder Folder, string? ParsingName)[]
{
(KnownSpecialFolder.RecycleBin, GetParsingName("shell:RecycleBinFolder")),
};
return folders.Where(folder => !string.IsNullOrEmpty(folder.ParsingName))
.ToDictionary(folder => folder.ParsingName!, folder => folder.Folder);
}
private static string? GetParsingName(string shellName)
{
try
{
return CreateShellItemFromShellName(shellName)?.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING);
}
catch (Exception)
{
return null;
}
}
private static IShellItem? CreateShellItemFromShellName(string shellName)
{
// Based on https://stackoverflow.com/a/42966899
const string ShellItem = "43826d1e-e718-42ee-bc55-a1e261c37bfe";
Guid shellItem2Guid = new(ShellItem);
int retCode = NativeMethods.SHCreateItemFromParsingName(shellName, IntPtr.Zero, ref shellItem2Guid, out IShellItem? nativeShellItem);
return retCode == 0 ? nativeShellItem : null;
}
}

View File

@ -0,0 +1,215 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using ManagedCommon;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using Peek.Common.Extensions;
using Peek.Common.Helpers;
using Peek.Common.Models;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers.Helpers;
using Windows.Foundation;
namespace Peek.FilePreviewer.Previewers;
public partial class SpecialFolderPreviewer : ObservableObject, ISpecialFolderPreviewer, IDisposable
{
private readonly DispatcherTimer _syncDetailsDispatcherTimer = new();
private ulong _folderSize;
private DateTime? _dateModified;
[ObservableProperty]
private SpecialFolderPreviewData preview = new();
[ObservableProperty]
private PreviewState state;
public SpecialFolderPreviewer(IFileSystemItem file)
{
_syncDetailsDispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
_syncDetailsDispatcherTimer.Tick += DetailsDispatcherTimer_Tick;
Item = file;
Preview.FileName = file.Name;
Dispatcher = DispatcherQueue.GetForCurrentThread();
}
public static bool IsItemSupported(IFileSystemItem item)
{
// Always allow know special folders.
bool isKnownSpecialFolder = KnownSpecialFolders.FoldersByParsingName.ContainsKey(item.ParsingName);
// Allow empty paths unless Unc; icons don't load correctly for Unc paths.
bool isEmptyNonUncPath = string.IsNullOrEmpty(item.Path) && !PathHelper.IsUncPath(item.ParsingName);
return isKnownSpecialFolder || isEmptyNonUncPath;
}
private IFileSystemItem Item { get; }
private DispatcherQueue Dispatcher { get; }
public void Dispose()
{
_syncDetailsDispatcherTimer.Tick -= DetailsDispatcherTimer_Tick;
GC.SuppressFinalize(this);
}
public Task<PreviewSize> GetPreviewSizeAsync(CancellationToken cancellationToken)
{
Size? size = new(680, 500);
var previewSize = new PreviewSize { MonitorSize = size, UseEffectivePixels = true };
return Task.FromResult(previewSize);
}
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
State = PreviewState.Loading;
var tasks = await Task.WhenAll(LoadIconPreviewAsync(cancellationToken), LoadDisplayInfoAsync(cancellationToken));
State = tasks.All(task => task) ? PreviewState.Loaded : PreviewState.Error;
}
public async Task CopyAsync()
{
await Dispatcher.RunOnUiThread(async () =>
{
var storageItem = await Item.GetStorageItemAsync();
ClipboardHelper.SaveToClipboard(storageItem);
});
}
public async Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
{
bool isIconValid = false;
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
await Dispatcher.RunOnUiThread(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
var iconBitmap = await IconHelper.GetIconAsync(Item.ParsingName, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
isIconValid = iconBitmap != null;
Preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg"));
});
});
return isIconValid && isTaskSuccessful;
}
public async Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
{
bool isDisplayValid = false;
var isTaskSuccessful = await TaskExtension.RunSafe(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
var fileType = await Task.Run(Item.GetContentTypeAsync);
cancellationToken.ThrowIfCancellationRequested();
isDisplayValid = fileType != null;
await Dispatcher.RunOnUiThread(() =>
{
Preview.FileType = fileType;
return Task.CompletedTask;
});
RunUpdateDetailsWorkflow(cancellationToken);
});
return isDisplayValid && isTaskSuccessful;
}
private void RunUpdateDetailsWorkflow(CancellationToken cancellationToken)
{
Task.Run(
async () =>
{
try
{
await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Start);
ComputeDetails(cancellationToken);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
Logger.LogError("Failed to update special folder details", ex);
}
finally
{
await Dispatcher.RunOnUiThread(_syncDetailsDispatcherTimer.Stop);
}
await Dispatcher.RunOnUiThread(SyncDetails);
},
cancellationToken);
}
private void ComputeDetails(CancellationToken cancellationToken)
{
_dateModified = Item.DateModified;
switch (KnownSpecialFolders.FoldersByParsingName.GetValueOrDefault(Item.ParsingName, KnownSpecialFolder.None))
{
case KnownSpecialFolder.None:
break;
case KnownSpecialFolder.RecycleBin:
ThreadHelper.RunOnSTAThread(() => { ComputeRecycleBinDetails(cancellationToken); });
cancellationToken.ThrowIfCancellationRequested();
break;
}
}
private void ComputeRecycleBinDetails(CancellationToken cancellationToken)
{
var shell = new Shell32.Shell();
var recycleBin = shell.NameSpace(10); // CSIDL_BITBUCKET
foreach (dynamic item in recycleBin.Items())
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
_folderSize += Convert.ToUInt64(item.Size);
}
}
private void SyncDetails()
{
Preview.FileSize = _folderSize == 0 ? string.Empty : ReadableStringHelper.BytesToReadableString(_folderSize);
Preview.DateModified = _dateModified?.ToString(CultureInfo.CurrentCulture) ?? string.Empty;
}
private void DetailsDispatcherTimer_Tick(object? sender, object e)
{
SyncDetails();
}
}

View File

@ -45,8 +45,6 @@ namespace Peek.FilePreviewer.Previewers
Dispatcher = DispatcherQueue.GetForCurrentThread();
}
public bool IsPreviewLoaded => Preview.IconPreview != null;
private IFileSystemItem Item { get; }
private DispatcherQueue Dispatcher { get; }

View File

@ -7,49 +7,41 @@ using System.IO;
using ManagedCommon;
using Peek.Common.Models;
namespace Peek.UI.Extensions
namespace Peek.UI.Extensions;
public static class IShellItemExtensions
{
public static class IShellItemExtensions
public static IFileSystemItem ToIFileSystemItem(this IShellItem shellItem)
{
public static IFileSystemItem ToIFileSystemItem(this IShellItem shellItem)
{
string path = shellItem.GetPath();
string name = shellItem.GetName();
string path = shellItem.GetPath();
string name = shellItem.GetName();
return File.Exists(path) ? new FileItem(path, name) : new FolderItem(path, name);
return File.Exists(path) ? new FileItem(path, name) : new FolderItem(path, name, shellItem.GetParsingName());
}
private static string GetPath(this IShellItem shellItem) =>
shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_FILESYSPATH, logError: false);
private static string GetName(this IShellItem shellItem) =>
shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_NORMALDISPLAY, logError: true);
private static string GetParsingName(this IShellItem shellItem) =>
shellItem.GetNameCore(Windows.Win32.UI.Shell.SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, logError: true);
private static string GetNameCore(this IShellItem shellItem, Windows.Win32.UI.Shell.SIGDN displayNameType, bool logError)
{
try
{
return shellItem.GetDisplayName(displayNameType);
}
private static string GetPath(this IShellItem shellItem)
catch (Exception ex)
{
string path = string.Empty;
try
if (logError)
{
path = shellItem.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_FILESYSPATH);
}
catch (Exception ex)
{
// TODO: Handle cases that do not have a file system path like Recycle Bin.
path = string.Empty;
Logger.LogError("Getting path failed. " + ex.Message);
Logger.LogError($"Getting {Enum.GetName(displayNameType)} failed. {ex.Message}");
}
return path;
}
private static string GetName(this IShellItem shellItem)
{
string name = string.Empty;
try
{
name = shellItem.GetDisplayName(Windows.Win32.UI.Shell.SIGDN.SIGDN_NORMALDISPLAY);
}
catch (Exception ex)
{
name = string.Empty;
Logger.LogError("Getting path failed. " + ex.Message);
}
return name;
return string.Empty;
}
}
}