CefSharp - Is it possible to save IFrame.ViewSource() into specific file name? - c#

I'm having problems with trying to save HTML source code into specific file. My goal is to save data that I get from "chromeBrowser.GetBrowser().MainFrame.ViewSource();" in "OnLoadingStateChanged" method into a file named "html.txt". Is it possible to do that?
public void InitializeChromiumWebBrowser()
{
CefSettings cefSettings = new CefSettings();
Cef.Initialize(cefSettings);
chromeBrowser = new ChromiumWebBrowser("https://google.com");
this.Controls.Add(chromeBrowser);
chromeBrowser.Dock = DockStyle.Fill;
chromeBrowser.LoadingStateChanged += OnLoadingStateChanged;
}
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (!args.IsLoading)
chromeBrowser.GetBrowser().MainFrame.ViewSource();
}
I need exactly this method that is used in given code.
I've tried getting HTML Code through WebClient, HTTPWebRequest, GetSourceAsync, but none of those solutions work for my case.

Related

Disable C# Form until external Excel application is closed

I have a button on a C# Windows forms application that runs the code below:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
string path = "D:\MyCsvFile.csv";
Process.Start(path);
}
Basically, the user needs to be able to make changes to a CSV file. The user is most comfortable making these changes in Microsoft Excel. I found the above code online from someone who has many years of experience working with MS Office products and C#. This guy explained that the code below works but is more error prone than using "Process.Start()" as shown above.
string path = "D:\MyCsvFile.csv";
var ExcelApp = new Excel.Application();
ExcelApp.Workbooks.OpenText(path, Comma: true);
ExcelApp.Visible = true;
This guy explains that "Process.Start()" works because Windows is set up to use Excel as the default program for opening CSV files.
Anyways, when the user clicks the button above (btnAddDataToCSV), I need my C# form to become disabled (all the buttons grayed out) until the user closes the Excel workbook that is displaying the CSV file. Potentially, I may need the C# program to read the CSV file immediately after the user closes Excel.
Ho do I do this?
I assume... I need to do something like... "disable C# form... then Wait for Excel close event... then once excel close event happens... enable C# form and read CSV file."
Below is the code I have for reading the CSV file:
string path = "D:\MyCsvFile.csv";
var reader = new StreamReader(File.OpenRead(path));
List<string> listA = new List<string>();
List<string> listB = new List<string>();
List<string> listC = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
listC.Add(values[2]);
}
I think that you need to use an event handler for process that exits.
Something like this:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
//disable form
string path = "D:\MyCsvFile.csv";
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.Start(path);
}
}
public void Excel_Exit(object sender, System.EventArgs e){
//enableForm
}
Process.Exited example in documentation: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I was unable to get the solution above to work even though the solution is similar to Microsoft's solution.
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.exited?view=netcore-3.1
I found this:
Why is my process's Exited method not being called?
It says:
"I've come across examples that place new Process() in a using clause. Do not do that if you want to use the Exited feature. The using clause destroys the instance along with any event handles on Exited."
It says:
using(var process = new Process())
Should be:
var process = new Process();
This makes no since because the link above that I provided for Microsoft is an example of how to use the "Process.Exited Event" and it uses "using(var process = new Process())"
But sure enough... for some reason when I tried "var process = new Process();" it worked... This is the code that worked:
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
var myProcess = new Process();
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
Here is the code that does not work... But I don't understand why because it is similar to Microsoft's example.
private Process myProcess;
private void btnAddDataToCSV_Click(object sender, EventArgs e)
{
using (myProcess = new Process())
{
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(Excel_Exit);
myProcess.StartInfo.FileName = "D:\\MyCsvFile.csv";
myProcess.Start();
}
}
public void Excel_Exit(object sender, System.EventArgs e)
{
MessageBox.Show("Success!!");
}
When you close excel... the event never fires... Can someone explain this? Surely Microsoft's example isn't wrong.

Can't load website in CefSharp

