How to make WebView editable on winrt? - webview

I need to create simple HTML editor. I know desktop application I can get access to DOM and set DesignMode=true. How can I do it for WebView in winrt application?

So seems I've found solution how to set DesignMode for WebView in WinRT applications.
I just needed invoke javascript method that could change document.designMode property to "on"
In my case I implemented extension for WebView where added DependencyProperty.
public static class WebViewEx
{
public static readonly DependencyProperty DesignModeProperty = DependencyProperty.RegisterAttached(
"DesignMode", typeof(bool),
typeof(WebViewEx),
new PropertyMetadata(null, OnDesignModePropertyChanged));
private async static void OnDesignModePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (DesignMode.DesignModeEnabled)
return;
WebView view = dependencyObject as WebView;
if (view == null)
return;
if (e.NewValue == e.OldValue)
return;
await view.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
if ((bool)e.NewValue)
{
await view.InvokeScriptAsync("eval", new string[] { "document.designMode = \"on\";" });
}
else
{
await view.InvokeScriptAsync("eval", new string[] { "document.designMode = \"off\";" });
}
});
}
public static void SetDesignMode(DependencyObject element, bool value)
{
element.SetValue(DesignModeProperty, value);
}
public static bool GetDesignMode(DependencyObject element)
{
return (bool)element.GetValue(DesignModeProperty);
}
}
That allows me to turn on\off DesignMode from XAML
<WebView x:Name="webViewBody" Source="about:blank" controls:WebViewEx.DesignMode="true"/>
Mandatory requirement to Invoke javascript methods is webview should be initialized. In my case I set source property to "about:blank"

Related

How to approach writing a WPF Custom Control in F#?

