如何在NET MAUI桌面应用程序中从"共享"对话框中获取内容



我知道如何将订阅应用程序的plataform windows包清单配置为共享目标,但我不知道如何将共享内容放入网络MAUI

https://i.stack.imgur.com/tnj2z.jpg

我在Plataforms 内Windows文件夹下的Package.appxmanifest中注册此行为

https://i.stack.imgur.com/2TyMW.jpg

安卓上的intent事件可以存档此onCreate功能(https://developer.android.com/training/sharing/receive),在Net MAUI的Windows桌面应用程序下的"共享"对话框中获取内容需要捕捉什么方法或事件?如有任何建议,将不胜感激

  1. 首先,最好在Windows中禁用MAUI应用程序的多个实例。以下是您的应用程序可能修改的示例:https://github.com/dotnet/maui/issues/9973.

  2. 将所有激活事件重定向到处理程序。就像上面的例子:

//Getting events
var args = AppInstance.GetCurrent().GetActivatedEventArgs();
//Creating your own handler
---> keyInstance.Activated += OnActivated;
and redirect events
---> await keyInstance.RedirectActivationToAsync(args);
  1. 处理"共享";事件。我已经创建了处理从Paint 3D到我的MAUI应用程序的Share事件的示例。我已经使用MAUI依赖性服务来简化主应用程序逻辑方面的图像处理:

以下是Program.cs:中的更新

private static void OnActivated(object sender, AppActivationArguments args)
{
if (app is null)
{
throw new Exception("app should not be null");
}
//here is the sample how we could simply use MAUI Dependency Service for our purposes (see declarations in MauiProgram.cs)
var imageTransportService = app.Services.GetService(typeof(IImagePickerService)) as IImagePickerService;
if (imageTransportService is null)
return;
if (args?.Kind != ExtendedActivationKind.ShareTarget)
return;
var sharedArgs = args.Data as ShareTargetActivatedEventArgs;
if (sharedArgs == null)
return;
//Sharing number of files 
//3D Paint share pictures in this way
if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.StorageItems))
{
Task.Factory.StartNew(async () =>
{
sharedArgs.ShareOperation.ReportStarted();
IReadOnlyList<IStorageItem> storageItems = null;
storageItems = await sharedArgs.ShareOperation.Data.GetStorageItemsAsync();
string fileList = String.Empty;
List<Stream> imageStreamList = new();
for (int index = 0; index < storageItems.Count; index++)
{
var storageItem = storageItems[index] as StorageFile;
if (storageItem == null) continue;
if (HasImageExtension(storageItem.Name))
{
Stream imageStream = null;
try
{
var randomAccessStream = await storageItem.OpenReadAsync();
imageStream = randomAccessStream.AsStreamForRead();
}
catch (Exception ex) { continue; }
imageStreamList.Add(imageStream);
}
else { continue; }//some other format that we do not consider
}
if (imageStreamList.Count > 1)
{
imageTransportService.RizeImagesStream(imageStreamList);
}
else
{
if (imageStreamList.Count == 1)
imageTransportService.RizeImageStream(imageStreamList[0]);
}
sharedArgs.ShareOperation.ReportCompleted();
});
return;
}
else //Bitmap 
//Probably some other app could share bitmap in this way
//I was disappointed that 3D Paint do not share picture in this way :(
if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.Bitmap))
{
Task.Factory.StartNew(async () =>
{
sharedArgs.ShareOperation.ReportStarted();
var randomStream = await sharedArgs.ShareOperation.Data.GetBitmapAsync();
var resultStream = await randomStream.OpenReadAsync();
imageTransportService .RizeImageStream(resultStream.AsStreamForRead());
//Getting image on other side:
//var image = new Image { Source = ImageSource.FromStream(() => resultStream.AsStream()) };
sharedArgs.ShareOperation.ReportCompleted();
});
return;
}
//else //Text
//if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.Text))
//{
//}
//else
//    if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.Uri))
//{ 
//    //proceed uri
//}
//else
//    if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.ApplicationLink))
//{
//    //proceed app link
//}
//else
//    if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.WebLink))
//{
//    //proceed weblink
//}
//else
//    if (sharedArgs.ShareOperation.Data.Contains(StandardDataFormats.Html))
//{
//    //proceed html
//}
}
public static bool HasImageExtension(string pathOrExt)
{
// ext = "*.bmp;*.dib;*.rle"           descr = BMP
// ext = "*.jpg;*.jpeg;*.jpe;*.jfif"   descr = JPEG
// ext = "*.gif"                       descr = GIF
// ext = "*.tif;*.tiff"                descr = TIFF
// ext = "*.png"   
var source = Path.GetExtension(pathOrExt).ToLower();

return 
(source.EndsWith(".png") || //common case ->ths why the first
source.EndsWith(".bmp") || 
source.EndsWith(".dib") ||
source.EndsWith(".rle") ||
source.EndsWith(".jpg") ||
source.EndsWith(".jpeg") ||
source.EndsWith(".jpe") ||
source.EndsWith(".jfif") ||
source.EndsWith(".gif") ||
source.EndsWith(".tif") ||
source.EndsWith(".tiff"));
}

