How to open file .doc use "Word" app on Xamarin Forms? - xamarin

Use Xamarin Forms and on iOS, how to check and open Word app when Open file by UrL?
Reference: https://learn.microsoft.com/en-us/office/client-developer/integration/integrate-with-office-from-ios-applications
This is my code, it isn't work:
Device.OpenUri(new Uri("ms-word:ofe|u|https://calibre-ebook.com/downloads/demos/demo.docx"));
Please help me!
Thanks!

What you are asking here can be done using a simple WebView in iOS:
First, create a custom WebView class that allows you to pick file uri:
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
Then in ios make a renderer for the same and do something like this:
[assembly: ExportRenderer (typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace DisplayPDF.iOS
{
public class CustomWebViewRenderer : ViewRenderer<CustomWebView, UIWebView>
{
protected override void OnElementChanged (ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged (e);
if (Control == null) {
SetNativeControl (new UIWebView ());
}
if (e.OldElement != null) {
// Cleanup
}
if (e.NewElement != null) {
var customWebView = Element as CustomWebView;
string fileName = Path.Combine (NSBundle.MainBundle.BundlePath, string.Format ("Content/{0}", WebUtility.UrlEncode (customWebView.Uri)));
Control.LoadRequest (new NSUrlRequest (new NSUrl (fileName, false)));
Control.ScalesPageToFit = true;
}
}
}
}
Then use the custom control like this:
<local:CustomWebView Uri="FooPath" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
Where FooPath is the path for the doc file.

I have solution for my project, I try it:
If you only open file .docx on iOS, you could write code at Share Code of iOS:
Check device of user has setup app (Word, Excel, PP, etc...)
public static bool HasSetupAppDocument(string extension)
{
if (string.IsNullOrEmpty(extension))
return false;
// Device has setup app?
var result = UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString($"{extension}"));
return result;
}
(Ex: extension is ms-word: or ms-excel: or ms-excel:
Reference: https://learn.microsoft.com/en-us/office/client-developer/integration/integrate-with-office-from-ios-applications#office-protocols)
Notes: Add source to Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>ms-word</string>
<string>ms-excel</string>
<string>ms-powerpoint</string>
</array>
At Class Dependencies, open file with URL:
Device.OpenUri(new Uri($"{convertExtension}{url}"));
Note: url is link file share on Onedrive and Be sure that account Onedrive as same as account login Word app (if you had set security).
If file has mode Read-only, app Word will open file with mode Read-only.

Related

How To Pass a Query String in Xamarin WebView Source On Navigating

I need to pass a query string into a WebView Xamarin Source on Navigating (www.websiteexmple.com?mobileapp=true).
In Android works perfect, but in IOS does not seems to work correctly sometimes . I am missing someting ?
I have notice it does not work in IOS when Net Core website needs to do some redirects : Ex: User Enter Email and click Next Step.
public void WebView_NavigatingAsync(object sender, WebNavigatingEventArgs args)
{
if (!args.Url.Contains(Source) && !args.Url.Contains("facebook") && !args.Url.Contains("google"))
{
var uri = new Uri(args.Url);
// Device.OpenUri(uri);
Launcher.OpenAsync(uri);
args.Cancel = true;
}
else
{
var src = args.Url;
string prefix = "?";
if (src.Contains("?"))
{
prefix = "&";
}
if (!args.Url.Contains("mobileapp=true"))
{
args.Cancel = true;
var webview = (WebView)sender;
webview.Source = src + prefix + "mobileapp=true";
}
}
}
I think there is a bug in WebView.Source for iOS, the question mark is coming encoded and the system didn't recognize the URL.
You can create a custom WebViewRender and replace %3F to ?
On iOS project create a CustomWebViewRender
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace Test.iOS.Renders
{
public class CustomWebViewRenderer : WkWebViewRenderer
{
public override WKNavigation LoadRequest (NSUrlRequest request)
{
var url = request.Url.ToString().Replace("html%3F", "html?");
var dotnetURI = new System.Uri(url);
var idn = new System.Globalization.IdnMapping();
NSUrl nsURL = new NSUrl(dotnetURI.Scheme, idn.GetAscii(dotnetURI.DnsSafeHost), dotnetURI.PathAndQuery);
return base.LoadRequest(new NSUrlRequest(nsURL));
}
}
}
On Xamarin project use your custom WebView
cs file:
namespace Test.Renders
{
public class CustomWebView : WebView
{
}
}
Xaml file:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:test="clr-namespace:Test.Renders"
...>
<ContentPage.Content>
<StackLayout>
<test:CustomWebView x:Name="map"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"/>
...

Phone dialer issues in xamarin.plugins.messaging

I am new for Xamarin and I just wanted to directly make a phone call from my Xamarin app.
I just wanted to include the hashtag(#) symbol in the phone number as a string, but it doesn't include the symbol when the program is executed.
Here is the code,
private void Button_Clicked(object sender, EventArgs e)
{
var phoneDialer = CrossMessaging.Current.PhoneDialer;
if (phoneDialer.CanMakePhoneCall)
phoneDialer.MakePhoneCall("*999#");
}
Any help would be appreciated!
Have a look at this thread:
Specifically, if a URL contains the * or # characters, the Phone
application does not attempt to dial the corresponding phone number.
There is also an answer in Swift which says iOS11 now allows us to call number with * or # , I translate it to C# and you can have a try with dependency service:
In shared Project:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
DependencyService.Get<IPhoneCallTask>().DoPhoneCall();
}
}
public interface IPhoneCallTask
{
void DoPhoneCall();
}
In iOS Project:
[assembly: Dependency(typeof(phoneCall))]
namespace App591.iOS
{
class phoneCall : IPhoneCallTask
{
public void DoPhoneCall()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
NSString number = new NSString("*111*12345#");
NSString encode = number.CreateStringByAddingPercentEncoding(NSUrlUtilities_NSCharacterSet.UrlHostAllowedCharacterSet);
NSString header = new NSString("tel://");
string tmp = string.Format("{0}{1}", encode, header);
NSString urlTemp = new NSString(tmp);
if (UIApplication.SharedApplication.CanOpenUrl(new Uri(urlTemp)))
{
UIApplication.SharedApplication.OpenUrl(new Uri(urlTemp));
}
}
}
}
}