I am trying to understand how a wpf custom control could be written in F#.
As an example, I have the following C# code for a drag and drop on a canvas (in C#). It inherits from ListBox. I'm not looking for anybody to rewrite this. But I'm at a loss as to how it would be implemented in Elmish.wpf since there is no xaml to deal with. (I believe a Custom Control does not have a XAML interface).
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Stargate.XI.Client.Views.CustomControls
{
public delegate void DropCompletedEventHandler(object sender, DropCompletedEventArgs e);
// To add a custom DropCompletedEvent to an ItemsControl, I would either have to have an attached property, as in
// https://stackoverflow.com/questions/15134514/attached-behavior-handling-an-attached-event-in-wpf
// or subclass an ItemsControl as below. Creating a simple custom control, like here, seems cleaner.
// Note: ItemsControl can't select items, only present collections. Only a Selector or one of it's descendants can select items
// Hence, only the ListBox or its derivative,ListView, have Selector's.
public class ChartCanvas : ListBox
{
public event EventHandler PlayMusicEvent;
public event EventHandler PauseMusicEvent;
public event EventHandler StopMusicEvent;
public event EventHandler DisposeMusicEvent;
public event EventHandler DisposePosterEvent;
#region DropCompletedEvent
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent DropCompletedEvent = EventManager.RegisterRoutedEvent(
"DropCompleted", RoutingStrategy.Bubble, typeof(DropCompletedEventHandler), typeof(ChartCanvas));
// Provide CLR accessors for the event. The RoutedEventHandler, e.g., "DropCompleted" is used in the xaml declaration for the ImageCanvas.
public event DropCompletedEventHandler DropCompleted
{
add { AddHandler(DropCompletedEvent, value); }
remove { RemoveHandler(DropCompletedEvent, value); }
}
// This method raises the DropCompleted event
public void RaiseDropCompletedEvent(object datatype)
{
RaiseEvent(new DropCompletedEventArgs(DropCompletedEvent, datatype));
}
#endregion
public ChartCanvas()
{
AllowDrop = true;
DragEnter += IC_DragEnter;
Drop += IC_Drop;
DragOver += IC_DragOver;
DragLeave += IC_DragLeave;
}
private void IC_DragLeave(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void IC_DragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void IC_Drop(object sender, DragEventArgs e)
{
var data = e.Data.GetData(DataFormats.Text);
var dragSource = e.Data.GetData("DragSource");
RaiseDropCompletedEvent(data);
}
private void IC_DragEnter(object sender, DragEventArgs e)
{
e.Handled = true;
}
#region PlayMovie
private ICommand _playMovie;
public ICommand PlayMovieCommand
{
get
{
if (_playMovie == null)
{
_playMovie = new RelayCommand(
p => true,
p => this.PlayMovie());
}
return _playMovie;
}
}
private void PlayMovie()
{
PlayMusicEvent?.Invoke(this, EventArgs.Empty);
}
#endregion
#region PauseMovie
private ICommand _pauseMovie;
public ICommand PauseMovieCommand
{
get
{
if (_pauseMovie == null)
{
_pauseMovie = new RelayCommand(
p => true,
p => this.PauseMovie());
}
return _pauseMovie;
}
}
private void PauseMovie()
{
PauseMusicEvent?.Invoke(this, EventArgs.Empty);
}
#endregion
#region StopMovie
private ICommand _stopMovie;
public ICommand StopMovieCommand
{
get
{
if (_stopMovie == null)
{
_stopMovie = new RelayCommand(
p => true,
p => this.StopMovie());
}
return _stopMovie;
}
}
private void StopMovie()
{
StopMusicEvent?.Invoke(this, EventArgs.Empty);
}
#endregion
public bool Dispose
{
get { return (bool)GetValue(DisposeProperty); }
set { SetValue(DisposeProperty, value); }
}
// Using a DependencyProperty as the backing store for Dispose. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisposeProperty =
DependencyProperty.Register("Dispose", typeof(bool), typeof(ChartCanvas), new PropertyMetadata(false,
(s,e) =>
{
ChartCanvas chartcanvas = s as ChartCanvas;
chartcanvas.DisposeMusicEvent?.Invoke(chartcanvas, EventArgs.Empty);
chartcanvas.DisposePosterEvent?.Invoke(chartcanvas, EventArgs.Empty);
}
));
}
}
Any suggestions to this newbie as to how to approach this would be much appreciated.
TIA

How to call a javascript function from webview xamarin android?

I am trying to call a javascript function from my webview xamarin.android app. How can I do it?
Firstly, implement a custom WebClient (look at CustomWebClient inheritance model) which overrides the OnPageFinished (aka OnNavigationCompleted) default behaviout:
WebView webView = new WebView(this);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.AllowUniversalAccessFromFileURLs = true;
webView.LoadUrl("http://yoururl.com");
SetContentView(webView);
And now you can invoke JS directly like
webView.EvaluateJavascript("JS code",null);
The second argument is a callback . Which is a placeholder for the javascript result .You can also define the JavascriptResult as the following:
public class JavascriptResult : Java.Lang.Object, IValueCallback
{
private TaskCompletionSource<string> source;
public Task<string> JsResult => source.Task;
public JavascriptResult()
{
source = new TaskCompletionSource<string>();
}
public void OnReceiveValue(Java.Lang.Object result)
{
try
{
string res = ((Java.Lang.String)result).ToString();
source.SetResult(res);
}
catch (Exception ex)
{
source.SetException(ex);
}
}
}

Implement dependency injection in background services in Xamarin Forms using Prism

I am making use of Prism in my xamarin forms project.I was able to use dependency injection(constructor injection) in my View Model without any problems.I am also making use of background services to push long running tasks in the background.How do I inject dependency in my Background services?When I try to pass the interface object as a paramater to the constructor(SyncingBackgroundingCode) ,the object(SqliteService) is null.I have registered and resolved the objects in the dependency injection container.
How to handle this case?Can anybody provide an example or link to implement this scenario?
This is the piece of code where im trying to implement dependency injection.
This is in Droid :-
public class AndroidSyncBackgroundService : Service
{
CancellationTokenSource _cts;
public override IBinder OnBind (Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)
{
_cts = new CancellationTokenSource ();
Task.Run (() => {
try {
//INVOKE THE SHARED CODE
var oBackground = new SyncingBackgroundingCode();
oBackground.RunBackgroundingCode(_cts.Token).Wait();
}
catch (OperationCanceledException)
{
}
finally {
if (_cts.IsCancellationRequested)
{
var message = new CancelledTask();
Device.BeginInvokeOnMainThread (
() => MessagingCenter.Send(message, "CancelledTask")
);
}
}
}, _cts.Token);
return StartCommandResult.Sticky;
}
public override void OnDestroy ()
{
if (_cts != null) {
_cts.Token.ThrowIfCancellationRequested ();
_cts.Cancel ();
}
base.OnDestroy ();
}
}
This is in PCL:-
public class SyncingBackgroundingCode
{
public SQLiteConnection _sqlconnection;
SqliteCalls oSQLite = new SqliteCalls();
ISqliteService _SqliteService;
public SyncingBackgroundingCode(ISqliteService SqliteService)
{
//object is null
}
public async Task RunBackgroundingCode(CancellationToken token)
{
DependencyService.Get<ISQLite>().GetConnection();
await Task.Run (async () => {
token.ThrowIfCancellationRequested();
if (App.oSqliteCallsMainLH != null)
{
App.bRunningBackgroundTask = true;
oSQLite = App.oSqliteCallsMainLH;
await Task.Run(async () =>
{
await Task.Delay(1);
oSQLite.ftnSaveOnlineModeXMLFormat("Offline", 0);
oSQLite.SyncEmployeeTableData();
oSQLite.SaveOfflineAppCommentData();
oSQLite.SaveOfflineAdditionToFlowData();
await Task.Delay(500);
var msgStopSyncBackgroundingTask = new StopSyncBackgroundingTask();
MessagingCenter.Send(msgStopSyncBackgroundingTask, "StopSyncBackgroundingTask");
});
}
}, token);
}
}
Unfortunately Xamarin and Xamarin Forms don't give frameworks like Prism anywhere to tie into to handle IoC scenarios. There are a couple of ways you can handle this though.
First the Container is a public property on the PrismApplication in your background service you could do something like:
public class FooBackgroundService
{
private App _app => (App)Xamarin.Forms.Application.Current;
private void DoFoo()
{
var sqlite = _app.Container.Resolve<ISQLite>();
}
}
Another slightly more involved way would be to use the ServiceLocator pattern. You might have something like the following:
public static class Locator
{
private static Func<Type, object> _resolver;
public static T ResolveService<T>() =>
(T)_resolver?.Invoke(typeof(T));
public static void SetResolver(Func<Type, object> resolver) =>
_resolver = resolver;
}
In your app you would then simply set the resolver. Prism actually does something similar to this with the ViewModel locator, which then allows it to inject the correct instance of the NavigationService.
public class App : PrismApplication
{
protected override void OnInitialized()
{
SetServiceLocator();
NavigationService.NavigateAsync("MainPage");
}
protected override void RegisterTypes()
{
// RegisterTypes
}
private void SetServiceLocator()
{
Locator.SetResolver(type => Container.Resolve(type, true));
}
}
Finally your service would simply reference the Service Locator like:
public class BarBackgroundService
{
public void DoBar()
{
var sqlite = Locator.ResolveService<ISQLite>();
// do foo
}
}

How to get the progress of WebView while loading the data, Xamarin.Forms

I am developing an App using Xamarin.Forms for listing the news from different sources. I use a webView to open the link corresponding to the news. But I want to show the progress while loading the webpage into web view, like the progress bar on Safari App. For this I have used the ProgressBar element like this:
<StackLayout>
<!-- WebView needs to be given height and width request within layouts to render. -->
<ProgressBar Progress ="" HorizontalOptions="FillAndExpand" x:Name="progress"/>
<WebView x:Name="webView"
HeightRequest="1000"
WidthRequest="1000"
VerticalOptions= "FillAndExpand"
Navigating="webOnNavigating"
Navigated="webOnEndNavigating"/>
</StackLayout>
and in the code I have used
void webOnNavigating (object sender, WebNavigatingEventArgs e)
{
progress.IsVisible = true;
}
void webOnEndNavigating (object sender, WebNavigatedEventArgs e)
{
progress.IsVisible = false;
}
But I want to show also the progress of loading the data, not just an indication that is loading and load. I want the user to know that the data are loading. Is there a way to achieve this.
The implementations should be platform specific via custom renders. Luckily this topics has been discussed already for different platforms here on SO.
The Android version based on this thread:
[assembly: ExportRenderer(typeof(WebView), typeof(GenericWebViewRenderer))]
namespace WebViewWithProgressBar.Droid
{
public class GenericWebViewRenderer : WebViewRenderer
{
Context ctx;
public GenericWebViewRenderer(Context context) : base(context)
{
ctx = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var progressBar = new Android.Widget.ProgressBar(ctx, null, Android.Resource.Attribute.ProgressBarStyleHorizontal);
Control.SetWebChromeClient(new MyWebChromeClient(progressBar));
Control.AddView(progressBar);
}
class MyWebChromeClient : Android.Webkit.WebChromeClient
{
Android.Widget.ProgressBar progressBar;
public MyWebChromeClient(Android.Widget.ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
progressBar.SetProgress(newProgress, true);
}
}
}
}
On iOS it is a bit trickier, here is a very simple mock that does it job pretty well:
[assembly: ExportRenderer(typeof(WebView), typeof(GenericWebViewRenderer))]
namespace WebViewWithProgressBar.iOS
{
public class GenericWebViewRenderer : ViewRenderer<WebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var progressBar = new UIProgressView(UIProgressViewStyle.Bar);
progressBar.TintColor = UIColor.Green;
progressBar.TrackTintColor = UIColor.Black;
progressBar.ProgressTintColor = UIColor.Red;
var webView = new UIWebView(Frame);
webView.AddSubview(progressBar);
SetNativeControl(webView);
Control.Delegate = new MyUIWebViewDelegate(progressBar);
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://google.com")));
}
}
class MyUIWebViewDelegate : UIWebViewDelegate
{
UIProgressView progressBar { get; }
public MyUIWebViewDelegate(UIProgressView progressBar)
{
this.progressBar = progressBar;
}
public override void LoadStarted(UIWebView webView)
{
progressBar.SetProgress(0.1f, false);
}
public override void LoadingFinished(UIWebView webView)
{
progressBar.SetProgress(1.0f, true);
}
public override void LoadFailed(UIWebView webView, NSError error)
{
// TODO:
}
}
}
}
For more details please check here.
P.S.: This code examples are available on github.