修改MauiProgram.cs以支持一些新的";共享图像传输";服务。

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{

var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

// Registering our service here -->
builder.Services.AddSingleton<IImagePickerService>(new ImagePickerService());
//registering your app pages and services
builder.Services.AddSingleton<MainPage>();
//builder.Services.AddTransient<SomeOtherPage>();
return builder.Build();
}
}
//Some prototypes of "shared images transport" service
public interface IImagePickerService
{
public delegate void ImagePass(object sender, Stream imageStream);
public event ImagePass OnPickImage;
public delegate void ImagesPass(object sender, List<Stream> imageStreams);
public event ImagesPass OnPickImages;
public void RizeImageStream(Stream imageStream);
public void RizeImagesStream(List<Stream> imageStreams);
}
public class ImagePickerService : IImagePickerService
{
public event IImagePickerService.ImagePass OnPickImage;
public event IImagePickerService.ImagesPass OnPickImages;
public void RizeImageStream(Stream imageStream)
{
OnPickImage?.Invoke(this, imageStream);
}
public void RizeImagesStream(List<Stream> imageStreams)
{
OnPickImages?.Invoke(this, imageStreams);
}
}

还有最后一个。在主逻辑方面(在我的示例MainPage.xaml.cs中(,您可以sibscribe到共享数据事件:

//..some declarations here
IImagePickerService imagePicker;
//Thanks MAUI dependecy services mechanism for automatic providing our "shared images transport" registered service -->
public MainPage(IImagePickerService imagePicker)
{
//.. some code here
_imagePicker = imagePicker;
_imagePicker.OnPickImage += OnPickImage;
_imagePicker.OnPickImages += OnPickImages;
InitializeComponent();

//.. some code here
}
private void OnPickImages(object sender, List<Stream> imageStreams)
{
List<Image> images = new List<Image>();
foreach(var imageStream in imageStreams)
{
images.Add(new Image { Source = ImageSource.FromStream(() => imageStream) });
}
//some usage of shared images
}
private void OnPickImage(object sender, Stream imageStream)
{
Image image = new Image { Source = ImageSource.FromStream(() => imageStream) };
//some usage of shared image
}

也许还有更方便的方法。我只是向您展示了在哪里可以找到事件,以及应该如何处理数据。ShareTargetActivatedEventArgs的处理方式非常不寻常。

第二件事是,在我的情况下,传递给应用程序的图像(paint 3D(在rgb通道中存在一些错误。在图像后处理步骤中可以很容易地纠正它,但我认为微软应该站在他们这边做这项工作。

PS:别忘了ImageSource.FromFile(((=>{}(目前不起作用(可能,23/Microsoft错误(,所以如果你能组织更好的图像传输方式(如文件或共享路径(,你就不会在页面上看到图像。无论如何,您需要使用流。


PS2:我还创建了一个Android支持的示例。不幸的是,由于缺乏适当的文件,这对我来说更加复杂。

我添加了IntentFilter以共享支持作为示例,了解如何管理不同类型。还使用了上一个示例中的MAUI依赖机制来展示我们如何将图像数据从MainActivity转移到应用程序中的其他位置。

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density),
LaunchMode = LaunchMode.SingleTask]
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSend, Intent.ActionSendMultiple },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryDefault, Intent.CategoryDefault },
DataMimeTypes = new[] { "image/*", "text/plain", "image/*"})]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);

string action = intent.Action;
string type = intent.Type;
if (Intent.ActionSend.Equals(action) && type != null)
{
if (type.Equals("text/plain"))
{
ProceedText(intent);
}
else
if (type.StartsWith("image/"))
{
ProceedImage(intent);
}
}
else
if (Intent.ActionSendMultiple.Equals(action) && type != null)
{
if (type.StartsWith("image/"))
{
ProceedMultipleImages(intent);
}
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

if (Intent == null) return;
string action = Intent.Action;
string type = Intent.Type;
if (Intent.ActionSend.Equals(action) && type != null)
{
if (type.Equals("text/plain"))
{
ProceedText(Intent);
}
else
if (type.StartsWith("image/"))
{
ProceedImage(Intent);
}
} 
else
if (Intent.ActionSendMultiple.Equals(action) && type != null)
{
if(type.StartsWith("image/"))
ProceedMultipleImages(Intent);
}
}

private void ProceedMultipleImages(Intent intent)
{
var imagePickerService = MauiApplication.Current?.Services?.GetService(typeof(IImagePickerService)) as IImagePickerService;
if (imagePickerService == null) { return; }
var clipData = intent.ClipData;
if (clipData != null)
{
List<Stream> streams = new List<Stream>();
for (int i = 0; i < clipData.ItemCount; i++)
{
ClipData.Item item = clipData.GetItemAt(i);
Android.Net.Uri uri = item.Uri;
Stream stream = ContentResolver.OpenInputStream(uri);
if(stream == null) {
continue;
}
streams.Add(stream);
}
if(streams.Count > 0)
{
imagePickerService.RizeImagesStream(streams);
}

}
}
private void ProceedImage(Intent intent)
{
var imagePickerService = MauiApplication.Current?.Services?.GetService(typeof(IImagePickerService)) as IImagePickerService;
if (imagePickerService == null) { return; }
var clipData = intent.ClipData;
if (clipData?.ItemCount == 0) return;

ClipData.Item item = clipData.GetItemAt(0);
Android.Net.Uri uri = item.Uri;

Stream stream = ContentResolver.OpenInputStream(uri);
if (stream == null)
{
return;
}
imagePickerService.RizeImageStream(stream);
}
private void ProceedText(Intent intent)
{
var imagePickerService = MauiApplication.Current?.Services?.GetService(typeof(IImagePickerService)) as IImagePickerService;
if (imagePickerService == null) { return; }
//do the same as in previous (add to IImagePickerService -> text handlers) and proceed text through it
//..
}

您应该考虑的关键事项是:

  1. 您应该设置LaunchMode=LaunchMode.SingleTask。在这种情况下,传入共享数据的默认处理程序是OnNewIntent方法。否则(SingleTop除外(,默认处理程序将为OnCreate方法=>在这种情况下,您将获得应用程序的其他实例。这不是我们正确处理的问题,但在这种应用程序初始化中,Microsoft GUI方面存在错误(SetColors方法=>MAUI主题初始化(。

  2. 您也应该在OnCreate方法中复制共享数据处理的逻辑。如果你的应用程序被停止,而其他应用程序通过向你共享一些数据来启动你的应用-默认处理程序将是OnCreate方法。。。在这种情况下,所有初始化都是正确的。

  3. 我还在这里展示了如何从droid端访问MAUI依赖机制的示例:MauiApplication.Current?。服务?。GetService(typeof(IImagePickerService((作为IImagePickersService;

最新更新