How do I enable WebGL in Xamarin.Forms WebView on UWP?

I’m new to Xamarin.Forms and tried using WebView on my Windows 10 x64 v1803 machine with UWP but I can’t see how to get it to work with WebGL.
Sites which use WebGL either display a message that “Your video card does not support WebGL or just don’t display and graphical content at all.
Is this a limitation of UWP or WebView itself?
Is it a WebView configuration issue?
WebGL works in all other browsers on this machine.
UWP WebView control is support WebGL. There is similar issue case in msdn you could refer. Please try to use SeparateProcess mode WebView to replace the default one.
public MainPage()
{
this.InitializeComponent();
var MyWebView = new WebView(WebViewExecutionMode.SeparateProcess);
MyWebView.Source = new Uri("http://cycleblob.com/");
this.RootGrid.Children.Add(MyWebView);
}
I had the same problem, but with the newer Xamarin Forms it took a little more poking around to get this took work right. However, I do like that they moved the native WebView resolver back to the responsibility of the UWP/iOS/Android project (as a native XAML object) instead of using code branching with compiler directives in the Shared project.
Start by creating a HybridWebView class in the shared project to use as your WebForm view object:
public class HybridWebView : Xamarin.Forms.WebView
{
Action<string> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void Cleanup()
{
action = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
}
Then in the UWP project, create a custom renderer, which will construct the native WebView and relay the events back to the WebForms object in the Shared project:
Put this at the top of the namespace, to link the HybridWebView with the Custom Renderer:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(WebViewRenderer2))]
Then create the renderer class (for the IOS and android projects, if you leave this class out, it defaults to the standard native controls which seem to work fine for me):
public class WebViewRenderer2 : ViewRenderer<Xamarin.Forms.WebView, Windows.UI.Xaml.Controls.WebView>, IWebViewDelegate
{
Windows.UI.Xaml.Controls.WebView _control;
public void LoadHtml(string html, string baseUrl)
{
}
public void LoadUrl(string url)
{
}
protected override void Dispose(bool disposing)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
if (_control == null) {
_control = new Windows.UI.Xaml.Controls.WebView(WebViewExecutionMode.SeparateProcess);
SetNativeControl(_control);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var xamWebView = sender as HybridWebView;
switch(e.PropertyName.ToLower())
{
case "source":
var urlSource = xamWebView.Source as Xamarin.Forms.UrlWebViewSource;
_control.Source = new Uri(urlSource.Url);
break;
case "width":
_control.Width = xamWebView.Width;
break;
case "height":
_control.Height = xamWebView.Height;
break;
case "isfocused":
var focused = xamWebView.IsFocused;
if (focused)
_control.Focus(FocusState.Programmatic);
else
_control.Focus(FocusState.Unfocused);
break;
}
}
}
You can also use the Custom Renderer to inject scripts, and you can use it to communicate from the native webview back to the Xamarin App, as seen here: HybridWebView Communication

How to create a code-only webview with Xamarin.Forms

