How to upload photo from library in webview in android app - javascript

I am using webview component on my android app. Users can load images from android photo library and show these images on a web page in the webview. How can I upload these image to my backend server from javascript?
Below is my java code to handle image chooser behavior:
setWebChromeClient(new WebChromeClient() {
#Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
Intent chooser = Intent.createChooser(intent, "Select Image");
activity.startActivityForResult(chooser, ResultCode.CHOOSE_PHOTO_REQUEST);
return false;
}
});
the above code will show image picker and when a user select an image, the onActivityResult will pass the selected image path to javascript as below:
if (resultCode == Activity.RESULT_OK) {
Uri imageUri = imageReturnedIntent.getData();
Log.d("IMAGE", "choose image uri " + imageUri);
String path = getRealPathFromURI(imageUri);
Log.d("IMAGE", "choose image real path " + path);
webView.loadUrl("javascript:choosePhotos('" + path + "')");
}
public String getRealPathFromURI(Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Images.Media.DATA};
cursor = mainActivity.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
in javascript, I can put the image path on a <img src=''/> tag to show the selected images. The path is something like this: '/storage/emulated/0/DCIM/Camera/IMG_20160808_200837.jpg'
It works fine here. But now I want to upload this image to my backend server. How can javascript handle this path: /storage/emulated/0/DCIM/Camera/IMG_20160808_200837.jpg.

You Can use filePathCallback to pass the selected file to webview.
Just create a global variable
ValueCallback filePathCallback;
and assign the parameter from onShowFileChooser() method to it.
then you can use this callback in onActivityResult() to pass the selected file to webview as :
Uri results[] = new Uri[]{imageUri};
filePathCallback.onReceiveValue(results);
then on html you will get file at

Related

Javascript scripts in html not firing from winform webBrowser control