how to get webview content android after loadurl

Here is My Code.
I just want to get the page content from the Webview after load the URL .
On Android, I have a WebView that is displaying a page.
How do I get the page source without requesting the page again?
It seems WebView should have some kind of WebResourceResponse () method in Android 3.0 but don't know how to use it.
public class WebActivity extends Activity {
WebView mWebView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webView);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("https://xyz.com");
mWebView.setWebViewClient(new HelloWebViewClient());
}
private class HelloWebViewClient extends WebViewClient {
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
{
return null;
}
#Override
public void onPageFinished(WebView view, String url)
{
/* This call inject JavaScript into the page which just finished loading. */
mWebView.loadUrl("javascript:window.HTMLOUT.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}}
I know this is old, but for future visitors, the following should work.
First, you need to define an interface such as:
final class LoadingInterface {
#JavascriptInterface
public void outputHTML(final String html){
//do what ever you want with html
}
}
Then on your WebView call
webView.addJavascriptInterface(new LoadingInterface(), "INTERFACE");
In onPageFinished put:
String js = "javascript:"
+"var html = document.getElementsByTagName('html')[0].innerHTML;"
+"window.INTERFACE.outputSchedule(html);";
webView.loadUrl(js);
If you only need part of a page, you can replace getElementsByTagName('html')[0] with something like getElementById('partOfPage')

Resources