I have created a very simple UWP application with a single button. Clicking it should show the built-in share popup to share a PDF file.
The fact is that I have it working for Windows 10 (Desktop) but it doesn't work for mobile (the popup doesn't appear on the screen).
The PDF file comes as a byte array (because it will come from a remote service).
This is the code in MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataTransferManager.GetForCurrentView().DataRequested += OnDataRequested;
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
// This should come from a service
PdfBytes = await Microsoft.Toolkit.Uwp.StorageFileHelper.ReadBytesFromPackagedFileAsync("Document.pdf");
}
public byte[] PdfBytes { get; set; }
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
var si = await StorageFile.CreateStreamedFileAsync("Document.pdf", stream =>
{
var writeStream = stream.AsStreamForWrite();
writeStream.Write(PdfBytes, 0, PdfBytes.Length);
stream.Dispose();
}, null);
args.Request.Data.Properties.Title = "PDF Document";
args.Request.Data.Properties.Description = "Some description";
args.Request.Data.SetStorageItems(new IStorageItem[] { si });
deferral.Complete();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
DataTransferManager.ShowShareUI();
}
}
Is it correct? If it's not, how should I share the PDF (from its bytes)?
Thank you for your feedback. It seems that CreateStreamedFileAsync method does not work properly with Share contract in Mobile. We've logged this issue internally and I will update here once there is any progress.
For now, as a workaround, you can store the file in TemporaryFolder first and then share it like the following:
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
var tempFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("Document.pdf", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(tempFile, PdfBytes);
args.Request.Data.Properties.Title = "PDF Document";
args.Request.Data.Properties.Description = "Some description";
args.Request.Data.SetStorageItems(new IStorageItem[] { tempFile });
deferral.Complete();
}
Temporary app data store is the right place for data that you don’t want persisted after the current app session. The system can delete data stored at this location as needed to free up space. You can use it for any intermediate or temporary files. If you are writing large amounts of data to Temp, it is a good idea to clear it when your app is initialized to avoid the system or the user having to take action to free up storage. And you can do this by calling:
await ApplicationData.ClearAsync(ApplicationDataLocality.Temporary);
You have similar issue I had I believe
Have you tried changing
private async void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
var si = await StorageFile.CreateStreamedFileAsync("Document.pdf", stream =>
{
var writeStream = stream.AsStreamForWrite();
writeStream.Write(PdfBytes, 0, PdfBytes.Length);
stream.Dispose();
args.Request.Data.Properties.Title = "PDF Document";
args.Request.Data.Properties.Description = "Some description";
args.Request.Data.SetStorageItems(new IStorageItem[] { si });
deferral.Complete();
}, null);
}
I havent checked this code, so it probably wont compile but I have found that I had issue that looks similar to yours, if threads are involved. Take look at my issue here UWP DataTransferManager ShowShareUI() Opens Sharing Dialog with "This app can't share right now" and Closes it Immediately After
I faced the same issue, My share worked good in desktop application but not in mobile. After big struggle I found that the deferral is not working in windows 10 mobile.
So better remove these lines and try. Its working
var deferral = args.Request.GetDeferral();
deferral.Complete();
Related
I want to write into a Bluetooth LE Characteristic. (wpf c#, but has to work with UWP also)
I'm not exactly sure how this works, because I want to write a value not as a client, but as the server.
Like in the MS Example:
https://github.com/microsoft/Windows-universal-samples/blob/main/Samples/BluetoothLE/cs/Scenario3_ServerForeground.xaml.cs
The BLE service and characteristic are created on program start. (not in the MS example, but in my program)
After creating a Characteristic
GattLocalCharacteristicResult result = await serviceProvider.Service.CreateCharacteristicAsync(Constants.ModeCharacteristicUuid, Constants.modeParameters);
modeCharacteristic = result.Characteristic;
modeCharacteristic.WriteRequested += ModeCharacteristic_WriteRequestedAsync;
I want to use this method to write into the characteristic:
private async void ModeCharacteristic_WriteRequestedAsync(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args)
{
using (args.GetDeferral())
{
GattWriteRequest request = await args.GetRequestAsync();
if (request == null)
{
// No access allowed to the device. Application should indicate this to the user.
return;
}
request.Respond();
}
}
Only question to me now is how to write in the Mode-Characteristic.
For example, I simply want to write a 5 into this Characteristic.
What code do I need?
ModeCharacteristic_WriteRequestedAsync(modeCharacteristic, 5);
doesn't work.
I don't know how to use GattWriteRequestedEventArgs args or the event handler.
Write to Bluetooth LE Characteristic as a Server in WPF or UWP
You should process write operation in GattLocalCharacteristic ReadRequested, when the client send read request, you can get GattReadRequest in above event, and then call RespondWithValue to response data that written with data writer.
private async void ResultCharacteristic_ReadRequestedAsync(GattLocalCharacteristic sender, GattReadRequestedEventArgs args)
{
// BT_Code: Process a read request.
using (args.GetDeferral())
{
// Get the request information. This requires device access before an app can access the device's request.
GattReadRequest request = await args.GetRequestAsync();
if (request == null)
{
// No access allowed to the device. Application should indicate this to the user.
rootPage.NotifyUser("Access to device not allowed", NotifyType.ErrorMessage);
return;
}
var writer = new DataWriter();
writer.ByteOrder = ByteOrder.LittleEndian;
writer.WriteInt32(resultVal);
// Can get details about the request such as the size and offset, as well as monitor the state to see if it has been completed/cancelled externally.
// request.Offset
// request.Length
// request.State
// request.StateChanged += <Handler>
// Gatt code to handle the response
request.RespondWithValue(writer.DetachBuffer());
}
}
This is the code I'm now using. It works exactly like it should. First create a characteristic, then adding a subscribedClientsChanged eventhandler.
GattLocalCharacteristicResult result = await serviceProvider.Service.CreateCharacteristicAsync(Constants.ModusCharacteristicUuid, Constants.gattOperandParameters);
if (result.Error == BluetoothError.Success)
{
modeCharacteristic = result.Characteristic;
}
else
{
return false;
}
modeCharacteristic.SubscribedClientsChanged += ResultCharacteristic_SubscribedClientsChanged;
private void ResultCharacteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args)
{
ModeNotify(5);
}
private async void ModeNotify(int computedValue)
{
var writer = new DataWriter();
writer.ByteOrder = ByteOrder.LittleEndian;
writer.WriteInt32(computedValue);
IReadOnlyList<GattClientNotificationResult> results = await modeCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}
First of all hello guys i just wanted to add button that downloads zip files from link and then unzips and i ran into problems i get this error:
"System.IO.IOException: 'The process cannot access the file
'C:\GTA\TEST.zip' because it is being used by another process.'"
It looks really simple but i can't solve it so i hope you guys help me. this is code:
private void button2_Click(object sender, EventArgs e)
{
string root = #"C:\GTA";
//this if directory doesn't exist
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
progressBar1.Value = 0;
WebClient webcl = new WebClient();
webcl.DownloadFileCompleted += Webcl_DownloadFileCompleted;
webcl.DownloadProgressChanged += Webcl_DownloadProgressChanged;
webcl.DownloadFileAsync(new Uri("https://download1474.mediafire.com/17r5hin4vceg/izkb8vk7pudg5g4/TEST.zip"), #"C:\GTA\TEST.zip");
string targetfolder = #"C:\GTA\UNZIPEDFolder";
string sourceZipFile = #"C:\GTA\TEST.zip";
ZipFile.ExtractToDirectory(sourceZipFile, targetfolder);
}
I'm no expert here, however you get the file asynchronosly without awaiting it.
DownloadFileAsync
So you make a call to extract the file while it's being downloaded.
You calling ExtractToDirectory before file will be actually downloaded, as file downloading is async. So, you need to await when downloading process will finish. To do so, you will need the following
make the whole event click handler async - private async void button2_Click(object sender, EventArgs e).
replace DownloadFileAsync which returns void and thus is not async/await-friendly with DownloadFileTaskAsync, which is awaitable.
Then you will able to await downloading with await webcl.DownloadFileTaskAsync(...args here...);
finally, you can remove DownloadFileCompleted subscription, as you may be sure that after await the file downloading is completed.
By the way, WebClient is considered as an old API and is not recommended for using in the new code. You may consider to switch to HttpClient.
To elaborate a bit on the two previous answers, you are in fact trying to unzip the file before you have downloaded it. You should change your code as follows:
private async void button2_Click(object sender, EventArgs e)
{
string root = #"C:\GTA";
//this if directory doesn't exist
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
progressBar1.Value = 0;
WebClient webcl = new WebClient();
webcl.DownloadFileCompleted += Webcl_DownloadFileCompleted;
webcl.DownloadProgressChanged += Webcl_DownloadProgressChanged;
await webcl.DownloadFileAsync(new Uri("https://download1474.mediafire.com/17r5hin4vceg/izkb8vk7pudg5g4/TEST.zip"), #"C:\GTA\TEST.zip");
string targetfolder = #"C:\GTA\UNZIPEDFolder";
string sourceZipFile = #"C:\GTA\TEST.zip";
ZipFile.ExtractToDirectory(sourceZipFile, targetfolder);
}
Note the async as well as the await before DownloadFileAsync().
Additionally, you might want to refactor that a bit and move the download / unzip part out of the Button Event Handler.
I'm trying to figure out a bug in my C# Xamarin Android code. Simple thing this application is ought to do - connect to the REST API, download the contents as a string for further analysis. At first my mistake was not including async tasks and using voids instead, but when I changed them the code seems to be stuck at point of retrieval of data.
String content;
private void OnClick1(object sender, EventArgs e)
{
output.Text = "";
GetJSONTextFromWeb("https://ameobea.me/osutrack/api/get_changes.php?user=XXXXXX&mode=0", "XXXXXX", "0");
while (content==null)
{
DoNothing();
}
output.Text = content;
}
private async Task GetJSONTextFromWeb(String address, String user, String modeID)
{
URL url = new URL(address);
URLConnection conn = url.OpenConnection();
//conn.AddRequestProperty("user", user); those lines are
//conn.AddRequestProperty("mode", modeID); removed for investigation.
//conn.Connect(); //this one caused the same issue.
content = (String)conn.Content; //Here the code seems to freeze without any warning.
}
private void DoNothing()
{
//literally. Made to await for the result.
}
Anyone knows the possible reason?
I'd suggest swapping out the use of that particular library in favor of the System.Http assembly, it's supported in Xamarin and is a lot better documented. I'd change your above code to something like below (Don't forget to declare System.Net.Http in the same place that you've declared your other assemblies).
using System.Net.Http;
async private void OnClick1(object sender, EventArgs e)
{
output.Text = "";
// await the return of a string from the url address
// awaiting this removes the need for the pointless while loop you were doing
content = await GetJSONTextFromWeb("https://ameobea.me/osutrack/api/get_changes.php?user=XXXXXX&mode=0");
output.Text = content;
}
private async Task<string> GetJSONTextFromWeb(String address)
{
// The library you were using is a poorly documented port from a JAVA library
// I'd suggest using the http library, it's supported in Xamarin and has better docs
var client = new HttpClient();
var data = await client.GetStringAsync(address);
return data;
}
In my app, I send an email with Windows Store Share Contract. Everything works fine, only one problem what I cant solve. Where and how can I delete the created file? Because if I delete it anywhere, the email goes without attachment. There is my code:
async void ShareImageHandler(DataTransferManager sende, DataRequestedEventArgs e)
{
DataRequest request = e.Request;
DataRequestDeferral deferral = request.GetDeferral();
var localFolder = ApplicationData.Current.LocalFolder;
var file = await localFolder.CreateFileAsync(Guid.NewGuid(); + ".png");
var screenShotter = new ScreenShotter();
try
{
await screenShotter.SaveVisualElementToFileAsync(
gridPrintForPostCard, file, new Rect(0, 0, 10000, 10000));
request.Data.Properties.Thumbnail =
RandomAccessStreamReference.CreateFromFile(file);
request.Data.SetBitmap(
RandomAccessStreamReference.CreateFromFile(file));
}
finally
{
deferral.Complete();
}
}
You can add a background task that runs even when the app isn't running that periodically clears away the temporary files. You can even have conditions on when it can run, like if the user is away from the computer for example.
More Info: Guidelines for background tasks
A more simple solution would be to simply delete all of them when you app starts (loaded event) or shuts down. But it's a less robust solution.
hi there :) il get right to it.
Problem :
when i try to instanciate LiveConnectClient and then try to access the event : GetCompleted
which supose to be in the LiveConnectClient is not showing and on all the examples i been looking at even those on here are using it. this is not the only class this is happening on it is also happening on LiveAuthClient as well no events even the post on the net says there should be.
i tried to reinstall Vs2012 and sdk wp8 and live sdk from scratch but have not solved it
for refrence i using this example to see if i can it to work :
//event triggered when Skydrive sign in status is changed
private void btnSignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
//if the user is signed in
if (e.Status == LiveConnectSessionStatus.Connected)
{
session = e.Session;
client = new LiveConnectClient(e.Session);
infoTextBlock.Text = "Accessing SkyDrive...";
//get the folders in their skydrive
client.GetCompleted +=
new EventHandler<LiveOperationCompletedEventArgs>(btnSignin_GetCompleted);
client.GetAsync("me/skydrive/files?filter=folders,albums");
}
//otherwise the user isn't signed in
else
{
infoTextBlock.Text = "Not signed in.";
client = null;
}
}
i got no luck solving it and running out of ideas. So im hoping one of u boys out there can shed some light on it or lend a hand with dew wise words :)
thanks in advance. and i do apologies if this is to long a post.
regards jens
Indeed, it seems like those events have been removed in the latest versions of the SDK. You don't need them though, thanks to the async/await keywords. First, mark your method as async, then call the GetAsync method with the await keyword. And place afterward the code you would normally put in the GetCompleted event:
private async void btnSignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
//if the user is signed in
if (e.Status == LiveConnectSessionStatus.Connected)
{
session = e.Session;
client = new LiveConnectClient(e.Session);
infoTextBlock.Text = "Accessing SkyDrive...";
//get the folders in their skydrive
var result = await client.GetAsync("me/skydrive/files?filter=folders,albums");
// Do here what you would normally do in btnSignin_GetCompleted
}
//otherwise the user isn't signed in
else
{
infoTextBlock.Text = "Not signed in.";
client = null;
}
}