diff --git a/Files/ViewModels/PreviewPaneViewModel.cs b/Files/ViewModels/PreviewPaneViewModel.cs index 619d5abb3246..798d716fffbb 100644 --- a/Files/ViewModels/PreviewPaneViewModel.cs +++ b/Files/ViewModels/PreviewPaneViewModel.cs @@ -210,31 +210,23 @@ private async Task LoadPreviewControlAsync(CancellationToken token) private async Task LoadPreviewControlFromExtension(ListedItem item, Extension extension) { - try - { - UIElement control = null; - var file = await StorageFile.GetFileFromPathAsync(item.ItemPath); - string sharingToken = SharedStorageAccessManager.AddFile(file); - var result = await extension.Invoke(new ValueSet() { { "token", sharingToken } }); - - if (result.TryGetValue("preview", out object preview)) - { - control = XamlReader.Load(preview as string) as UIElement; - } - - if (result.TryGetValue("details", out object details)) - { - var detailsList = JsonConvert.DeserializeObject>(details as string); - await BasePreviewModel.LoadDetailsOnly(item, detailsList); - } + UIElement control = null; + var file = await StorageFile.GetFileFromPathAsync(item.ItemPath); + string sharingToken = SharedStorageAccessManager.AddFile(file); + var result = await extension.Invoke(new ValueSet() { { "token", sharingToken } }); - return control; + if (result.TryGetValue("preview", out object preview)) + { + control = XamlReader.Load(preview as string) as UIElement; } - catch (Exception e) + + if (result.TryGetValue("details", out object details)) { - Debug.WriteLine(e.ToString()); + var detailsList = JsonConvert.DeserializeObject>(details as string); + await BasePreviewModel.LoadDetailsOnly(item, detailsList); } - return null; + + return control; } private async void SelectedItemChanged() @@ -242,9 +234,33 @@ private async void SelectedItemChanged() loadCancellationTokenSource?.Cancel(); if (SelectedItem != null && SelectedItems.Count == 1) { - DetailsListVisibility = Visibility.Visible; - loadCancellationTokenSource = new CancellationTokenSource(); - await LoadPreviewControlAsync(loadCancellationTokenSource.Token); + try + { + DetailsListVisibility = Visibility.Visible; + loadCancellationTokenSource = new CancellationTokenSource(); + await LoadPreviewControlAsync(loadCancellationTokenSource.Token); + } + catch (Exception e) + { + Debug.WriteLine(e); + loadCancellationTokenSource?.Cancel(); + // If initial loading fails, attempt to load a basic preivew (thumbnail and details only) + // If that fails, revert to no preview/details available + try + { + var basicModel = new BasicPreviewViewModel(SelectedItem); + await basicModel.LoadAsync(); + PreviewPaneContent = new BasicPreview(basicModel); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + PreviewPaneContent = null; + DetailsListVisibility = Visibility.Collapsed; + DetailsErrorText = "PreviewPaneDetailsNotAvailableText".GetLocalized(); + PreviewErrorText = "DetailsPanePreviewNotAvaliableText".GetLocalized(); + } + } } else if (SelectedItem != null) { diff --git a/Files/ViewModels/Previews/BasePreviewModel.cs b/Files/ViewModels/Previews/BasePreviewModel.cs index 01022a5a57b1..3c80201f24c0 100644 --- a/Files/ViewModels/Previews/BasePreviewModel.cs +++ b/Files/ViewModels/Previews/BasePreviewModel.cs @@ -4,7 +4,6 @@ using Microsoft.Toolkit.Mvvm.ComponentModel; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,42 +20,32 @@ public BasePreviewModel(ListedItem item) : base() Item = item; } - public delegate void LoadedEventHandler(object sender, EventArgs e); - - public event LoadedEventHandler LoadedEvent; + public ListedItem Item { get; internal set; } public List DetailsFromPreview { get; set; } - public ListedItem Item { get; internal set; } - public CancellationTokenSource LoadCancelledTokenSource { get; } = new CancellationTokenSource(); - public static async Task LoadDetailsOnly(ListedItem item, List details = null) - { - var temp = new DetailsOnlyPreviewModel(item) { DetailsFromPreview = details }; - await temp.LoadAsync(); - } + /// + /// This is cancelled when the user has selected another file or closed the pane. + /// + public CancellationTokenSource LoadCancelledTokenSource { get; } = new CancellationTokenSource(); - public virtual async Task LoadAsync() + /// + /// Override this if the preview control needs to handle the unloaded event. + /// + /// + /// + public virtual void PreviewControlBase_Unloaded(object sender, RoutedEventArgs e) { - // Files can be corrupt, in use, and stuff - try - { - var detailsFull = new List(); - Item.ItemFile ??= await StorageFile.GetFileFromPathAsync(Item.ItemPath); - DetailsFromPreview = await LoadPreviewAndDetails(); - RaiseLoadedEvent(); - var props = await GetSystemFileProperties(); - - DetailsFromPreview?.ForEach(i => detailsFull.Add(i)); - props?.ForEach(i => detailsFull.Add(i)); - - Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection(detailsFull); - } - catch (Exception e) - { - Debug.WriteLine(e); - } + LoadCancelledTokenSource.Cancel(); } + /// + /// Override this and place the code to load the file preview here. + /// You can return details that may have been obtained while loading the preview (eg. word count). + /// This details will be displayed *before* the system file properties. + /// If there are none, return an empty list. + /// + /// A list of details public async virtual Task> LoadPreviewAndDetails() { var (IconData, OverlayData, IsCustom) = await FileThumbnailHelper.LoadIconOverlayAsync(Item.ItemPath, 400); @@ -75,17 +64,6 @@ public async virtual Task> LoadPreviewAndDetails() return new List(); } - public virtual void PreviewControlBase_Unloaded(object sender, RoutedEventArgs e) - { - LoadCancelledTokenSource.Cancel(); - } - - protected virtual void RaiseLoadedEvent() - { - // Raise the event in a thread-safe manner using the ?. operator. - LoadedEvent?.Invoke(this, new EventArgs()); - } - private async Task> GetSystemFileProperties() { if (Item.IsShortcutItem) @@ -100,6 +78,42 @@ private async Task> GetSystemFileProperties() return list.Where(i => i.Value != null).ToList(); } + /// + /// Call this function when you are ready to load the preview and details. + /// Override if you need custom loading code. + /// + /// The task to run + public virtual async Task LoadAsync() + { + var detailsFull = new List(); + Item.ItemFile ??= await StorageFile.GetFileFromPathAsync(Item.ItemPath); + DetailsFromPreview = await LoadPreviewAndDetails(); + RaiseLoadedEvent(); + var props = await GetSystemFileProperties(); + + // Add the details from the preview function, then the system file properties + DetailsFromPreview?.ForEach(i => detailsFull.Add(i)); + props?.ForEach(i => detailsFull.Add(i)); + + Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection(detailsFull); + } + + public event LoadedEventHandler LoadedEvent; + + public delegate void LoadedEventHandler(object sender, EventArgs e); + + protected virtual void RaiseLoadedEvent() + { + // Raise the event in a thread-safe manner using the ?. operator. + LoadedEvent?.Invoke(this, new EventArgs()); + } + + public static async Task LoadDetailsOnly(ListedItem item, List details = null) + { + var temp = new DetailsOnlyPreviewModel(item) { DetailsFromPreview = details }; + await temp.LoadAsync(); + } + internal class DetailsOnlyPreviewModel : BasePreviewModel { public DetailsOnlyPreviewModel(ListedItem item) : base(item) diff --git a/Files/ViewModels/Previews/FolderPreviewViewModel.cs b/Files/ViewModels/Previews/FolderPreviewViewModel.cs index c90528c0a666..5bf6840ded10 100644 --- a/Files/ViewModels/Previews/FolderPreviewViewModel.cs +++ b/Files/ViewModels/Previews/FolderPreviewViewModel.cs @@ -22,13 +22,7 @@ public FolderPreviewViewModel(ListedItem item) public async Task LoadAsync() { - try - { - await LoadPreviewAndDetailsAsync(); - } - catch (Exception) - { - } + await LoadPreviewAndDetailsAsync(); } private async Task LoadPreviewAndDetailsAsync() diff --git a/Files/ViewModels/Previews/HtmlPreviewViewModel.cs b/Files/ViewModels/Previews/HtmlPreviewViewModel.cs index b50eb13a8b7e..24465fcbc530 100644 --- a/Files/ViewModels/Previews/HtmlPreviewViewModel.cs +++ b/Files/ViewModels/Previews/HtmlPreviewViewModel.cs @@ -30,14 +30,7 @@ public string TextValue public async override Task> LoadPreviewAndDetails() { - try - { - TextValue = await FileIO.ReadTextAsync(Item.ItemFile); - } - catch (Exception e) - { - Debug.WriteLine(e); - } + TextValue = await FileIO.ReadTextAsync(Item.ItemFile); return new List(); } } diff --git a/Files/ViewModels/Previews/ImagePreviewViewModel.cs b/Files/ViewModels/Previews/ImagePreviewViewModel.cs index c5273b5db435..a2cf9ff60067 100644 --- a/Files/ViewModels/Previews/ImagePreviewViewModel.cs +++ b/Files/ViewModels/Previews/ImagePreviewViewModel.cs @@ -31,27 +31,20 @@ public ImageSource ImageSource public override async Task> LoadPreviewAndDetails() { - try - { - FileRandomAccessStream stream = (FileRandomAccessStream)await Item.ItemFile.OpenAsync(FileAccessMode.Read); + FileRandomAccessStream stream = (FileRandomAccessStream)await Item.ItemFile.OpenAsync(FileAccessMode.Read); - // svg files require a different type of source - if (!Item.ItemPath.EndsWith(".svg")) - { - var bitmap = new BitmapImage(); - ImageSource = bitmap; - await bitmap.SetSourceAsync(stream); - } - else - { - var bitmap = new SvgImageSource(); - ImageSource = bitmap; - await bitmap.SetSourceAsync(stream); - } + // svg files require a different type of source + if (!Item.ItemPath.EndsWith(".svg")) + { + var bitmap = new BitmapImage(); + ImageSource = bitmap; + await bitmap.SetSourceAsync(stream); } - catch (Exception e) + else { - Debug.WriteLine(e); + var bitmap = new SvgImageSource(); + ImageSource = bitmap; + await bitmap.SetSourceAsync(stream); } return new List(); diff --git a/Files/ViewModels/Previews/MarkdownPreviewViewModel.cs b/Files/ViewModels/Previews/MarkdownPreviewViewModel.cs index 2acaa31d47ec..d60c7dcef503 100644 --- a/Files/ViewModels/Previews/MarkdownPreviewViewModel.cs +++ b/Files/ViewModels/Previews/MarkdownPreviewViewModel.cs @@ -28,16 +28,9 @@ public string TextValue public override async Task> LoadPreviewAndDetails() { - try - { - var text = await FileIO.ReadTextAsync(Item.ItemFile); - var displayText = text.Length < Constants.PreviewPane.TextCharacterLimit ? text : text.Remove(Constants.PreviewPane.TextCharacterLimit); - TextValue = displayText; - } - catch (Exception e) - { - Debug.WriteLine(e); - } + var text = await FileIO.ReadTextAsync(Item.ItemFile); + var displayText = text.Length < Constants.PreviewPane.TextCharacterLimit ? text : text.Remove(Constants.PreviewPane.TextCharacterLimit); + TextValue = displayText; return new List(); } diff --git a/Files/ViewModels/Previews/PDFPreviewViewModel.cs b/Files/ViewModels/Previews/PDFPreviewViewModel.cs index c0c83d3ffc8a..eda08b86c385 100644 --- a/Files/ViewModels/Previews/PDFPreviewViewModel.cs +++ b/Files/ViewModels/Previews/PDFPreviewViewModel.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Threading.Tasks; using Windows.Data.Pdf; using Windows.Storage.Streams; @@ -35,20 +36,34 @@ public Visibility LoadingBarVisibility public async override Task> LoadPreviewAndDetails() { var pdf = await PdfDocument.LoadFromFileAsync(Item.ItemFile); - var details = new List(); - - LoadPagesAsync(pdf); - // Add the number of pages to the details - details.Add(new FileProperty() + TryLoadPagesAsync(pdf); + var details = new List { - NameResource = "PropertyPageCount", - Value = pdf.PageCount, - }); + + // Add the number of pages to the details + new FileProperty() + { + NameResource = "PropertyPageCount", + Value = pdf.PageCount, + } + }; return details; } - private async void LoadPagesAsync(PdfDocument pdf) + public async void TryLoadPagesAsync(PdfDocument pdf) + { + try + { + await LoadPagesAsync(pdf); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + private async Task LoadPagesAsync(PdfDocument pdf) { // This fixes an issue where loading an absurdly large PDF would take to much RAM // and eventually cause a crash diff --git a/Files/ViewModels/Previews/RichTextPreviewViewModel.cs b/Files/ViewModels/Previews/RichTextPreviewViewModel.cs index 29a95fe3524e..375432ec9085 100644 --- a/Files/ViewModels/Previews/RichTextPreviewViewModel.cs +++ b/Files/ViewModels/Previews/RichTextPreviewViewModel.cs @@ -22,15 +22,7 @@ public RichTextPreviewViewModel(ListedItem item) : base(item) public async override Task> LoadPreviewAndDetails() { - try - { - Stream = await Item.ItemFile.OpenReadAsync(); - } - catch (Exception e) - { - Debug.WriteLine(e); - } - + Stream = await Item.ItemFile.OpenReadAsync(); return new List(); } } diff --git a/Files/ViewModels/Previews/ShortcutPreviewViewModel.cs b/Files/ViewModels/Previews/ShortcutPreviewViewModel.cs index 8ea2693851db..d5dd4e833893 100644 --- a/Files/ViewModels/Previews/ShortcutPreviewViewModel.cs +++ b/Files/ViewModels/Previews/ShortcutPreviewViewModel.cs @@ -13,19 +13,6 @@ public ShortcutPreviewViewModel(ListedItem item) : base(item) { } - public override async Task LoadAsync() - { - try - { - var details = await LoadPreviewAndDetails(); - Item.FileDetails?.Clear(); - Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection(details.Where(i => i.Value != null)); - } - catch (Exception) - { - } - } - public async override Task> LoadPreviewAndDetails() { var item = Item as ShortcutItem; @@ -62,5 +49,12 @@ public async override Task> LoadPreviewAndDetails() return details; } + + public override async Task LoadAsync() + { + var details = await LoadPreviewAndDetails(); + Item.FileDetails?.Clear(); + Item.FileDetails = new System.Collections.ObjectModel.ObservableCollection(details.Where(i => i.Value != null)); + } } } \ No newline at end of file