I'm trying to use a library that doesn't has a .Net SDK, but as I want to use it only to return a string, I thought I could use it's JS SDK by creating a custom WebView that returns strings (https://xamarinhelp.com/xamarin-forms-webview-executing-javascript/).
The first problem that I faced was that a CustomRenderer is not called in Xamarin.Forms until the View is added to a Page (or at least I couldn't make it be called). To fix this I added a call to Platform.CreateRenderer in each platform.
It did the trick and the CustomRenderer executed. But when I tried to call a JS function to retrieve a string, the app just hung and stayed that way.
I didn't try to insert the WebView in a Page because I want it to be independent of the page that the app is current on, and as I want a "code-only" html, I don't see the point of adding it somewhere.
My classes:
JSEvaluator
namespace MyNamespace.Views
{
public class JSEvaluator : WebView
{
public static BindableProperty EvaluateJavascriptProperty = BindableProperty.Create(nameof(EvaluateJavascript), typeof(Func<string, Task<string>>), typeof(JSEvaluator), null, BindingMode.OneWayToSource);
public Func<string, Task<string>> EvaluateJavascript
{
get { return (Func<string, Task<string>>)GetValue(EvaluateJavascriptProperty); }
set { SetValue(EvaluateJavascriptProperty, value); }
}
public JSEvaluator()
{
}
}
}
UWP Renderer
[assembly: ExportRenderer(typeof(JSEvaluator), typeof(JSEvaluatorRenderer))]
namespace MyNamespace.UWP.Renderers
{
public class JSEvaluatorRenderer : WebViewRenderer
{
public JSEvaluatorRenderer() { }
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
var webView = e.NewElement as JSEvaluator;
if (webView != null)
webView.EvaluateJavascript = async (js) =>
{
return await Control.InvokeScriptAsync("eval", new[] { js });
};
}
}
}
Creation and use
if (jsEvaluator == null)
{
jsEvaluator = new JSEvaluator { Source = new HtmlWebViewSource { Html = HTML.html } };
#if __ANDROID__
Xamarin.Forms.Platform.Android.Platform.CreateRenderer(jsEvaluator);
#elif __IOS__
Xamarin.Forms.Platform.iOS.Platform.CreateRenderer(jsEvaluator);
#elif WINDOWS_UWP
Xamarin.Forms.Platform.UWP.Platform.CreateRenderer(jsEvaluator);
#endif
}
Thanks for the help :)
I had to add the WebView to a page, as #SushiHangover said in the comment. With this done, it worked as expected.

How do you allow users to copy and paste from an Xamarin.Forms label

How do you allow users to copy and paste from an Xamarin.Forms Label?
Click on the text on any platform the default settings don't allow highlighting and therefore copying and pasting.
Any help would be appreciated.
What you could do is wrap your label in a gesture recogniser:
<Label Text="Test">
<Label.GestureRecognizers>
<TapGestureRecognizer
Tapped="YourFunctionToHandleMadTaps"
NumberOfTapsRequired="1"
/>
</Label.GestureRecognizers>
</Label>
This will trigger your function and in that function you can get to the clipboard and copy and paste. However I haven't been able to find an easy way to access the clipboard in Xamarin.Forms so you have to use the dependency service.
Xamarin.Forms Dependency service documentation
Here is how I did my clipboard data access. Please note that in my project I only needed to nab data from the clipboard so this code just shows you how to access the clipboard data:
Create an interface in you X.F project eg:
public interface IClipBoard
{
String GetTextFromClipBoard();
}
Implement the interface in your mobile projects:
Android:
public string GetTextFromClipBoard ()
{
var clipboardmanager = (ClipboardManager)Forms.Context.GetSystemService (Context.ClipboardService);
var item = clipboardmanager.PrimaryClip.GetItemAt(0);
var text = item.Text;
return text;
}
iOs:
public string GetTextFromClipBoard ()
{
var pb = UIPasteboard.General.GetValue ("public.utf8-plain-text");
return pb.ToString ();
}
Don't forget to add the Assembly bits at the top:
iOs: [assembly: Dependency (typeof (ClipBoard_iOs))]
Android: [assembly: Dependency (typeof (ClipBoard_Droid))]
Call the dependency service from you X.F function
public void YourFunctionToHandleMadTaps(Object sender, EventArgs ea)
{
var clipboardText = DependencyService.Get<IClipBoard> ().GetTextFromClipBoard ();
YourFunctionToHandleMadTaps.Text = clipboardText;
}
Since I_Khanage provided only a half solution, I will post the full solution.
IClipboardService should be implemented for all the targeting platforms, in my case it is Android and iOS:
public interface IClipboardService
{
string GetTextFromClipboard();
void SendTextToClipboard(string text);
}
// iOS
public class ClipboardService : IClipboardService
{
public string GetTextFromClipboard() => UIPasteboard.General.String;
public void SendTextToClipboard(string text) => UIPasteboard.General.String = text;
}
// Android
public class ClipboardService : IClipboardService
{
public string GetTextFromClipboard()
{
var clipboardmanager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
var item = clipboardmanager.PrimaryClip.GetItemAt(0);
var text = item.Text;
return text;
}
public void SendTextToClipboard(string text)
{
// Get the Clipboard Manager
var clipboardManager = (ClipboardManager)Forms.Context.GetSystemService(Context.ClipboardService);
// Create a new Clip
var clip = ClipData.NewPlainText("YOUR_TITLE_HERE", text);
// Copy the text
clipboardManager.PrimaryClip = clip;
}
}
The code is available on github.
Now just add a GestureRecognizer in order to trigger the tap event.
P.S.: Clipboard service is now available as a part of Xamarin.Essentials package, so there is no need to write one yourself.

Resources