Edit

Share via


Share

Browse sample. Browse the sample

This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) IShare interface. This interface provides an API to send data, such as text or web links, to the devices share function.

The default implementation of the IShare interface is available through the Share.Default property. Both the IShare interface and Share class are contained in the Microsoft.Maui.ApplicationModel.DataTransfer namespace.

When a share request is made, the device displays a share window, prompting the user to choose an app to share with:

Share from your app to a different app

Get started

To access the Share functionality, the following platform-specific setup is required:

No setup is required.

The share functionality works by calling the RequestAsync method with a data payload that includes information to share to other applications. ShareTextRequest.Text and ShareTextRequest.Uri can be mixed and each platform will handle filtering based on content.

public async Task ShareText(string text)
{
    await Share.Default.RequestAsync(new ShareTextRequest
    {
        Text = text,
        Title = "Share Text"
    });
}

public async Task ShareUri(string uri, IShare share)
{
    await share.RequestAsync(new ShareTextRequest
    {
        Uri = uri,
        Title = "Share Web Link"
    });
}

Share a file

You can also share files to other applications on the device. .NET MAUI automatically detects the file type (MIME) and requests a share. However, operating systems may restrict which types of files can be shared. To share a single file, use the ShareFileRequest type.

The following code example writes a text file to the device, and then requests to share it:

public async Task ShareFile()
{
    string fn = "Attachment.txt";
    string file = Path.Combine(FileSystem.CacheDirectory, fn);

    File.WriteAllText(file, "Hello World");

    await Share.Default.RequestAsync(new ShareFileRequest
    {
        Title = "Share text file",
        File = new ShareFile(file)
    });
}

Share multiple files

Sharing multiple files is slightly different from sharing a single file. To share a single file, use the ShareMultipleFilesRequest type.

The following code example writes two text files to the device, and then requests to share them:

public async Task ShareMultipleFiles()
{
    string file1 = Path.Combine(FileSystem.CacheDirectory, "Attachment1.txt");
    string file2 = Path.Combine(FileSystem.CacheDirectory, "Attachment2.txt");

    File.WriteAllText(file1, "Content 1");
    File.WriteAllText(file2, "Content 2");

    await Share.Default.RequestAsync(new ShareMultipleFilesRequest
    {
        Title = "Share multiple files",
        Files = new List<ShareFile> { new ShareFile(file1), new ShareFile(file2) }
    });
}

Control file locations

Important

This section only applies to Android.

In some scenarios on Android, such as when a file is in private storage, it can be copied into the app cache which is then shared via an Android FileProvider. However, this can unintentionally expose the entire cache and app data to an attacker. This can be prevented by adding a file provider file paths override file to your app, and ensuring that files are copied to the location specified in this file prior to sharing.

To add a file provider file paths override file to your app, add a file named microsoft_maui_essentials_fileprovider_file_paths.xml to the Platforms\Android\Resources\xml folder in your app. Therefore, the full relative file name to the project should be Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml. Then, add XML to the file for your required paths:

 <?xml version="1.0" encoding="UTF-8" ?>
 <paths>
    <external-path name="external_files" path="sharing-root" />
    <cache-path name="internal_cache" path="sharing-root" />
    <external-cache-path name="external_cache" path="sharing-root" />  
 </paths>

For more information about file provider paths, see FileProvider on developer.android.com.

Prior to sharing a file, you should ensure it's first written to the sharing-root folder in one of the locations from the override file:

// Write into the specific sub-directory
var dir = Path.Combine(FileSystem.CacheDirectory, "sharing-root");  
Directory.CreateDirectory(dir);
var file = Path.Combine(dir, "mydata.txt");
await File.WriteAllTextAsync(file, $"My data: {count}");

// Share the file
await Launcher.OpenAsync(new OpenFileRequest
{
   Title = "My data",
   File = new ReadOnlyFile(file),
});

You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file <CacheDirectory>/sharing-root/mydata.txt and the shared URI is content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt then the file provider isn't using the correct path. If the shared URI is content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt then the file provider is using the correct path.

Warning

When sharing a file, if you receive an Java.Lang.IllegalArgumentException, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root.

Presentation location

Important

This section only applies to iPadOS.

When requesting a share or opening launcher on iPadOS, you can present it in a popover. This specifies where the popover will appear and point an arrow directly to. This location is often the control that launched the action. You can specify the location using the PresentationSourceBounds property:

await Share.RequestAsync(new ShareFileRequest
    {
        Title = Title,
        File = new ShareFile(file),
        PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
                                ? new Rect(0, 20, 0, 0)
                                : Rect.Zero
    });
await Launcher.OpenAsync(new OpenFileRequest
    {
        File = new ReadOnlyFile(file),
        PresentationSourceBounds = DeviceInfo.Platform == DevicePlatform.iOS && DeviceInfo.Idiom == DeviceIdiom.Tablet
                                ? new Rect(0, 20, 0, 0)
                                : Rect.Zero
    });

Everything described here works equally for Share and Launcher.

Here is an extension method that helps calculate the bounds of a view:

public static class ViewHelpers
{
    public static Rect GetAbsoluteBounds(this Microsoft.Maui.Controls.View element)
    {
        Element looper = element;

        var absoluteX = element.X + element.Margin.Top;
        var absoluteY = element.Y + element.Margin.Left;

        // Add logic to handle titles, headers, or other non-view bars

        while (looper.Parent != null)
        {
            looper = looper.Parent;
            if (looper is Microsoft.Maui.Controls.View v)
            {
                absoluteX += v.X + v.Margin.Top;
                absoluteY += v.Y + v.Margin.Left;
            }
        }

        return new Rect(absoluteX, absoluteY, element.Width, element.Height);
    }
}

This can then be used when calling RequestAsync:

public Command<Microsoft.Maui.Controls.View> ShareCommand { get; } = new Command<Microsoft.Maui.Controls.View>(Share);

async void Share(Microsoft.Maui.Controls.View element)
{
    try
    {
        await Share.Default.RequestAsync(new ShareTextRequest
        {
            PresentationSourceBounds = element.GetAbsoluteBounds(),
            Title = "Title",
            Text = "Text"
        });
    }
    catch (Exception)
    {
        // Handle exception that share failed
    }
}

You can pass in the calling element when the Command is triggered:

<Button Text="Share"
        Command="{Binding ShareWithFriendsCommand}"
        CommandParameter="{Binding Source={RelativeSource Self}}"/>

For an example of the ViewHelpers class, see the .NET MAUI Sample hosted on GitHub.

Platform differences

This section describes the platform-specific differences with the share API.