I want to get the HTML from a WebView page, so I make use of the EvaluateJavaScriptAsync function to get the body. However, this throws an NRE, probably because I am fetching the document before the page has loaded. Hence, I add a lambda expression to the Navigated
event.
var wv = new WebView();
wv.Source = "https://www.bbc.com/news";
wv.Navigated += async (s, e) =>
{
var x = await wv.EvaluateJavaScriptAsync("document.body.innerHTML");
Debugger.Break();
};
However, the execution never breaks, i.e. the Navigated event is not getting fired. I've tried other events, and realised the Navigating event also never fires. This doesn't work for any other website as well. Any idea of what's going on?
Alternatively, is there any other way to do dynamic web scraping in Xamarin that wouldn't be too computationally expensive on mobile? Thanks.
Here is a demo about getting the html from webview page by using CoreWebView2.ExecuteScriptAsync().
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.webView2.Source = new System.Uri(textBox1.Text, System.UriKind.Absolute);
}
private async void webView2_NavigationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs e)
{
object obj = await webView2.CoreWebView2.ExecuteScriptAsync("document.body.innerHTML");
textBox2.Text = obj.ToString();
}
private async void button2_Click(object sender, EventArgs e)
{
object obj = await webView2.CoreWebView2.ExecuteScriptAsync("document.body.innerHTML");
textBox2.Text = obj.ToString();
}
private async void button3_Click(object sender, EventArgs e)
{
object obj = await webView2.CoreWebView2.ExecuteScriptAsync("document.body.innerText");
textBox2.Text = obj.ToString();
}
private async void button4_Click(object sender, EventArgs e)
{
object obj = await webView2.CoreWebView2.ExecuteScriptAsync("$('.f3')[0].innerHTML");
textBox2.Text = obj.ToString();
}
Related
CefSharp Version: 86.0.241.0
.Net Framework 4.5.2
I am new to C# and my English is not good. Sorry....
Try a variety of methods to implement:
private void button18_Click(object sender, EventArgs e)
{
//1
DevToolsClient devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
devTool.ExecuteDevToolsMethodAsync("Browser.getVersion").Wait();// stuck
//2
DevToolsClient dev = DevToolsExtensions.GetDevToolsClient(chromiumWebBrowser1);
dev.ExecuteDevToolsMethodAsync("Browser.getVersion").Wait();// stuck
}
// load complete after:
chromiumWebBrowser1.FrameLoadEnd += new EventHandler<FrameLoadEndEventArgs>(new Action<object, FrameLoadEndEventArgs>((s, ea) => {
IBrowser browser = chromiumWebBrowser1.GetBrowser();
DevToolsClient devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
//devTool.Browser.GetVersionAsync().Wait(); //execute nomal
devTool.DOM.GetDocumentAsync().Wait(); // stuck
}));
//
private void button1_Click(object sender, EventArgs e)
{
DevToolsClient devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
//devTool.Page.ReloadAsync(); //nomal page reload
devTool.Page.ReloadAsync().Wait(); // stuck
}
In addition to being successful Browser.GetVersionAsync() in FrameLoadEnd event .
A solution has been found! use async/await solve.
private async void button1_Click(object sender, EventArgs e)
{
DevToolsClient devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
DevToolsMethodResponse resp = await devTool.ExecuteDevToolsMethodAsync("DOM.getDocument");
Console.WriteLine(resp.ResponseAsJsonString);
}
The final solution:
//global variable
DevToolsClient devTool = null;
//Should be obtained at initialization time DevToolsClient,And only get it once
//Each call to GetDevToolsClient() messageId resets
chromiumWebBrowser1.IsBrowserInitializedChanged += new EventHandler(delegate {
devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
});
//Common execution method method: CDP method name ,param: method param
public async Task<string> ExecuteDevToolsMethods(string method, IDictionary<string, object> param = null)
{
DevToolsMethodResponse resp = await devTool.ExecuteDevToolsMethodAsync(method, param);
return resp.ResponseAsJsonString;
}
//call
private void button3_Click(object sender, EventArgs e)
{
ExecuteDevToolsMethods("DOM.getDocument").ContinueWith(new Action<Task<string>>((result) => {
Console.WriteLine(result.Result);
}));
}
if Please point out if I have misunderstood,very thank!
could please help with that. I can not subscribe for event fired by an object from another Task.
Note: If i took the code outside the Task, it works fine. you can see the commented code
private void Form1_Load(object sender, EventArgs e)
{
_machines.Add(new Machine() { Ip = "192.168.1.10", Name = "Machine_01", Status = "Connected" });
dgvMachines.DataSource = _machines;
}
private void startLiveMonitoringToolStripMenuItem_Click(object sender, EventArgs e)
{
//if (_machines.Count <= 0) return;
//_fpMachine = new ZktFingerPrint(_machines[0].Ip, _machines[0].Name);
//_fpMachine.Connect();
//_fpMachine.EnableLiveFingerPrintDetection();
//_fpMachine.NewFingerPrintDetected += FpMachine_NewFingerPrintDetected;
foreach (var machine in _machines)
{
Task.Factory.StartNew(() =>
{
IFingerPrintMachine fpMachine = new ZktFingerPrint(machine.Ip, machine.Name);
fpMachine.Connect();
fpMachine.EnableLiveFingerPrintDetection();
fpMachine.NewFingerPrintDetected += FpMachine_NewFingerPrintDetected;
});
}
}
private void FpMachine_NewFingerPrintDetected(object sender, FingerPrintEventArgs e)
{
var fpr = new FingerPrintRecord(e.FingerPrintDateTime, e.UserId);
dgvMonitor.Rows.Add("Machine_01", fpr.FingerPrintDateTime, fpr.UserSn);
}
My problem is that I can get from page_load to show_books function
but I cant get from show_books to book_detail.
Every time I try to fire book_details it gets me to page_load.
my code is this:
protected void Page_Load(object sender, EventArgs e)
{
//Here I create dynamic linkbuttons that calls datashow
lnk_button.Command += new CommandEventHandler(book_show);
}
protected void book_show(object sender, EventArgs e)
{
//Here I show all books from a category
//and I create dynamic linkbuttons that calls book_details
book_button.Command += new CommandEventHandler(book_details);
}
protected void book_details(object sender, EventArgs e)
{
//and Here I show the details of each book
}
I'm sorry if my question is annoyingly newbie
but I would like some help .
I just started to learn asp.net
well i don't understand why you need to do this,but code below works,hope it helps
protected void Page_Load(object sender, EventArgs e)
{
//Here I create dynamic linkbuttons that calls datashow
lnk_button.Command += new CommandEventHandler(this.book_show);
lnk_button.CommandName = "testClick";
lnk_button.CommandArgument = "1";
lnk_button.ID = "11";
lnk_button.Text = "blah";
book_button.Command += new CommandEventHandler(this.book_details);
book_button.CommandName = "testClick2";
book_button.CommandArgument = "2";
book_button.ID = "22";
book_button.Text = "blah2";
}
protected void book_show(object sender, EventArgs e)
{
//Here I show all books from a category
//and I create dynamic linkbuttons that calls book_details
book_button.Command += new CommandEventHandler(this.book_details);
book_button.CommandName = "testClick2";
book_button.CommandArgument = "2";
book_button.ID = "22";
book_button.Text = "blah2";
}
protected void book_details(object sender, EventArgs e)
{
//and Here I show the details of each book
}
public void book_show()
{
book_details() ;
}
public void book_details()
{
}
I'm trying to find a way to get the string name of the method call which pops up a new window. I have three button click event handlers which will open the new window but I need to know which called the .Show();
private void buttonSettingsPortfolio1_Click(object sender, RoutedEventArgs e)
{
var settingsWindow = new MobilityPortfolioSettings();
settingsWindow.Show();
}
private void buttonSettingsPortfolio2_Click(object sender, RoutedEventArgs e)
{
var settingsWindow = new MobilityPortfolioSettings();
settingsWindow.Show();
}
private void buttonSettingsPortfolio3_Click(object sender, RoutedEventArgs e)
{
var settingsWindow = new MobilityPortfolioSettings();
settingsWindow.Show();
}
I don't want to have to have three separate windows! is there an opening event handler parameter which I can fetch the caller from?
well, you can simply add a public variable at MobilityPortfolioSettings class and set its value in each method, ex: in buttonSettingsPortfolio1_Click add MobilityPortfolioSettings.Variable = 1 and so on.
Here
Console.write(triggeredBy); you can output the value by logging to file or some other way . This value will indicate which path your code took.
private void buttonSettingsPortfolio1_Click(object sender, RoutedEventArgs e)
{
Open("buttonSettingsPortfolio1_Click");
}
private void buttonSettingsPortfolio2_Click(object sender, RoutedEventArgs e)
{
Open("buttonSettingsPortfolio2_Click");
}
private void buttonSettingsPortfolio3_Click(object sender, RoutedEventArgs e)
{
Open("buttonSettingsPortfolio3_Click");
}
private Open(string triggeredBy){
Console.write(triggeredBy); // You can write to file or output in some different way here.
var settingsWindow = new MobilityPortfolioSettings();
settingsWindow.Show();
}
Try this:
Cast sender as button and then get it's name.
Change the MobilityPortfolioSettings constructor so that it needs a string parameter.
Pass the button name to the constructor.
private void buttonSettingsPortfolio1_Click(object sender, RoutedEventArgs e)
{
string buttonName = "";
if (sender is Button)
buttonName = ((Button)sender).Name;
Window settingsWindow = new MobilityPortfolioSettings(buttonName);
settingsWindow.Show();
}
BTW use Window as variable type instead of var.
Cheers
That's the code:
private void button1_Click(object sender, EventArgs e)
{
ParaClass pcs = new ParaClass();
pcs.strPath = textBox1.Text;
pcs.sendedGrid = ugSrc;
this.backgroundWorker1.RunWorkerAsync(pcs);
ParaClass pcsB = new ParaClass();
pcsB.strPath = textBox2.Text;
pcsB.sendedGrid = ultraGrid2;
this.backgroundWorker2.RunWorkerAsync(pcsB);
doSomething();
}
and in both backgrandworker1 & backgrandworker2 ' complet event ,i write code like this:
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk1();
}
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
}
now the problem is : the function doSomething() in button1's click event must wait both backgrandworker's complete event finish.
if i change doSomething() to
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
doSomething();
}
then,because there are two thread,i don't know which thread will finish first,so what is the solution
Create 2 flags which represent complete state of 2 BackgroundWorker.
Turn each flag on in the RunWorkerCompleted event, then call doSomething() method.
In doSomething method, check if both flags is on, then continue to do, otherwise, return.
Create 2 AutoResetEvents, set them when each Background worker finishes and wait for them all in the main method with a WaitHandle.
WaitHandle[] handles = new WaitHandle[] { new AutoResetEvent(false), new AutoResetEvent(false)};
private void button1_Click(object sender, EventArgs e)
{
ParaClass pcs = new ParaClass();
pcs.strPath = textBox1.Text;
pcs.sendedGrid = ugSrc;
this.backgroundWorker1.RunWorkerAsync(pcs);
ParaClass pcsB = new ParaClass();
pcsB.strPath = textBox2.Text;
pcsB.sendedGrid = ultraGrid2;
this.backgroundWorker2.RunWorkerAsync(pcsB);
WaitHandle.WaitAll(this.handles);
doSomething();
}
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk1();
((AutoResetEvent)this.handles[0]).Set();
}
private void backgroundWorker2_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
doSomethingelsebk2();
((AutoResetEvent)this.handles[1]).Set();
}