I have a webview inside a uwp application and i need to inject some javascript files into it. I can't include them in the source code of the html page.
This is what I have so far.
private async void WebView_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
await Webview.InvokeScriptAsync("eval", new string[]
{
"var script = document.createElement('script'); script.type='text/javascript'; script.charset='UTF-8'; script.src ="
+ "'ms-appx://Apps.Webapp/JS/jquery-3.2.1.min.js'"
+ ";document.body.appendChild(script);"
});
}
But when i try with the direct invocation the files are simple not loaded
Anyone has any thoughts that can help me?
Edit: I have tried with no luck
"'ms-appx-web://Apps.Webapp/JS/jquery-3.2.1.min.js'"
To load file from the your app package in the WebView you need to use ms-appx-web scheme, not ms-appx. For example:
<script type="text/javascript" src="ms-appx-web:///JS/jquery-3.2.1.min.js"></script>
More details please reference the "Navigating to content section" of WebView class.
After some trial and error I got a working solution:
private async void WebView_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
string scriptToLoad = File.ReadAllText("Apps.Webapp/JS/jquery-3.2.1.min.js");
string[] arguments = { scriptToLoad };
await WebBrowserWizzio.InvokeScriptAsync("eval", arguments);
}
This way i'm injecting the whole script into the page and not only the path.
Related
I am working on a cefsharp based browser and i am trying to implement a search engine into the browser, but the code I have tried docent work, it doesn't really have any errors but when i star the project and type something i the text field nothing happens and it dosent load the search engine i entered into the code, the only time the textbox loads anything is when a url is typed.
This is the code used in the browser that docent work
private void LoadUrl(string url)
{
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
{
WebUI.Load(url);
}
else
{
var searchUrl = "https://www.google.com/search?q=" + WebUtility.HtmlEncode(url);
WebUI.Load(searchUrl);
}
}
i have also tried
void LoadURl(String url)
{
if (url.StartsWith("http"))
{
WebUI.Load(url);
}
else
{
WebUI.Load(url);
}
}
i was also suggested to try
private void LoadUrl(string url)
{
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
{
WebUI.LoadUrl(url);
}
else
{
var searchUrl = "https://www.google.com/search?q=" + Uri.EscapeDataString(url);
WebUI.LoadUrl(searchUrl);
}
}
We have here really few Information on how your code works. But what I notice is that you use WebUtility.HtmlEncode for the search query. WebUtility has also a WebUtility.UrlEncode Method, that how I understand your question makes more sense it the context. This is the documentation for the method: https://learn.microsoft.com/de-de/dotnet/api/system.net.webutility.urlencode
The Url you are generating is invalid. You need to use Uri.EscapeDataString to convert the url param into a string that can be appended to a url.
// For this example we check if a well formed absolute Uri was provided
// and load that Url, all others will be loaded using the search engine
// e.g. https://github.com will load directly, attempting to load
// github.com will load the search engine with github.com as the query.
//
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
chromiumWebBrowser.LoadUrl(url);
}
else
{
var searchUrl = "https://www.google.com/search?q=" + Uri.EscapeDataString(url);
chromiumWebBrowser.LoadUrl(searchUrl);
}
nothing happens and it dosent load the search engine
You need to subscribe to the LoadError event to get actual error messages. It's up to you to display errors to the user. The following is a basic example:
chromiumWebBrowser.LoadError += OnChromiumWebBrowserLoadError;
private void OnChromiumWebBrowserLoadError(object sender, LoadErrorEventArgs e)
{
//Actions that trigger a download will raise an aborted error.
//Aborted is generally safe to ignore
if (e.ErrorCode == CefErrorCode.Aborted)
{
return;
}
var errorHtml = string.Format("<html><body><h2>Failed to load URL {0} with error {1} ({2}).</h2></body></html>",
e.FailedUrl, e.ErrorText, e.ErrorCode);
_ = e.Browser.SetMainFrameDocumentContentAsync(errorHtml);
}
For testing purposes you can also copy and paste the searchUrl string you've generated and try loading it in Chrome to see what happens, you should also get an error.
I'm facing an issue with uploading a file via the Drag'n Drop API.
Here the following blazor component:
<InputTextArea
#ondrop="HandleDrop"
#ondragenter="HandleDragEnter"
#ondragleave="HandleDragLeave"/>
</InputTextArea>
#code {
private async Task HandleDrop(DragEventArgs args)
{
var files = args.DataTransfer.Files;
// Do something to upload the file and get the content
}
I want to upload the file and display it in the textarea. Now since .NET6 the DragEventArgs will list all files (if any) associated with the Drag'n Drop event.
Natively there seems to be no way to get the content of those files.
Therefore I tried to achieve this with JavaScript interop:
private async Task HandleDrop(DragEventArgs args)
{
var content = await jsRuntime.InvokeAsync<string>("getContentFromFile", args);
}
With the following JavaScript function (which is referenced in the _Layout.cshtml):
async function getContentFromFile(args) {
// Use some API here? File Upload API? Didn't work as args is only a data object and not the "real" DragEventArgs from JavaScript
// I also tried FileReader
const fileName = args.Files[0]; // Let's assume we always have one file
let content = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(fileName);
});
console.log(content);
return content;
}
This approach let to other issues and exceptions, like that the FileReader threw:
parameter is not of type 'Blob'
Is this with this approach with the current version of blazor (.net6) possible at all? Any tips are welcome.
Is there a way to extract the user-agent string that the WebView control uses? If so, I would greatly appreciate it if anyone can give me a method to do so. Using the following does not seem to work:
var userAgent = new StringBuilder(256);
int length = 0;
UrlMkGetSessionOption(UrlMonOptionUserAgent, userAgent, userAgent.Capacity - 1, ref length, 0);
I take that back, using UrlMkGetSessionOption as mentioned in the code above does work.
I currently use this method, adapted from a method given for windows phone originally. It gives the correct result, and gets it straight from a real instance of a WebView object, so gives me more confidence in it having the correct value.
private static string s_userAgent = null;
// Get the default UserAgent which webviews use on this platform.
public async Task<string> GetUserAgent()
{
if (s_userAgent == null)
{
const string Html = #"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01 Transitional//EN"">
<html>
<head>
<script language=""javascript"" type=""text/javascript"">
function notifyUA() { window.external.notify(navigator.userAgent); }
</script>
</head>
<body onload=""notifyUA();""></body>
</html>";
SemaphoreSlim signal_done = new SemaphoreSlim(0, 1);
var wv = new WebView();
wv.ScriptNotify += (sender, args) =>
{
s_userAgent = args.Value;
// set signal, to show we've done
signal_done.Release();
};
wv.NavigateToString(Html);
// wait for signal
await signal_done.WaitAsync();
Debug.WriteLine("GetUserAgent() called. User agent from WebView: \n{0}", s_userAgent);
}
return s_userAgent;
}
This started as a comment but became too long. To expand on his (#Rexfelis) own answer:
I've found that there can be a difference in what UrlMkGetSessionOption returns depending on where you are in the application lifecycle and if a WebView has been initialized yet in a XAML view.
If you call it before component initialization, it will be missing WebView/3.0 (at least in Windows 10); after initialization it will have that text and results in the same string as the answer by #SimonTillson.
If you need to know the right user agent before component initialization, you have to new up a WebView and navigate before querying UrlMkGetSessionOption; e.g. var wv = new WebView(); wv.NavigateToString(...);. It seems that the user agent is modified on first navigation to include WebView/3.0.
So, I am dynamically generating jQuery using C# and sending it to the webpage.
The problem is it appears to be generating correct jQuery according to the file and according to Js Fiddle but it does not actually work on the page.
The jsFiddle is here http://jsfiddle.net/ER2hE/
Now I looked up how to send javacript to the website. It should work like this.
http://msdn.microsoft.com/en-us/library/bb359558.aspx
and my code which does that is this method
private void sendScript(string script)
{
const string someScript = "alertMe";
//send the built script to the website.
ScriptManager.RegisterStartupScript(this.Page, this.GetType(), someScript, script, true);
}
This is super simple it has worked for other pieces of code calling. But it has not for this instance.
The code that calls it is this in my C#
private void populateGroups()
{
//this generates correct javascript according to the file and JS fiddle but unfortunately doees not work.
string splitme = "USE ACES SELECT GroupName, GroupID FROM PrimaryGroup ORDER BY GroupName";
DataTable dt = fillDataTable(splitme);
string script = "";
foreach (DataRow dr in dt.Rows)
{
//add the locations to the <select> box
script += " $('#groupList').append('<option value=\" " + dr.ItemArray[1].ToString() + " \"> " + dr.ItemArray[0].ToString() + " </option>'); ";
}
sendScript(script);
JSErrorLog(script, "GROUPS");
}
The whole thing is being called on startup
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack == false)
{
populateMakes();
populateLocation();
populateGroups();
}
}
The jQuery its generating also works in JSFiddle I am pulling this from a method that writes the javascript it generates in a method calling here is the fiddle JSErrorLog.
http://jsfiddle.net/ER2hE/
Oh and my html in my aspx file looks like this
<div class="row2">
<span>Group</span>
<select id="groupList" multiple="multiple" onclick="setGroups()" class="normalsize">
</select>
</div>
I believe that is everything. I just want my stuff to work. I am willing to post any additional code, just ask. If you have an idea as to why its not working, let me know.
When does it actually execute that code? Before or after the element with id "groupList" exists in the DOM? My guess is before.
Solution? Wrap your code inside a document.ready handler.
jQuery(function($) {
$('#groupList').append('<option value=" 46 "> AC Units </option>');
// etc etc
});
Return simple string js code. And run it with eval()
Edit: Sorry - now that I've understood the problem a bit better, I think my problem lies elsewhere
I have 2 asynchronus requests.
The first is this:
public void DownloadWebData(Uri apiUrl)
{
WebClient client = new WebClient();
client.DownloadDataCompleted += DownloadDataCompleted;
client.DownloadDataAsync(apiUrl);
}
public void DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
string result = System.Text.Encoding.UTF8.GetString (e.Result);
Uri downloadLink = (GetUri(result));
}
Basically it makes a simple url based API request to a remote webserver which returns some basic textual data over http. GetUri() just parses that data to extract an address from the data for an image to download.
I'm then using imageLoader in monotouch.dialog to download the image. All code is in the same class.
Edit: added the imageLoader code (I left the Console lines in because they serve reasonably well as comments).
public void downloadImage (Uri imageUri)
{
var tmp_img = ImageLoader.DefaultRequestImage (imageUri, this);
if (tmp_img != null)
{
adView.Image = tmp_img;
Console.WriteLine ("Image already cached, displaying");
}
else
{
adView.Image = UIImage.FromFile ("Images/downloading.jpg");
Console.WriteLine ("Image not cached. Using placeholder.");
}
}
public void UpdatedImage (System.Uri uri)
{
adView.Image = ImageLoader.DefaultRequestImage(uri, this);
}
You missed to check if e.Result actually contains something. The download might as well have failed and e.Result is null. Add some basic error handling to your code.
if you are using DownloadWebData inside a for loop, it will be better you generate seperate functions for DownloadDataCompleted event.
You can use anonymous function inside DownloadWebData().
client.DownloadDataCompleted +=(s,e)=>{
string result = System.Text.Encoding.UTF8.GetString (e.Result);
Uri downloadLink = (GetUri(result));
};
After realizing I was asking the wrong question, I finally figured it out here:
Hand back control to main UI thread to update UI after asynchronus image download