With the below html file, that gets loaded into the document of a winform webBrowser, the functions and events in the scripts do not fire. When the winform displays, it only shows the button with id=paybutton (see form definition). It does not run the 1st script (window.YocoSDK etc) which references an online sdk (as in the src), and which adds a few more fields onto the form. This works in an online java test but not via c# winforms. Can anyone assist.
Secondly, the ShowMessage() function also does not fire on clicking the button.
My guess with both is that the inclusion of the online sdk in the "src" field is not happening.
HTMLPageSample.html file:
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>
</head>
<body>
<form id='payform' method='POST' >
<div class='one-liner'>
<div id='card-frame'>
</div>
<button id='paybutton' onclick='ShowMessage()'>
PAY ZAR 2.00
</button>
</div>
<p class='success-payment-message' />
</form>
<script>
var sdk = new window.YocoSDK({
publicKey: 'pk_test_blahblah'
});
var inline = sdk.inline({
layout: 'field',
amountInCents: 2000,
currency: 'ZAR'
});
inline.mount('#card-frame');
</script>
<script>
function ShowMessage() {
var form = document.getElementById('payform');
var submitButton = document.getElementById('paybutton');
form.addEventListener('submit', function (event) {
event.preventDefault()
submitButton.disabled = true;
inline.createToken().then(function (result) {
submitButton.disabled = false;
if (result.error) {
const errorMessage = result.error.message;
errorMessage && alert('error occured: ' + errorMessage);
} else {
const token = result;
alert('card successfully tokenised: ' + token.id);
}
}).catch(function (error) {
submitButton.disabled = false;
alert('error occured: ' + error);
});
});
};
</script>
</body>
</html>
//c# code for the windows form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApp1
{
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public partial class Form1 : Form
{
//Class example
//[ComVisible(true)]
public Form1()
{
InitializeComponent();
webBrowser1.ScriptErrorsSuppressed = true;
}
void Form1_Load(object sender, EventArgs e)
{
string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), #"..\..\");
//load the html into the webbrowser document. only the paybutton displays, the referenced library in "src" should call an online sdk that adds the payment fields to the form. these fields do not get added. so it seems the src reference is not working, or the script and form definitions cannot "see" each other?
webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
webBrowser1.ObjectForScripting = this;
}
private void button1_Click(object sender, EventArgs e)
{
//on button click, invoke the script that processes payment (does nothing)
webBrowser1.Document.InvokeScript("ShowMessage" );
}
private void button2_Click(object sender, EventArgs e)
{
//on button click, locate paybutton and invoke click method (does nothing)
foreach (HtmlElement element in webBrowser1.Document.All)
{
if (element.InnerText != null && element.InnerText.ToLower().StartsWith("pay zar"))
{
element.InvokeMember("click");
}
}
}
}
}
When using WebBrowser, it defaults to IE7, unless there is an entry in the registry.
If the process is running as 64-bit, the following registry keys are searched:
HKLM\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
HKCU\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
If the process is running as 32-bit, the following registry keys are searched:
HKLM\SOFTWARE\WOW6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
HKCU\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
Note: While different registry keys are searched in HKLM, when HKCU is searched, both 32-bit and 64-bit search the same subkey.
Here's a sample registry entry for a program named "MyApp.exe" that emulates IE11.
Below are step-by-step instructions that show how to run a JavaScript function when a button is clicked in C#. It uses a modified version of the HTML that's in the OP.
VS 2019:
Create a new project: Windows Forms App (.NET Framework) (name: WebBrowserTest)
Create a class (name: HelperRegistry.cs)
Note: The following code can be used to add the required entry in the registry when the Form loads. It's adapted from here.
HelperRegistry
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Diagnostics;
namespace WebBrowserTest
{
public enum BrowserEmulationVersion
{
Default = 0,
Version7 = 7000,
Version8 = 8000,
Version8Standards = 8888,
Version9 = 9000,
Version9Standards = 9999,
Version10 = 10000,
Version10Standards = 10001,
Version11 = 11000,
Version11Edge = 11001
};
public class HelperRegistry
{
public static BrowserEmulationVersion GetBrowserEmulationVersion()
{
//get browser emmulation version for this program (if it exists)
BrowserEmulationVersion result = BrowserEmulationVersion.Default;
try
{
string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
object data = GetValueFromRegistry(RegistryHive.CurrentUser, #"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", programName);
if (data != null)
{
result = (BrowserEmulationVersion)Convert.ToInt32(data);
}
}
catch (System.Security.SecurityException ex)
{
// The user does not have the permissions required to read from the registry key.
LogMsg("Error: (GetBrowserEmulationVersion - SecurityException) - " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
// The user does not have the necessary registry rights.
LogMsg("Error: (GetBrowserEmulationVersion - UnauthorizedAccessException) - " + ex.Message);
}
catch (Exception ex)
{
LogMsg("Error: (GetBrowserEmulationVersion) - " + ex.Message);
}
return result;
}
public static int GetInternetExplorerMajorVersion()
{
//get IE version
int result = 0;
string version = string.Empty;
try
{
string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
object data = GetValueFromRegistry(RegistryHive.LocalMachine, #"Software\Microsoft\Internet Explorer", "svcVersion");
if (data == null)
data = GetValueFromRegistry(RegistryHive.CurrentUser, #"Software\Microsoft\Internet Explorer", "Version");
if (data != null)
{
version = data.ToString();
int separator = version.IndexOf('.');
if (separator != -1)
{
int.TryParse(version.Substring(0, separator), out result);
}
}
}
catch (System.Security.SecurityException ex)
{
// The user does not have the permissions required to read from the registry key.
LogMsg("Error: (GetInternetExplorerMajorVersion - SecurityException) - " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
// The user does not have the necessary registry rights.
LogMsg("Error: (GetInternetExplorerMajorVersion - UnauthorizedAccessException) - " + ex.Message);
}
catch (Exception ex)
{
LogMsg("Error: (GetInternetExplorerMajorVersion) - " + ex.Message);
}
return result;
}
private static object GetValueFromRegistry(RegistryHive hive, string subkey, string regValue)
{
//if running as 64-bit, get value from 64-bit registry
//if running as 32-bit, get value from 32-bit registry
RegistryView rView = RegistryView.Registry64;
object data = null;
if (!Environment.Is64BitProcess)
{
//running as 32-bit
rView = RegistryView.Registry32;
}
using (RegistryKey regBaseKey = RegistryKey.OpenBaseKey(hive, rView))
{
using (RegistryKey sKey = regBaseKey.OpenSubKey(subkey))
{
if (sKey != null)
{
data = sKey.GetValue(regValue, null);
if (data != null)
{
LogMsg("data: " + data.ToString());
}
else
{
LogMsg("data is null (" + data + ")");
}
}
}
}
return data;
}
public static bool IsBrowserEmulationSet()
{
return GetBrowserEmulationVersion() != BrowserEmulationVersion.Default;
}
private static void LogMsg(string msg)
{
string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
System.Diagnostics.Debug.WriteLine(logMsg);
}
public static bool SetBrowserEmulationVersion()
{
BrowserEmulationVersion emulationCode;
int ieVersion = GetInternetExplorerMajorVersion();
if (ieVersion >= 11)
{
emulationCode = BrowserEmulationVersion.Version11;
}
else
{
switch (ieVersion)
{
case 10:
emulationCode = BrowserEmulationVersion.Version10;
break;
case 9:
emulationCode = BrowserEmulationVersion.Version9;
break;
case 8:
emulationCode = BrowserEmulationVersion.Version8;
break;
default:
emulationCode = BrowserEmulationVersion.Version7;
break;
}
}
return SetBrowserEmulationVersion(emulationCode);
}
public static bool SetBrowserEmulationVersion(BrowserEmulationVersion browserEmulationVersion)
{
bool result = false;
//if running as 64-bit, get value from 64-bit registry
//if running as 32-bit, get value from 32-bit registry
RegistryView rView = RegistryView.Registry64;
if (!Environment.Is64BitProcess)
{
//running as 32-bit
rView = RegistryView.Registry32;
}
try
{
string programName = System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]);
using (RegistryKey regBaseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, rView))
{
using (RegistryKey sKey = regBaseKey.OpenSubKey(#"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION", true))
{
if (sKey != null)
{
if (browserEmulationVersion != BrowserEmulationVersion.Default)
{
// if it's a valid value, update or create the value
sKey.SetValue(programName, (int)browserEmulationVersion, Microsoft.Win32.RegistryValueKind.DWord);
}
else
{
// otherwise, remove the existing value
sKey.DeleteValue(programName, false);
}
result = true;
}
}
}
}
catch (System.Security.SecurityException ex)
{
// The user does not have the permissions required to read from the registry key.
LogMsg("Error: (SetBrowserEmulationVersion - SecurityException) - " + ex.Message);
}
catch (UnauthorizedAccessException ex)
{
// The user does not have the necessary registry rights.
LogMsg("Error: (SetBrowserEmulationVersion - UnauthorizedAccessException) - " + ex.Message);
}
catch (Exception ex)
{
LogMsg("Error: (SetBrowserEmulationVersion) - " + ex.Message);
}
return result;
}
}
}
In the Form "Load" event handler add the following:
HelperRegistry.SetBrowserEmulationVersion();
If desired, the HTML can be embedded in the program.
Open Solution Explorer
In VS menu, click View
Select Solution Explorer
Open Properties Window
In VS menu, click View
Select Properties Window
Create HTML folder
In Solution Explorer, right-click <solution name>
Select Add
Select New Folder (rename to desired name; ex: HTML)
Right-click the folder you just created (ex: HTML) and select Add
Select New Item...
Select HTML Page (name: HTMLPageSample.html)
Click Add
Note: If you don't see "HTML Page" as an option, you'll need to open Visual Studio Installer and add a workload that includes HTML.
Set Properties for HTMLPageSample.html
In Solution Explorer, click HTMLPageSample.html
In the Properties Window, set Build Action = Embedded Resource
HTMLPageSample.html
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>
<script type="text/javascript">
var sdk = new window.YocoSDK({
publicKey: 'pk_test_blahblah'
});
var inline = sdk.inline({
layout: 'field',
amountInCents: 2000,
currency: 'ZAR'
});
inline.mount('#card-frame');
</script>
<script type="text/javascript">
function ShowMessage() {
try {
//alert('in ShowMessage...');
var form = document.getElementById('payform');
var submitButton = document.getElementById('paybutton');
form.addEventListener('submit', function (event) {
event.preventDefault()
submitButton.disabled = true;
inline.createToken().then(function (result) {
submitButton.disabled = false;
if (result.error) {
const errorMessage = result.error.message;
errorMessage && alert('error occured: ' + errorMessage);
} else {
const token = result;
alert('card successfully tokenised: ' + token.id);
}
}).catch(function (error) {
submitButton.disabled = false;
alert('error occured: ' + error);
});
});
}
catch (err) {
alert(err.message);
}
};
</script>
</head>
<body>
<form id='payform' method='POST'>
<div class='one-liner'>
<div id='card-frame'>
</div>
<button id='paybutton' onclick='ShowMessage()'>
PAY ZAR 2.00
</button>
</div>
<p class='success-payment-message' />
</form>
</body>
</html>
Now, we'll need some code to read the embedded HTML file. We'll use code from here.
Create a class (name: HelperLoadResource.cs)
HelperLoadResource
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Diagnostics;
namespace WebBrowserTest
{
public static class HelperLoadResource
{
public static string ReadResource(string filename)
{
//use UTF8 encoding as the default encoding
return ReadResource(filename, Encoding.UTF8);
}
public static string ReadResource(string filename, Encoding fileEncoding)
{
string fqResourceName = string.Empty;
string result = string.Empty;
//get executing assembly
Assembly execAssembly = Assembly.GetExecutingAssembly();
//get resource names
string[] resourceNames = execAssembly.GetManifestResourceNames();
if (resourceNames != null && resourceNames.Length > 0)
{
foreach (string rName in resourceNames)
{
if (rName.EndsWith(filename))
{
//set value to 1st match
//if the same filename exists in different folders,
//the filename can be specified as <folder name>.<filename>
//or <namespace>.<folder name>.<filename>
fqResourceName = rName;
//exit loop
break;
}
}
//if not found, throw exception
if (String.IsNullOrEmpty(fqResourceName))
{
throw new Exception($"Resource '{filename}' not found.");
}
//get file text
using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
{
using (StreamReader reader = new StreamReader(s, fileEncoding))
{
//get text
result = reader.ReadToEnd();
}
}
}
return result;
}
}
}
Usage:
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
Next, we'll work on our Form (name: Form1).
In Solution Explorer, right-click Form1.cs
Select View Designer
Open the Toolbox
In the VS menu, click View
Select Toolbox
Add WebBrowser to Form
In Toolbox, click on WebBrowser and drag it on top of the Form
Add Button to Form
In Toolbox, click on Button and drag it on top of the form
In the Properties Window, rename the button (name: btnSubmit)
Add Load event Handler to Form
In the Properties Window, click on
Double-click Load, to add the event handler
Whenever a page is loaded in the WebBrowser, either by using Navigate or by setting the DocumentText, it's important to wait until it is fully loaded. We'll create a method for the wait operation. I normally avoid using "DoEvents", but we'll use it this time.
private void WaitForBrowserToBeReady(int sleepTimeInMs = 125)
{
do
{
System.Threading.Thread.Sleep(sleepTimeInMs);
Application.DoEvents();
} while (webBrowser1.ReadyState != WebBrowserReadyState.Complete);
}
Now in Form1_Load, add the following code:
private void Form1_Load(object sender, EventArgs e)
{
//set browser emulation in registry
HelperRegistry.SetBrowserEmulationVersion();
//suppress script errors
webBrowser1.ScriptErrorsSuppressed = true;
//string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), #"..\..\");
//load the html into the webbrowser document. only the paybutton displays, the referenced library in "src"
//should call an online sdk that adds the payment fields to the form. these fields do not get added. so
//it seems the src reference is not working, or the script and form definitions cannot "see" each other?
//webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
if (Environment.Is64BitProcess)
{
Debug.WriteLine("Running as 64-bit");
}
else
{
Debug.WriteLine("Running as 32-bit");
}
//initialize WebBrowser
webBrowser1.Navigate("about:blank");
WaitForBrowserToBeReady();
//set HTML
webBrowser1.DocumentText = html;
WaitForBrowserToBeReady();
//Debug.WriteLine(webBrowser1.DocumentText);
}
As stated in the OP, when the button is click it's desired that the ShowMessage() javascript function be called. Due to the way the JavaScript function is written, we'll do the following:
HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("button");
foreach (HtmlElement element in col)
{
if (element.GetAttribute("id").Equals("paybutton"))
{
element.InvokeMember("click"); // Invoke the "Click" member of the button
}
}
Note: While the following will also call ShowMessage(),
object result = webBrowser1.Document.InvokeScript("ShowMessage");
it won't give the desired result due to form.addEventListener('submit'... which requires a "click".
Here's the full code for Form1.cs.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WebBrowserTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//set browser emulation in registry
HelperRegistry.SetBrowserEmulationVersion();
//suppress script errors
webBrowser1.ScriptErrorsSuppressed = true;
//string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), #"..\..\");
//load the html into the webbrowser document. only the paybutton displays, the referenced library in "src"
//should call an online sdk that adds the payment fields to the form. these fields do not get added. so
//it seems the src reference is not working, or the script and form definitions cannot "see" each other?
//webBrowser1.Navigate(System.IO.Path.Combine(path, "HTMLPageSample.html"));
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
if (Environment.Is64BitProcess)
{
Debug.WriteLine("Running as 64-bit");
}
else
{
Debug.WriteLine("Running as 32-bit");
}
//initialize WebBrowser
webBrowser1.Navigate("about:blank");
WaitForBrowserToBeReady();
//set HTML
webBrowser1.DocumentText = html;
WaitForBrowserToBeReady();
//Debug.WriteLine(webBrowser1.DocumentText);
}
private void LogMsg(string msg)
{
string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
System.Diagnostics.Debug.WriteLine(logMsg);
}
private void btnSubmit_Click(object sender, EventArgs e)
{
//object result = webBrowser1.Document.InvokeScript("ShowMessage", null);
//object result = webBrowser1.Document.InvokeScript("ShowMessage");
HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("button");
foreach (HtmlElement element in col)
{
if (element.GetAttribute("id").Equals("paybutton"))
{
element.InvokeMember("click"); // Invoke the "Click" member of the button
}
}
}
private void WaitForBrowserToBeReady(int sleepTimeInMs = 125)
{
do
{
System.Threading.Thread.Sleep(sleepTimeInMs);
Application.DoEvents();
} while (webBrowser1.ReadyState != WebBrowserReadyState.Complete);
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
}
}
}
Resources:
How to Click A Button Programmatically - Button in WebBrowser (IE)
Calling JavaScript functions in the Web Browser Control
Execute a JS function in WebBrowser C#
Configuring the Emulation Mode of an Internet Explorer WebBrowser Control
How to read embedded resource text file
C# WebBrowser control: window.external access sub object
Here's a version that uses WebView2. Below are step-by-step instructions that show how to run a JavaScript function when a button is clicked in C#. It uses a modified version of the HTML that's in the OP.
VS 2019:
Create a new project: Windows Forms App (.NET Framework) (name: WebView2SM)
If desired, the HTML can be embedded in the program.
Open Solution Explorer
In VS menu, click View
Select Solution Explorer
Open Properties Window
In VS menu, click View
Select Properties Window
Create HTML folder
In Solution Explorer, right-click <solution name>
Select Add
Select New Folder (rename to desired name; ex: HTML)
Right-click the folder you just created (ex: HTML) and select Add
Select New Item...
Select HTML Page (name: HTMLPageSample.html)
Click Add
Note: If you don't see "HTML Page" as an option, you'll need to open Visual Studio Installer and add a workload that includes HTML.
Set Properties for HTMLPageSample.html
In Solution Explorer, click HTMLPageSample.html
In the Properties Window, set Build Action = Embedded Resource
HTMLPageSample.html
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript' src='https://js.yoco.com/sdk/v1/yoco-sdk-web.js'></script>
<script type="text/javascript">
var sdk = new window.YocoSDK({
publicKey: 'pk_test_blahblah'
});
var inline = sdk.inline({
layout: 'field',
amountInCents: 2000,
currency: 'ZAR'
});
inline.mount('#card-frame');
</script>
<script type="text/javascript">
function ShowMessage() {
try {
//alert('in ShowMessage...');
var form = document.getElementById('payform');
var submitButton = document.getElementById('paybutton');
form.addEventListener('submit', function (event) {
event.preventDefault()
submitButton.disabled = true;
inline.createToken().then(function (result) {
submitButton.disabled = false;
if (result.error) {
const errorMessage = result.error.message;
errorMessage && alert('error occured: ' + errorMessage);
} else {
const token = result;
alert('card successfully tokenised: ' + token.id);
}
}).catch(function (error) {
submitButton.disabled = false;
alert('error occured: ' + error);
});
});
}
catch (err) {
alert(err.message);
}
};
</script>
</head>
<body>
<form id='payform' method='POST'>
<div class='one-liner'>
<div id='card-frame'>
</div>
<button id='paybutton' onclick='ShowMessage()'>
PAY ZAR 2.00
</button>
</div>
<p class='success-payment-message' />
</form>
</body>
</html>
Now, we'll need some code to read the embedded HTML file. We'll use code from here.
Create a class (name: HelperLoadResource.cs)
HelperLoadResource
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Diagnostics;
namespace WebView2SM
{
public static class HelperLoadResource
{
public static string ReadResource(string filename)
{
//use UTF8 encoding as the default encoding
return ReadResource(filename, Encoding.UTF8);
}
public static string ReadResource(string filename, Encoding fileEncoding)
{
string fqResourceName = string.Empty;
string result = string.Empty;
//get executing assembly
Assembly execAssembly = Assembly.GetExecutingAssembly();
//get resource names
string[] resourceNames = execAssembly.GetManifestResourceNames();
if (resourceNames != null && resourceNames.Length > 0)
{
foreach (string rName in resourceNames)
{
if (rName.EndsWith(filename))
{
//set value to 1st match
//if the same filename exists in different folders,
//the filename can be specified as <folder name>.<filename>
//or <namespace>.<folder name>.<filename>
fqResourceName = rName;
//exit loop
break;
}
}
//if not found, throw exception
if (String.IsNullOrEmpty(fqResourceName))
{
throw new Exception($"Resource '{filename}' not found.");
}
//get file text
using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
{
using (StreamReader reader = new StreamReader(s, fileEncoding))
{
//get text
result = reader.ReadToEnd();
}
}
}
return result;
}
}
}
Usage:
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
Change/Verify NuGet Package Manager Settings (optional for .NET Framework; required for .NET)
In VS menu, click Tools
Select Options
Double-click NuGet Package Manager
Under "Package Management", set Default package management format: PackageReference
Click OK
See the following for more information:
Package references (PackageReference) in project files
Migrate from packages.config to PackageReference
Add WebView2 NuGet package
In Solution Explorer, right-click <solution name> (ex: WebView2SM)
Select Manage NuGet packages...
Click Browse
Optional: check Include prerelease box next to the search box
In search box, type: Microsoft.Web.WebView2
Select the desired version
Click Install
If you see a pop-up, click OK
Note: To add WebView2 to just the project, instead of the solution, right-click <project name> instead of <solution name>.
Next, we'll work on our Form (name: Form1).
In Solution Explorer, right-click Form1.cs
Select View Designer
Open the Toolbox
In the VS menu, click View
Select Toolbox
Add WebView2 to Form
In Toolbox, click on WebView2 Windows Control Form to expand it
Click on WebView2 and drag it on top of the Form
Resize the WebView2 control as desired
Add Button to Form
In Toolbox, click on Button and drag it on top of the form
In the Properties Window, rename the button (name: btnSubmit)
In the Properties Window, click
Double-click Click, to add the event handler
Add Load event Handler to Form
In the Properties Window, click
Double-click Load, to add the event handler
Add CoreWebView2InitializationCompleted event handler
In Solution Explorer, right-click Form1.cs
Select View Designer
In Properties Window, click
Click on the drop-down and select the WebView2 control (ex: webView21)
Double-click CoreWebView2InitializationCompleted
Optional: double-click NavigationCompleted (repeat for any other desired event handlers)
Now, it's we'll work on the code for the Form.
In Solution Explorer, right-click Form1.cs
Select View Code
For testing, we'll add a method named LogMsg.
private void LogMsg(string msg)
{
string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
System.Diagnostics.Debug.WriteLine(logMsg);
}
This method can be modified as desired. If desired, one could write the information to a log file. If you already have a method for logging, you can use that instead. I've included this one, because it's used in the code.
In order to set the desired location for the web cache that will be created, we'll explicitly initialize WebView2. We'll call it InitializeCoreWebView2Async. We'll also create a method that can be used to add code using AddScriptToExecuteOnDocumentCreatedAsync.
Note: It's necessary to use async when using await. Notice the use of Task instead of void. If void is used, execution will continue without waiting.
AddScriptToExecuteOnDocumentCreatedAsync
private async Task AddExecuteOnDocumentCreatedAsyncCode()
{
if (webView21 != null && webView21.CoreWebView2 != null)
{
string jsCode = string.Empty;
//ToDo: add desired code using 'AddScriptToExecuteOnDocumentCreatedAsync'
if (!String.IsNullOrEmpty(jsCode))
{
await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode);
}
}
}
InitializeCoreWebView2Async
public async Task InitializeCoreWebView2Async(WebView2 wv, string webCacheDir = "")
{
CoreWebView2EnvironmentOptions options = null;
string tempWebCacheDir = string.Empty;
CoreWebView2Environment webView2Environment = null;
//set value
tempWebCacheDir = webCacheDir;
if (String.IsNullOrEmpty(tempWebCacheDir))
{
//use temp folder
//get fully-qualified path to user's temp folder
tempWebCacheDir = System.IO.Path.GetTempPath();
//create a randomly named folder - this will create a new web cache folder each time
//creating a new web cache folder takes time. By re-using an existing web cache,
//the load time will be shorter. However, one may need to manage (clean-up)
//objects in the web cache that are no longer needed
//tempWebCacheDir = System.IO.Path.Combine(tempWebCacheDir, System.Guid.NewGuid().ToString("N"));
}
//webView2Environment = await CoreWebView2Environment.CreateAsync(#"C:\Program Files (x86)\Microsoft\Edge Dev\Application\1.0.902.49", tempWebCacheDir, options);
webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempWebCacheDir, options);
//wait for CoreWebView2 initialization
await wv.EnsureCoreWebView2Async(webView2Environment);
//add desired code using AddScriptToExecuteOnDocumentCreatedAsync
await AddExecuteOnDocumentCreatedAsyncCode();
LogMsg("Info: Cache data folder set to: " + tempWebCacheDir);
}
Usage:
await InitializeCoreWebView2Async(webView21);
Now in Form1_Load, add the following code:
private async void Form1_Load(object sender, EventArgs e)
{
//initialize
await InitializeCoreWebView2Async(webView21);
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
webView21.NavigateToString(html);
}
Once WebView2 initialization is completed, we'll add any desired event handlers for CoreWebView2. We'll add them in CoreWebView2InitializationCompleted.
private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
{
//subscribe to events (add event handlers)
webView21.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
}
Note: Don't place any code in the event handler that could cause "blocking".
As stated in the OP, when the button is click it's desired that the ShowMessage() javascript function be called. Due to the way the JavaScript function is written, we'll do the following:
var result = await webView21.CoreWebView2.ExecuteScriptAsync("document.getElementById('paybutton').click();");
Note: While the following will also call ShowMessage(),
var result = await webView21.CoreWebView2.ExecuteScriptAsync("ShowMessage();");
it won't give the desired result due to form.addEventListener('submit'... which requires a "click".
Here's the full code for Form1.cs.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
namespace WebView2SM
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
//initialize
await InitializeCoreWebView2Async(webView21);
string html = HelperLoadResource.ReadResource("HTMLPageSample.html");
webView21.NavigateToString(html);
}
private async Task AddExecuteOnDocumentCreatedAsyncCode()
{
if (webView21 != null && webView21.CoreWebView2 != null)
{
string jsCode = string.Empty;
//ToDo: add desired code using 'AddScriptToExecuteOnDocumentCreatedAsync'
if (!String.IsNullOrEmpty(jsCode))
{
await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(jsCode);
}
}
}
private async Task InitializeCoreWebView2Async()
{
//initialize CorewWebView2
await webView21.EnsureCoreWebView2Async();
//add desired code using AddScriptToExecuteOnDocumentCreatedAsync
await AddExecuteOnDocumentCreatedAsyncCode();
}
public async Task InitializeCoreWebView2Async(WebView2 wv, string webCacheDir = "")
{
CoreWebView2EnvironmentOptions options = null;
string tempWebCacheDir = string.Empty;
CoreWebView2Environment webView2Environment = null;
//set value
tempWebCacheDir = webCacheDir;
if (String.IsNullOrEmpty(tempWebCacheDir))
{
//use temp folder
//get fully-qualified path to user's temp folder
tempWebCacheDir = System.IO.Path.GetTempPath();
//create a randomly named folder - this will create a new web cache folder each time
//creating a new web cache folder takes time. By re-using an existing web cache,
//the load time will be shorter. However, one may need to manage (clean-up)
//objects in the web cache that are no longer needed
//tempWebCacheDir = System.IO.Path.Combine(tempWebCacheDir, System.Guid.NewGuid().ToString("N"));
}
//webView2Environment = await CoreWebView2Environment.CreateAsync(#"C:\Program Files (x86)\Microsoft\Edge Dev\Application\1.0.902.49", tempWebCacheDir, options);
webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempWebCacheDir, options);
//wait for CoreWebView2 initialization
await wv.EnsureCoreWebView2Async(webView2Environment);
//add desired code using AddScriptToExecuteOnDocumentCreatedAsync
await AddExecuteOnDocumentCreatedAsyncCode();
LogMsg("Info: Cache data folder set to: " + tempWebCacheDir);
}
private void LogMsg(string msg)
{
string logMsg = String.Format("{0} {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:fff"), msg);
System.Diagnostics.Debug.WriteLine(logMsg);
}
private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
{
//subscribe to events (add event handlers)
webView21.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
}
private void CoreWebView2_DOMContentLoaded(object sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs e)
{
}
private void webView21_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
}
private async void btnSubmit_Click(object sender, EventArgs e)
{
//var result = await webView21.CoreWebView2.ExecuteScriptAsync("ShowMessage();");
var result = await webView21.CoreWebView2.ExecuteScriptAsync("document.getElementById('paybutton').click();");
}
}
}
Resources
Introduction to Microsoft Edge WebView2
Release notes for WebView2 SDK
Understand WebView2 SDK versions
Report An Issue With WebView2
Distribution of apps using WebView2
Get started with WebView2 in Windows Forms
Download the WebView2 Runtime
What is .NET Framework?
Introduction to .NET
.NET Core/5+ vs. .NET Framework for server apps

