Skip to content

Commit

Permalink
Added attachment click event
Browse files Browse the repository at this point in the history
Also added ability to just trigger download, and make sure icons reflect current load status
  • Loading branch information
dotMorten committed Jun 3, 2024
1 parent a6ee2c2 commit 14aa6e5
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<Grid Background="#AA333333" Visibility="Collapsed" x:Name="PopupBackground" MouseDown="PopupBackground_MouseDown">
<Border BorderBrush="Black" BorderThickness="1" Background="White" HorizontalAlignment="Center" VerticalAlignment="Center" >
<esri:PopupViewer x:Name="popupViewer" Margin="5" Width="400" MaxHeight="400" />
<esri:PopupViewer x:Name="popupViewer" Margin="5" Width="400" MaxHeight="400" PopupAttachmentClicked="popupViewer_PopupAttachmentClicked" />
</Border>
</Grid>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,25 @@ private void PopupBackground_MouseDown(object sender, System.Windows.Input.Mouse
PopupBackground.Visibility = Visibility.Collapsed;
popupViewer.Popup = null;
}

private void popupViewer_PopupAttachmentClicked(object sender, UI.Controls.PopupAttachmentClickedEventArgs e)
{
// 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;
_ = e.Attachment.LoadAsync();
}
else if (e.Attachment.LoadStatus == LoadStatus.FailedToLoad)
{
e.Handled = true;
_ = e.Attachment.RetryLoadAsync();
}
else if (e.Attachment.LoadStatus == LoadStatus.Loading)
{
e.Handled = true;
e.Attachment.CancelLoad();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,33 @@
<Setter.Value>
<ControlTemplate TargetType="{x:Type primitives:AttachmentsPopupElementView}">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock" x:Key="DownloadIcon">
<Setter Property="FontFamily" Value="Segoe MDL2 Assets" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,5,0" />
<Setter Property="Text" Value="&#xEBD3;" />
<Style.Triggers>
<DataTrigger Binding="{Binding LoadStatus}" Value="{x:Static es:LoadStatus.Loading}" xmlns:es="clr-namespace:Esri.ArcGISRuntime;assembly=Esri.ArcGISRuntime">
<Setter Property="Text" Value="&#xE826;"/>
<Setter Property="ToolTip" Value="Downloading"/>
</DataTrigger>
<DataTrigger Binding="{Binding LoadStatus}" Value="{x:Static es:LoadStatus.Loaded}" xmlns:es="clr-namespace:Esri.ArcGISRuntime;assembly=Esri.ArcGISRuntime">
<Setter Property="Text" Value="&#xF28B;"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock Text="{Binding Title, Mode=OneWay}" Style="{StaticResource PopupViewerTitleStyle}" Visibility="{Binding Title, Converter={StaticResource PopupViewerVisibilityConverter}}" />
<TextBlock Text="{Binding Description, Mode=OneWay}" Style="{StaticResource PopupViewerCaptionStyle}" Visibility="{Binding Description, Converter={StaticResource PopupViewerVisibilityConverter}}" />
<ListView x:Name="AttachmentList" BorderThickness="0" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Margin="5" Cursor="Hand"/>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource DownloadIcon}"/>
<TextBlock Text="{Binding Name}" Margin="5" Cursor="Hand"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,40 +152,6 @@ private static void Attachment_Tapped(object? sender, EventArgs e)
return parent as PopupViewer;
}

/// <summary>
/// Occurs when an attachment is clicked.
/// </summary>
/// <remarks>
/// <para>Override this to prevent the default open action.</para></remarks>
/// <param name="attachment">Attachment clicked.</param>
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,15 @@ public partial class AttachmentsPopupElementView : Control
{
private const string AttachmentListName = "AttachmentList";

/// <summary>
/// Occurs when an attachment is clicked.
/// </summary>
/// <remarks>Override this to prevent the default "save to file dialog" action.</remarks>
/// <param name="attachment">Attachment clicked.</param>
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;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,56 @@ public AttachmentsPopupElement? Element
/// </summary>
public static readonly DependencyProperty ElementProperty =
PropertyHelper.CreateProperty<AttachmentsPopupElement, AttachmentsPopupElementView>(nameof(Element), null, (s, oldValue, newValue) => s.LoadAttachments());


/// <summary>
/// Occurs when an attachment is clicked.
/// </summary>
/// <remarks>
/// <para>Override this to prevent the default open action.</para></remarks>
/// <param name="attachment">Attachment clicked.</param>
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
53 changes: 0 additions & 53 deletions src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.Maui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>
/// Raised when a loaded popup attachment is clicked
/// </summary>
/// <remarks>
/// <para>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 <see cref="PopupAttachmentClickedEventArgs.Handled"/> property to <c>true</c> and perform
/// your own logic. </para>
/// <example>
/// Example: Use the .NET MAUI share API for the attachment:
/// <code language="csharp">
/// 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)));
/// }
/// </code>
/// </example>
/// </remarks>
public event EventHandler<PopupAttachmentClickedEventArgs>? 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;
}
}

/// <summary>
/// Event argument for the <see cref="PopupViewer.PopupAttachmentClicked"/> event.
/// </summary>
public class PopupAttachmentClickedEventArgs : EventArgs
{
internal PopupAttachmentClickedEventArgs(PopupAttachment attachment)
{
Attachment = attachment;
}

/// <summary>
/// Gets or sets a value indicating whether the event handler has handled the event and the default action should be prevented.
/// </summary>
public bool Handled { get; set; }

/// <summary>
/// Gets the attachment that was clicked.
/// </summary>
public PopupAttachment Attachment { get; }
}
}
#endif
53 changes: 53 additions & 0 deletions src/Toolkit/Toolkit/UI/Controls/PopupViewer/PopupViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,59 @@ public ScrollBarVisibility VerticalScrollBarVisibility
ScrollBarVisibility.Auto
#endif
);

/// <summary>
/// Raised when a popup attachment is clicked
/// </summary>
/// <remarks>
/// <para>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 <see cref="PopupAttachmentClickedEventArgs.Handled"/> property to <c>true</c> and perform
/// your own logic. </para>
/// <example>
/// Example: Use the .NET MAUI share API for the attachment:
/// <code language="csharp">
/// 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)));
/// }
/// </code>
/// </example>
/// </remarks>
public event EventHandler<PopupAttachmentClickedEventArgs>? 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;
}
}

/// <summary>
/// Event argument for the <see cref="PopupViewer.PopupAttachmentClicked"/> event.
/// </summary>
public class PopupAttachmentClickedEventArgs : EventArgs
{
internal PopupAttachmentClickedEventArgs(PopupAttachment attachment)
{
Attachment = attachment;
}

/// <summary>
/// Gets or sets a value indicating whether the event handler has handled the event and the default action should be prevented.
/// </summary>
public bool Handled { get; set; }

/// <summary>
/// Gets the attachment that was clicked.
/// </summary>
public PopupAttachment Attachment { get; }
}
}
#endif

0 comments on commit 14aa6e5

Please sign in to comment.