I'm trying to load a website URL using RegisterJsObject in one of two cefsharp instances. The URL is received from the website (chromeBrowser1) however It wont update the window (chromeBrowser2). I tried to document the script as much as possible so it would be more easy to understand. I'm pretty sure somebody with general knowledge in C# will find my problem, I spend hours trying to find a solution but I just couldn't.
private void Form1_Load(object sender, EventArgs e)
{
CefSettings settings = new CefSettings();
Cef.Initialize(settings);
chromeBrowser2 = new ChromiumWebBrowser("http://google.com") // Initializate Browser 2 FIRST ( This is the instance we want to update every X seconds using chromeBrowser2.Load("http://somewebsite.com"); later on )
{
Dock = DockStyle.Fill,
};
splitContainer1.Panel2.Controls.Add(chromeBrowser2);
chromeBrowser1 = new ChromiumWebBrowser("http://127.0.0.1/") // Initializate Browser 1 - Main website - source of data and more
{
Dock = DockStyle.Fill,
};
splitContainer1.Panel1.Controls.Add(chromeBrowser1);
chromeBrowser1.RegisterJsObject("callbackObj", new CallbackObjectForJs()); /// the link is obtained from here - See picture
}
public class CallbackObjectForJs
{
private readonly static BackgroundWorker backgroundWorker1 = new BackgroundWorker();
private readonly static ChromiumWebBrowser chromeBrowser2 = new ChromiumWebBrowser("http://facebook.com");
// IF I REMOVE ANY OF THE ABOVE I WILL RECEIVE THE FOLLOWING ERROR :
// Error CS0120 An object reference is required for the non-static field, method, or property 'Form1.backgroundWorker1'
// Error CS0120 An object reference is required for the non-static field, method, or property 'Form1.chromeBrowser2'
public void showMessage(string msg)
{
var sitelink = msg;
Console.WriteLine(sitelink); // WORKING - link is visible on console but chromeBrowser 2 is not update, still reads http://google.com
Console.WriteLine("Loading site using one of the options below"); // Can be seen on console +
// AT THIS POINT chromeBrowser2 SHOULD LOAD THE NEW URL ///
chromeBrowser2.Load(sitelink); // NOT WORKING
Console.WriteLine("Check Point 1");
backgroundWorker1.RunWorkerAsync(sitelink); // NOT WORKING
Console.WriteLine("Check Point 2");
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) // NOT WORKING
{
Console.WriteLine("Background Worker"); // Nothing
string link = (string)e.Argument;
Console.WriteLine("Check Point 3");
chromeBrowser2.Load(link);
Console.WriteLine("Check Point 5");
Console.WriteLine(link);
Console.WriteLine("Check Point 6");
}
Console results.
I understand that, for some reason, neither of the option I've chosen are being triggered. I have high speculations that is because of Error CS0120, which I managed to hide with making them static but that's it pretty much. I'm out of ideas. Search results got me this far.
I installed ReSharper and it found the problem in less than a minute.
All I had to do is make chromeBrowser2 static.
public partial class Form1 : Form
{
public ChromiumWebBrowser chromeBrowser1;
public *static* ChromiumWebBrowser chromeBrowser2;
....
Thanks for any input.

Get HTML source code from CefSharp web browser

I am using aCefSharp.Wpf.ChromiumWebBrowser (Version 47.0.3.0) to load a web page. Some point after the page has loaded I want to get the source code.
I have called:
wb.GetBrowser().MainFrame.GetSourceAsync()
however it does not appear to be returning all the source code (I believe this is because there are child frames).
If I call:
wb.GetBrowser().MainFrame.ViewSource()
I can see it lists all the source code (including the inner frames).
I would like to get the same result as ViewSource(). Could some one point me in the right direction please?
Update – Added Code example
Note: The address the web browser is pointing too will only work up to and including 10/03/2016. After that it may display different data which is not what I would be looking at.
In the frmSelection.xaml file
<cefSharp:ChromiumWebBrowser Name="wb" Grid.Column="1" Grid.Row="0" />
In the frmSelection.xaml.cs file
public partial class frmSelection : UserControl
{
private System.Windows.Threading.DispatcherTimer wbTimer = new System.Windows.Threading.DispatcherTimer();
public frmSelection()
{
InitializeComponent();
// This timer will start when a web page has been loaded.
// It will wait 4 seconds and then call wbTimer_Tick which
// will then see if data can be extracted from the web page.
wbTimer.Interval = new TimeSpan(0, 0, 4);
wbTimer.Tick += new EventHandler(wbTimer_Tick);
wb.Address = "http://www.racingpost.com/horses2/cards/card.sd?race_id=644222&r_date=2016-03-10#raceTabs=sc_";
wb.FrameLoadEnd += new EventHandler<CefSharp.FrameLoadEndEventArgs>(wb_FrameLoadEnd);
}
void wb_FrameLoadEnd(object sender, CefSharp.FrameLoadEndEventArgs e)
{
if (wbTimer.IsEnabled)
wbTimer.Stop();
wbTimer.Start();
}
void wbTimer_Tick(object sender, EventArgs e)
{
wbTimer.Stop();
string html = GetHTMLFromWebBrowser();
}
private string GetHTMLFromWebBrowser()
{
// call the ViewSource method which will open up notepad and display the html.
// this is just so I can compare it to the html returned in GetSourceAsync()
// This is displaying all the html code (including child frames)
wb.GetBrowser().MainFrame.ViewSource();
// Get the html source code from the main Frame.
// This is displaying only code in the main frame and not any child frames of it.
Task<String> taskHtml = wb.GetBrowser().MainFrame.GetSourceAsync();
string response = taskHtml.Result;
return response;
}
}
I don't think I quite get this DispatcherTimer solution. I would do it like this:
public frmSelection()
{
InitializeComponent();
wb.FrameLoadEnd += WebBrowserFrameLoadEnded;
wb.Address = "http://www.racingpost.com/horses2/cards/card.sd?race_id=644222&r_date=2016-03-10#raceTabs=sc_";
}
private void WebBrowserFrameLoadEnded(object sender, FrameLoadEndEventArgs e)
{
if (e.Frame.IsMain)
{
wb.ViewSource();
wb.GetSourceAsync().ContinueWith(taskHtml =>
{
var html = taskHtml.Result;
});
}
}
I did a diff on the output of ViewSource and the text in the html variable and they are the same, so I can't reproduce your problem here.
This said, I noticed that the main frame gets loaded pretty late, so you have to wait quite a while until the notepad pops up with the source.
I was having the same issue trying to get click on and item located in a frame and not on the main frame. Using the example in your answer, I wrote the following extension method:
public static IFrame GetFrame(this ChromiumWebBrowser browser, string FrameName)
{
IFrame frame = null;
var identifiers = browser.GetBrowser().GetFrameIdentifiers();
foreach (var i in identifiers)
{
frame = browser.GetBrowser().GetFrame(i);
if (frame.Name == FrameName)
return frame;
}
return null;
}
If you have a "using" on your form for the module that contains this method you can do something like:
var frame = browser.GetFrame("nameofframe");
if (frame != null)
{
string HTML = await frame.GetSourceAsync();
}
Of course you need to make sure the page load is complete before using this, but I plan to use it a lot. Hope it helps!
Jim

Using the WaveformTimeline Control in the WPF Sound Visualisation Library

I am having problems using the WaveformTimeLine class in the WPF Sound Visualization Library using the NAUDIO class library.I have followed the instructions they have put out in their documentation but it's just not working for me.
My code behind looks like:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private OpenFileDialog dialogBox = new OpenFileDialog();
private void open_Click(object sender, RoutedEventArgs e)
{
//Get Audio File
dialogBox.Filter = "Wave File (*.mp3 ; *.wav) | *.mp3;*.wav;";
if (dialogBox.ShowDialog() == true)
{
NAudioEngine.Instance.OpenFile(dialogBox.FileName);
fileLocation = dialogBox.FileName;
}
var soundEngine = NAudioEngine.Instance;
soundEngine.PropertyChanged += NAudioEngine_PropertyChanged;
soundEngine.OpenFile(fileLocation);
if (NAudioEngine.Instance.CanPlay) NAudioEngine.Instance.Play();
myWave = new WaveformTimeline();
myWave.RegisterSoundPlayer(soundEngine);
}
The code doesn't throw any errors but it also doesn't do anything too.Can someone with experience with this API or similar APIs please help me solve this problem.
Key mistake was the line
myWave = new WaveformTimeline();
After adding the WaveformTimeline control in XAML design view, you don't initialise the control again in the code behind because you will be given another instance of the WaveformTimeline object.

Winform WebBrowser Pass Cookie Then Process Links?

I asked this question a while ago but seems that there are no answers, so i tried to go with an alternative solution but i am stuck now, please see the following code:
WebBrowser objWebBrowser = new WebBrowser();
objWebBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(objWebBrowser_DocumentCompleted);
objWebBrowser.Navigate("http://www.website.com/login.php?user=xxx&pass=xxx");
objWebBrowser.Navigate("http://www.website.com/page.php?link=url");
And here is the event code:
WebBrowser objWebBrowser = (WebBrowser)sender;
String data = new StreamReader(objWebBrowser.DocumentStream).ReadToEnd();
Since it's impossible for me to use the WebBrowser.Document.Cookies before a document is loaded, i have first to navigate the login page, that will store a cookie automatically, but after that i want to call the other navigate in order to get a result. Now using the above code it doesn't work cause it always takes the second one, and it won't work for me to put it in the event cause what i want is like this:
Navigate with the login page and store cookie for one time only.
Pass a different url each time i want to get some results.
Can anybody give a solution ?
Edit:
Maybe the sample of code i provided was misleading, what i want is:
foreach(url in urls)
{
Webborwser1.Navigate(url);
//Then wait for the above to complete and get the result from the event, then continue
}
I think you want to simulate a blocking call to Navigate if you are not authorized. There are probably many ways to accomplish this and other approaches to get what you want, but here's some code I wrote up quickly that might help you get started.
If you have any questions about what I'm trying to do here, let me know. I admit it feels like "a hack" which makes me think there's a smarter solution, but anyway....
bool authorized = false;
bool navigated;
WebBrowser objWebBrowser = new WebBrowser();
void GetResults(string url)
{
if(!authorized)
{
NavigateAndBlockWithSpinLock("http://www.website.com/login.php?user=xxx&pass=xxx");
authorized = true;
}
objWebBrowser.Navigate(url);
}
void NavigateAndBlockWithSpinLock(string url)
{
navigated = false;
objWebBrowser.DocumentCompleted += NavigateDone;
objWebBrowser.Navigate(url);
int count = 0;
while(!navigated && count++ < 10)
Thread.Sleep(1000);
objWebBrowser.DocumentCompleted -= NavigateDone;
if(!navigated)
throw new Exception("fail");
}
void NavigateDone(object sender, WebBrowserDocumentCompletedEventArgs e)
{
navigated = true;
}
void objWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if(authorized)
{
WebBrowser objWebBrowser = (WebBrowser)sender;
String data = new StreamReader(objWebBrowser.DocumentStream).ReadToEnd();
}
}

Categories

Resources