Get the image as file in angular

I am making angular application with image upload option which has the,
Html :
<label class="hoverable" for="fileInput">
<img [src]="url ? url : avatarImage">
<div class="hover-text">Choose file</div>
<div class="background"></div>
</label>
<br/>
<input id="fileInput" type='file' (change)="onSelectFile($event)">
<button *ngIf="url" (click)="delete()" >delete</button>
<img (click)="uploadPersonaImage($event)" class="avatar-images" src="https://www.w3schools.com/howto/img_avatar.png">
<img (click)="uploadPersonaImage($event)" class="avatar-images" src="https://www.w3schools.com/howto/img_avatar2.png">
Here what i am having is if the user clicks over the image he can select and update whatever image he has in local.
Same way if the user was not interested to update the profile image but interested to select any of the avatar image as per his/her wish which i have given like,
<img (click)="uploadPersonaImage($event)" class="avatar-images" src="https://www.w3schools.com/howto/img_avatar.png">
<img (click)="uploadPersonaImage($event)" class="avatar-images" src="https://www.w3schools.com/howto/img_avatar2.png">
And in ts made something like this,
uploadPersonaImage(e) {
this.url = e.target.src;
}
So on the click function the src that comes from the event.target was set to this.url..
But i need to convert it as file.. Because i need to send it as file to the service call so i need to update the avatar image.
So please help me to convert the avatar image selected/clicked by the user to the file/formdata so that it can be sent to the service as file format and can be updated as user selected image..
Example: https://stackblitz.com/edit/angular-file-upload-preview-85v9bg
You can use FormData to attach the read file and send to the API.
onSelectFile(event) {
if (event.target.files && event.target.files[0]) {
this.uploadToServer(event.target.files[0]);
... rest of the code
}
uploadToServer(file) {
let formData: FormData = new FormData();
formData.append('fileName', file);
// call your api service to send it to server, send formData
}
EDIT:
Try this out if you have no option to touch onSelectFile() or trigger a different function when you upload the file.
_url = ''
set url(val) {
this._url = val;
if (val) {
this.dataURLtoFile(val);
}
}
get url() {
return this._url;
}
uploadedImage: File ;
dataURLtoFile(dataurl) {
const arr = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const imageExtension = mime.split('/')[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
this.uploadedImage = new File([u8arr], `uploaded.${imageExtension}`);
}
On your API call, maybe when you click on a button,
uploadPersonaImage(e) {
// this.apiService.someMethod(this.uploadedImage);
}
If you want to trigger the API call just when you upload the image, add the code of dataURLtoFile() to uploadPersonaImage() and call uploadPersonaImage() from url setter
Clarification
Do you understand what does event.target.src mean (considering e as event)?
Here event means the click/change event you triggered when you
clicked onto upload photo.
event.target means the DOM element on which the event took place.
event.target.src will give you the src attribute value of the
DOM element on which you triggered the change event.
Now, you say won't it work? No, it won't because the element which you clicked is an HTMLInputElement but the src resides under the image in under the label tag. And how are you intending to call uploadPersonaImage()? what calls your method? You haven't answered that even after asking so many times.
In my last edit, I have added code under the setter of the url which will convert the dataUrlFile to an actual File, It completely depends on your server how you want to store the file. As a file or as a dataUrl? If you want to send it as a file then follow the conversions I added in the answer if you want to save as dataUrl then directly save the content of this.url on your API call.

How to access image file from HTML or Javascript for Windows Phone

I have a requirement in wp8, where the picture selected by the user needs to be shown in the browser. To browse and select the photo, I am using photo chooser task.
I am able to get the physical location of the selected image, but on passing the same to JavaScript from c# its not displaying the image.
On googling came across the following link How to access isolated storage file from HTML or Javascript for Windows Phone and PhoneGap Application But it did not solve my issue.
For reference, the location of the image I am using was:
C:\Data\Users\DefApps\AppData{FA586990-6E21-0130-BF9E-3C075409010C}\Local\sample_photo_00.jpg
This is my Javascript code:
function myPicture(data) {
document.getElementById("capturedImage").src = data.imageUri;
alert("data.imageUri " + document.getElementById("capturedImage").src );
var width = data.imageWidth;
var height = data.imageHeight;
alert("image width" + width );
alert("image height" + height );
}
And this is my C# code:
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
string[] picList = Directory.GetFiles(localFolder.Path, "*.jpg");
foreach (string DeleteFile in picList) {
File.Delete(DeleteFile);
}
StorageFile storageFile = await localFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (Stream outputStream = await storageFile.OpenStreamForWriteAsync())
{
await file.CopyToAsync(outputStream);
}
send (storageFile.Path);
Now send function should add MyHTML in picture.
You can call JavaScript function from C# by WebBrowser.InvokeScript and send image in args parameter. But args is string(s), so you will have to encode your image to string using some algorithm... Base64 for example:
string ImageToBase64String(Image image)
{
using (MemoryStream stream = new MemoryStream())
{
image.Save(stream, image.RawFormat);
return Convert.ToBase64String(stream.ToArray());
}
}
You will get some long string like this iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
On other side - in the JavaScript function you calling you will get that Base64 string and use it like this as src attribute of img element:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" />
More info about data uri scheme.
UPDATE: The easer solution. I think you can send your image path, width and height:
ImageProperties properties = await storageFile.Properties.GetImagePropertiesAsync();
webBrowser.InvokeScript("myPicture", storageFile.Path, (string)properties.Width, (string)properties.Height);
function myPicture(src, width, height) {
document.getElementById("capturedImage").src = src;
alert("data.imageUri " + document.getElementById("capturedImage").src );
alert("image width" + width );
alert("image height" + height );
}

fetch image from library and change image every 30 seconds in sharepoint?

I'm using web part and i write the code below
but it fetch only one image >>> how can i fetch all image from the library and change image every 30 seconds using javascript or jquery??
public class MSDN : System.Web.UI.WebControls.WebParts.WebPart
{
Image myimage = new Image();
protected override void CreateChildControls()
{
myimage.Height = 140;
myimage.Width =999;
SPSite mysite = SPContext.Current.Site;
SPWeb myweb = SPContext.Current.Web;
SPList mylist = myweb.Lists["Pic Lib"];
SPQuery myquery = new SPQuery();
myquery.Query = "<OrderBy><FieldRef Name='FileLeafRef' />"+
"<FieldRef Name='Status' /></OrderBy>"+
"<Where><Eq><FieldRef Name='Status' />"+
"<Value Type='Choice'>Active</Value></Eq></Where>";
string serverpath = mysite.ServerRelativeUrl.ToString();
SPListItemCollection mylistitem = mylist.GetItems(myquery);
if (mylistitem.Count > 0)
{
myimage.ImageUrl = serverpath + mylistitem[mylistitem.Count - 1].Url.ToString();
}
else
{
this.Page.Response.Write("No image found");
}
base.CreateChildControls();
}
protected override void Render(HtmlTextWriter writer)
{
myimage.RenderControl(writer);
}
}
}
You can use the SharePoint Client Object Model MSDN link to query the list and get the image urls, store it in a javascript array
Then use any jquery plugin (like SlidesJS.. the first link on google) or write your own to flip the images every 30 seconds.

Enlarge image on click of link

#foreach (var item in Model)
{
<img src='ShowShowcaseImage/#Html.Encode(item.ProductID)' id='#item.ProductID' />
<b>#Html.DisplayFor(m => item.ProductName)</b>
Enlarge
}
<div id="EnlargeContent" class="content">
<span class="button bClose"><span>X</span></span>
<div style="margin: 10px;" id="imageContent">
</div>
<p align="center"></p>
</div>
//Popup javascript
$('.enlargeImg').bind('click', function (e) {
$.post('/Home/EnlargeShowcaseImage/' + $(this).attr('id'), null, function (data) {
document.getElementById("imageContent").innerHTML += data;
});
$('#EnlargeContent').bPopup();
});
});
//
C# method
public ActionResult EnlargeShowcaseImage(string id)
{
var imageData = //linq query for retrive bytes from database;
StringBuilder builder = new StringBuilder();
if (imageData != null)
builder.Append("<img src='" + imageData.ImageBytes + "' />");
return Json(builder);
}
I want to show pop up of enlarged image on click of enlarge link. Image is stored in bytes in database. Two images are stored in database for each product - one is thumbnail and the other is enlarged. I am showing thumbnail image and I want to show enlarged image on click of enlarge link. I can't retrieve it from database.
I can't retrieve it from database
So your question is about retrieving an image from a database, right? It has strictly nothing to do with ASP.NET MVC?
Unfortunately you haven't told us whether you are using some ORM framework to access to your database or using plain ADO.NET. Let's assume that you are using plain ADO.NET:
public byte[] GetImage(string id)
{
using (var conn = new SqlConnection("YOUR CONNECTION STRING COMES HERE"))
using (var cmd = conn.CreateCommand())
{
conn.Open();
// TODO: replace the imageData and id columns and tableName with your actual
// database table names
cmd.CommandText = "SELECT imageData FROM tableName WHERE id = #id";
cmd.Parameters.AddWithValue("#id", id);
using (var reader = cmd.ExecuteReader())
{
if (!reader.Read())
{
// there was no corresponding record found in the database
return null;
}
const int CHUNK_SIZE = 2 * 1024;
byte[] buffer = new byte[CHUNK_SIZE];
long bytesRead;
long fieldOffset = 0;
using (var stream = new MemoryStream())
{
while ((bytesRead = reader.GetBytes(reader.GetOrdinal("imageData"), fieldOffset, buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, (int)bytesRead);
fieldOffset += bytesRead;
}
return stream.ToArray();
}
}
}
}
and if you are using some ORM it could be as simple as:
public byte[] GetImage(string id)
{
using (var db = new SomeDataContext())
{
return db.Images.FirstOrDefault(x => x.Id == id).ImageData;
}
}
and then inside your controller action:
public ActionResult EnlargeShowcaseImage(string id)
{
var imageData = GetImage(id);
if (imageData != null)
{
// TODO: adjust the MIME Type of the images
return File(imageData, "image/png");
}
return new HttpNotFoundResult();
}
and it is inside your view that you should create an <img> tag pointing to this controller action upon button click:
$('.enlargeImg').bind('click', function (e) {
$('#imageContent').html(
$('<img/>', {
src: '/Home/EnlargeShowcaseImage/' + $(this).attr('id')
})
);
$('#EnlargeContent').bPopup();
});
But hardcoding the url to your controller action in javascript like this is very bad practice because when you deploy your application it might break. It might also break if you decide to change the pattern of your routes. You should never hardcode urls like this. I would recommend you generating this url on the server.
For example I see that you have subscribed to some .enlargeImage element. Let's suppose that this is an anchor. Here's how to properly generate it:
#Html.ActionLink("Enlarge", "EnlargeShowcaseImage", "Home", new { id = item.Id }, new { #class = "enlargeImage" })
and then adapt the click handler:
$('.enlargeImg').bind('click', function (e) {
// Cancel the default action of the anchor
e.preventDefault();
$('#imageContent').html(
$('<img/>', {
src: this.href
})
);
$('#EnlargeContent').bPopup();
});
Try jQuery,
Here is one
http://superdit.com/2011/06/11/hover-image-zoom-with-jquery/
http://demo.superdit.com/jquery/zoom_hover/

Categories