diff --git a/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml b/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml
index cfecda8b8..b1ba464ba 100644
--- a/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml
+++ b/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml
@@ -14,7 +14,7 @@
-
+
diff --git a/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml.cs b/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml.cs
index 54ac420df..7dc8f9fad 100644
--- a/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml.cs
+++ b/src/Samples/Toolkit.SampleApp.WPF/Samples/PopupViewer/PopupViewerSample.xaml.cs
@@ -124,5 +124,35 @@ private void PopupBackground_MouseDown(object sender, System.Windows.Input.Mouse
PopupBackground.Visibility = Visibility.Collapsed;
popupViewer.Popup = null;
}
+
+ private async void popupViewer_PopupAttachmentClicked(object sender, UI.Controls.PopupAttachmentClickedEventArgs e)
+ {
+ if (!e.Attachment.IsLocal) // Attachment hasn't been downloaded
+ {
+ try
+ {
+ // Make first click just load the attachment (or cancel a loading operation). Otherwise fallback to default behavior
+ if (e.Attachment.LoadStatus == LoadStatus.NotLoaded)
+ {
+ e.Handled = true;
+ await e.Attachment.LoadAsync();
+ }
+ else if (e.Attachment.LoadStatus == LoadStatus.FailedToLoad)
+ {
+ e.Handled = true;
+ await e.Attachment.RetryLoadAsync();
+ }
+ else if (e.Attachment.LoadStatus == LoadStatus.Loading)
+ {
+ e.Handled = true;
+ e.Attachment.CancelLoad();
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Failed to download attachment", ex.Message);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Toolkit/Toolkit.WPF/UI/Controls/PopupViewer/PopupViewer.Theme.xaml b/src/Toolkit/Toolkit.WPF/UI/Controls/PopupViewer/PopupViewer.Theme.xaml
index d5c49439a..598ac999e 100644
--- a/src/Toolkit/Toolkit.WPF/UI/Controls/PopupViewer/PopupViewer.Theme.xaml
+++ b/src/Toolkit/Toolkit.WPF/UI/Controls/PopupViewer/PopupViewer.Theme.xaml
@@ -57,12 +57,57 @@
+
+
+
-
+
+
+
+
diff --git a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Maui.cs b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Maui.cs
index a58c70802..a99208694 100644
--- a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Maui.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Maui.cs
@@ -152,40 +152,6 @@ private static void Attachment_Tapped(object? sender, EventArgs e)
return parent as PopupViewer;
}
- ///
- /// Occurs when an attachment is clicked.
- ///
- ///
- /// Override this to prevent the default open action.
- /// Attachment clicked.
- public virtual async void OnAttachmentClicked(PopupAttachment attachment)
- {
- if (attachment.LoadStatus == LoadStatus.NotLoaded)
- {
- _ = attachment.LoadAsync();
- return;
- }
- if (attachment.LoadStatus == LoadStatus.Loaded && attachment.Attachment != null)
- {
- var viewer = GetPopupViewerParent();
- if(viewer is not null)
- {
- bool handled = viewer.OnPopupAttachmentClicked(attachment);
- if (handled)
- return;
- }
-
- try
- {
- await Microsoft.Maui.ApplicationModel.Launcher.Default.OpenAsync(
- new Microsoft.Maui.ApplicationModel.OpenFileRequest(attachment.Name, new ReadOnlyFile(attachment.Filename!, attachment.ContentType)));
- }
- catch
- {
- }
- }
- }
-
private class AttachmentViewModel : System.ComponentModel.INotifyPropertyChanged
{
public AttachmentViewModel(PopupAttachment attachment)
diff --git a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Windows.cs b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Windows.cs
index 32ef069bb..9a9f5c895 100644
--- a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Windows.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.Windows.cs
@@ -30,26 +30,15 @@ public partial class AttachmentsPopupElementView : Control
{
private const string AttachmentListName = "AttachmentList";
- ///
- /// Occurs when an attachment is clicked.
- ///
- /// Override this to prevent the default "save to file dialog" action.
- /// Attachment clicked.
- public virtual async void OnAttachmentClicked(PopupAttachment attachment)
+ private UI.Controls.PopupViewer? GetPopupViewerParent()
{
- SaveFileDialog saveFileDialog = new SaveFileDialog();
- saveFileDialog.FileName = attachment.Name;
- if (saveFileDialog.ShowDialog() == true)
+ var parent = VisualTreeHelper.GetParent(this);
+ while (parent is not null && parent is not UI.Controls.PopupViewer popup)
{
- try
- {
- using var stream = await attachment.Attachment!.GetDataAsync();
- using var outfile = saveFileDialog.OpenFile();
- await stream.CopyToAsync(outfile);
- }
- catch { }
+ parent = VisualTreeHelper.GetParent(parent);
}
+ return parent as UI.Controls.PopupViewer;
}
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.cs b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.cs
index ef27677e5..3b9a17405 100644
--- a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/AttachmentsPopupElementView.cs
@@ -126,6 +126,56 @@ public AttachmentsPopupElement? Element
///
public static readonly DependencyProperty ElementProperty =
PropertyHelper.CreateProperty(nameof(Element), null, (s, oldValue, newValue) => s.LoadAttachments());
+
+
+ ///
+ /// Occurs when an attachment is clicked.
+ ///
+ ///
+ /// Override this to prevent the default open action.
+ /// Attachment clicked.
+ public virtual async void OnAttachmentClicked(PopupAttachment attachment)
+ {
+ if (attachment.Attachment != null)
+ {
+ var viewer = GetPopupViewerParent();
+ if (viewer is not null)
+ {
+ bool handled = viewer.OnPopupAttachmentClicked(attachment);
+ if (handled)
+ return;
+ }
+#if MAUI
+ try
+ {
+ if (attachment.LoadStatus == LoadStatus.NotLoaded)
+ await attachment.LoadAsync();
+ await Microsoft.Maui.ApplicationModel.Launcher.Default.OpenAsync(
+ new Microsoft.Maui.ApplicationModel.OpenFileRequest(attachment.Name, new ReadOnlyFile(attachment.Filename!, attachment.ContentType)));
+ }
+ catch(System.Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine($"Failed to open attachment: " + ex.Message);
+ }
+#else
+ SaveFileDialog saveFileDialog = new SaveFileDialog();
+ saveFileDialog.FileName = attachment.Name;
+ if (saveFileDialog.ShowDialog() == true)
+ {
+ try
+ {
+ using var stream = await attachment.Attachment!.GetDataAsync();
+ using var outfile = saveFileDialog.OpenFile();
+ await stream.CopyToAsync(outfile);
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Trace.WriteLine($"Failed to save file to disk: " + ex.Message);
+ }
+ }
+#endif
+ }
+ }
}
}
#endif
\ No newline at end of file
diff --git a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.Maui.cs b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.Maui.cs
index bfb389ab5..64313a67d 100644
--- a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.Maui.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.Maui.cs
@@ -107,59 +107,6 @@ internal static Style GetStyle(string resourceKey, Style defaultStyle)
internal static Style GetPopupViewerTitleStyle() => GetStyle(PopupViewerTitleStyleName, DefaultPopupViewerTitleStyle);
internal static Style GetPopupViewerCaptionStyle() => GetStyle(PopupViewerCaptionStyleName, DefaultPopupViewerCaptionStyle);
-
- ///
- /// Raised when a loaded popup attachment is clicked
- ///
- ///
- /// By default, when an attachment is clicked, the default application for the file type (if any) is launched. To override this,
- /// listen to this event, set the property to true and perform
- /// your own logic.
- ///
- /// Example: Use the .NET MAUI share API for the attachment:
- ///
- /// private async void PopupAttachmentClicked(object sender, PopupAttachmentClickedEventArgs e)
- /// {
- /// e.Handled = true; // Prevent default launch action
- /// await Share.Default.RequestAsync(new ShareFileRequest(new ReadOnlyFile(e.Attachment.Filename!, e.Attachment.ContentType)));
- /// }
- ///
- ///
- ///
- public event EventHandler? PopupAttachmentClicked;
-
- internal bool OnPopupAttachmentClicked(PopupAttachment attachment)
- {
- var handler = PopupAttachmentClicked;
- if (handler is not null)
- {
- var args = new PopupAttachmentClickedEventArgs(attachment);
- PopupAttachmentClicked?.Invoke(this, args);
- return args.Handled;
- }
- return false;
- }
- }
-
- ///
- /// Event argument for the event.
- ///
- public class PopupAttachmentClickedEventArgs : EventArgs
- {
- internal PopupAttachmentClickedEventArgs(PopupAttachment attachment)
- {
- Attachment = attachment;
- }
-
- ///
- /// Gets or sets a value indicating whether the event handler has handled the event and the default action should be prevented.
- ///
- public bool Handled { get; set; }
-
- ///
- /// Gets the attachment that was clicked.
- ///
- public PopupAttachment Attachment { get; }
}
}
#endif
\ No newline at end of file
diff --git a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.cs b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.cs
index 37a2721b7..6721adcc8 100644
--- a/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.cs
@@ -217,6 +217,59 @@ public ScrollBarVisibility VerticalScrollBarVisibility
ScrollBarVisibility.Auto
#endif
);
+
+ ///
+ /// Raised when a popup attachment is clicked
+ ///
+ ///
+ /// By default, when an attachment is clicked, the default application for the file type (if any) is launched. To override this,
+ /// listen to this event, set the property to true and perform
+ /// your own logic.
+ ///
+ /// Example: Use the .NET MAUI share API for the attachment:
+ ///
+ /// private async void PopupAttachmentClicked(object sender, PopupAttachmentClickedEventArgs e)
+ /// {
+ /// e.Handled = true; // Prevent default launch action
+ /// await Share.Default.RequestAsync(new ShareFileRequest(new ReadOnlyFile(e.Attachment.Filename!, e.Attachment.ContentType)));
+ /// }
+ ///
+ ///
+ ///
+ public event EventHandler? PopupAttachmentClicked;
+
+ internal bool OnPopupAttachmentClicked(PopupAttachment attachment)
+ {
+ var handler = PopupAttachmentClicked;
+ if (handler is not null)
+ {
+ var args = new PopupAttachmentClickedEventArgs(attachment);
+ PopupAttachmentClicked?.Invoke(this, args);
+ return args.Handled;
+ }
+ return false;
+ }
+ }
+
+ ///
+ /// Event argument for the event.
+ ///
+ public class PopupAttachmentClickedEventArgs : EventArgs
+ {
+ internal PopupAttachmentClickedEventArgs(PopupAttachment attachment)
+ {
+ Attachment = attachment;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the event handler has handled the event and the default action should be prevented.
+ ///
+ public bool Handled { get; set; }
+
+ ///
+ /// Gets the attachment that was clicked.
+ ///
+ public PopupAttachment Attachment { get; }
}
}
#endif
\ No newline at end of file