WPF WebBrowser not loading - c#

I'm having a problem with the WebBrowser control. I have added it to one of the windows, but it doesen't load the page that i navigate to. I want to access to the control from the other windows, so i made public methods like Navigate etc. I have tried adding WebBrowser to other forms and it seems to work normally. It worked on this window when it was without any added code. I'm using the AutoResetEvent , so when the site would load it would continue the program. Could anyone tell me where could be the problem in this code?
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
readonly AutoResetEvent thread1Step = new AutoResetEvent(false);
public void EnterForm(string ElementId, string value)
{
HTMLDocument document = (HTMLDocument)TempBrowser.Document;
document.getElementById(ElementId).innerText = value;
}
public void Navigate(string url)
{
TempBrowser.Navigate(url);
thread1Step.WaitOne();
thread1Step.Reset();
}
public void PressButton(string id)
{
HTMLDocument doc = (HTMLDocument)TempBrowser.Document;
IHTMLElement btn = doc.getElementById(id);
if (btn != null)
{
btn.click();
}
}
public void Scroll(int n)
{
HTMLDocument doc = (HTMLDocument)TempBrowser.Document;
doc.parentWindow.scroll(0, n);
}
private void TempBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
thread1Step.Set();
}
public void CallFunction(string Funct)
{
TempBrowser.InvokeScript(Funct);
}
}

I prepared an async code for WPF based on my other answer ...
public static class MyExtensions
{
public static Task NavigateAsync(this WebBrowser browser, Uri uri)
{
var tcs = new TaskCompletionSource<object>();
LoadCompletedEventHandler loadCompleted = null;
loadCompleted = (s, e) =>
{
browser.LoadCompleted -= loadCompleted;
tcs.SetResult(e.WebResponse);
};
browser.LoadCompleted += loadCompleted;
browser.Navigate(uri);
return tcs.Task;
}
}
Now you can remove thread1Step and TempBrowser_LoadCompleted method. Just use
await TempBrowser.NavigateAsync(url);
DoYourWork(); //At this point your page is loaded. Read its content...

Related

WebBrowser not firing event DocumentCompleted

So I've written a piece of code inside a console application that just simply makes a web browser from System.Windows.Forms visit a website. Now the proble I'm facing is, the DocumentCompleted event will not fire if I use a ready made WebBrowser, as you can see I pass one into the constructor.
My ideal way would be to use the WebBrowser passed in via the constructor, and just navigate on the same WebBrowser, for some reason, it wont fire the DocumentCompleted event when using the same browser, it forces me to initialize a new browser, inside a new thread every time.
Why is this?
Here is the code that works, but has to initialize a new browser every time.
public class WebController
{
private readonly ILogger _logger = new ConsoleLogger(typeof(WebController));
private WebBrowser _webBrowser;
private readonly string _webAddress;
private bool _sendingMessage;
private string _lastMessage;
public WebController(WebBrowser webBrowser)
{
_webAddress = "https://{username}.example.com/";
}
public void SendMessage(string username, string message)
{
var thread = new Thread(() =>
{
_webBrowser = new WebBrowser();
_webBrowser.DocumentCompleted += BrowserDocumentCompleted;
_webBrowser.ScriptErrorsSuppressed = true;
_webBrowser.Navigate(_webAddress);
Application.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private void BrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.AbsolutePath != (sender as WebBrowser)?.Url.AbsolutePath)
{
return;
}
_logger.Warn("Navigated to: " + e.Url.AbsoluteUri);
}
}

send generated value from class to textbox

I have a library that generates strings to a function called MessageOut. This function, i cannot change the structure of.
It looks like this:
public void MessageOut(string msg) //params or return-type cannot be changed
{
Console.WriteLine(msg);
}
I have a textbox in my form that i want to show this message in.
How would I go about appending msg to that textbox?
I've tried:
public void MessageOut(string msg) //params or return-type cannot be changed
{
Console.WriteLine(msg);
sendMessageTextBox(msg);
}
public string[] sendMessageTextBox(params string[] msg)
{
string send = "";
foreach(var i in msg){send = i;}
return send;
}
Form:
private void getWaveformBtn_Click(object sender, EventArgs e)
{
MyClass className = new MyClass();
foreach(var i in className.sendMessageTextBox())
{
errorTextBox.Text += i;
}
}
For obvious reasons, this doesn't work ,but i'm unsure how to go about doing this. (i've tried: how to send text to textbox through a different class?
Sending information to a textbox from a class to a form)
However, i cannot seem to get those solutions to work.
Any help is much appreciated.
TL;DR - i basically want to show the new strings that messageOut recieves in a textbox
This can be done in may different ways, one of them will be to use a queue, fill it from MessageOut and drain it on button press.
public class MessageHandler /* , MessageOutInterface */
{
private readonly Queue<string> messages;
public MessageHandler()
{
this.messages = new Queue<string>();
}
public void MessageOut(string message)
{
this.messages.Enqueue(message);
}
public IEnumerable<string> PendingMessages()
{
string message;
while (this.messages.Count > 0)
yield return this.messages.Dequeue();
}
}
Your UI code
private void getWaveformBtn_Click(object sender, EventArgs e)
{
foreach(var i in messageHandler.PendingMessages())
{
errorTextBox.Text += i;
}
}
If MessageOut is called on one thread (i.e. not the main thread) and the button press obviously happens on the main thread you'll need a thread safe approach:
public class MessageHandler /* , MessageOutInterface */
{
private readonly object syncRoot = new Object();
private readonly Queue<string> messages;
public MessageHandler()
{
this.messages = new Queue<string>();
}
public void MessageOut(string message)
{
lock (this.syncRoot)
{
this.messages.Enqueue(message);
}
}
public IEnumerable<string> PendingMessages()
{
lock (this.syncRoot)
{
var pending = this.messages.ToArray();
this.messages.Clear();
return pending;
}
}
}
This is not the best way to synchronize the threads but you can ask another question about synchronization.

Update a form in parallel with an installation script?

I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}

GeckoFX Observer Service

I'm trying to block specific images on a page from loading, but I've ran into a bit of trouble.
GeckoWebBrowser's HttpActivityObserver is returning a Not Implemented exception and crashing my program, so I'm trying to implement my own observer but the observe method isn't being called.
Any ideas would be helpful.
public class HttpObserver : nsIObserver
{
private nsIObserverService service;
private List<string> bans;
public HttpObserver()
{
bans = new List<string>();
service = Xpcom.CreateInstance<nsIObserverService>("#mozilla.org/observer-service;1");
}
public void Register()
{
service.AddObserver(this, "http-on-modify-request", false);
}
public void Unregister()
{
service.RemoveObserver(this, "http-on-modify-request");
}
public void BanUrl(string url)
{
bans.Add(url);
}
public void Observe(nsISupports aSubject, string aTopic, string aData)
{
nsIHttpChannel httpChannel = Xpcom.QueryInterface<nsIHttpChannel>(aSubject);
if (aTopic == "http-on-modify-request")
{
foreach (string url in bans)
{
if (url == httpChannel.GetURIAttribute().ToUri().AbsoluteUri)
{
httpChannel.Cancel(unchecked((int)0x804b0002));
}
}
}
}
}
Figured it out. For anyone else struggling with this, replace Xpcom.CreateInterface with Xpcom.GetService
Thanks, i try to find alternative BeforeNavigate2 and all you need just:
Gecko.GeckoWebBrowser wb = new GeckoWebBrowser { Dock = DockStyle.Fill, UseHttpActivityObserver = true };
wb.ObserveHttpModifyRequest += (o, e) => { MessageBox.Show(e.Uri.ToString()); };

update a richtextbox from a static class

I have the following code:
namespace SSS.RemoteTruckService
{
public partial class Startup : Form
{
private Timer _gpsTimer;
private Timer _ppsTimer;
private Timer _creditCardTimer;
private Timer _iniTimer;
public string Message
{
get { return richTextBox_Message.Text; }
set
{
richTextBox_Message.Invoke((MethodInvoker)(()
=> richTextBox_Message.Text = DateTime.Now + " " +
value + Environment.NewLine + richTextBox_Message.Text));
}
}
public Startup()
{
InitializeComponent();
}
private void ButtonStartClick(object sender, EventArgs e)
{
StartRemoteTruck();
}
private void ButtonPauseClick(object sender, EventArgs e)
{
if (_gpsTimer.Enabled) _gpsTimer.Enabled = false;
if (_ppsTimer.Enabled) _ppsTimer.Enabled = false;
if (_creditCardTimer.Enabled) _creditCardTimer.Enabled = false;
if (_iniTimer.Enabled) _iniTimer.Enabled = false;
ProcessIniFile.StopProcess();
}
public void StartRemoteTruck()
{
Message = "RemoteTruck started.";
if (Settings.GlobalSettings == null)
{
Message = "GlobalSettings was null or not loaded. Cannot continue.";
Logging.Log("GlobalSettings was null or not loaded. Cannot continue.", "RemoteTruck", Apps.RemoteTruckService);
Environment.Exit(0);
}
if (Settings.GlobalSettings.IniFileWatcherEnabled)
{
ProcessIniFile.StartProcess();
}
CreateTimers();
}
And in the ProcessIniFile.StartProcess() I have the code:
namespace SSS.RemoteTruckService.inifile
{
public static class ProcessIniFile
{
private static DateTime _iniLastWriteTime;
private static readonly string Inifile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "sss.ini");
private static FileSystemWatcher _watcher;
public static void StartProcess()
{
ReadIniFile();
SaveCurrentIniReadings();
CreateIniFileWatcher();
}
public static void StopProcess()
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher = null;
}
}
private static void CreateIniFileWatcher()
{
_watcher = new FileSystemWatcher
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.Windows),
NotifyFilter = NotifyFilters.LastWrite,
Filter = "sss.ini"
};
_watcher.Changed += SssIniWatcherChanged;
_watcher.EnableRaisingEvents = true;
}
I'd like to pass back the the calling form the status of the reads of the file watcher.
Maybe I'm overthinking this, but if I want to add to the Message on the main form, how do I get to it?
You can use Events for that. Your process can send events and your form can handle them.
More info: http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
The simple but not pretty way I like to use is to make that part of the form static as well. For example, creating a static variable WriteMessage, and in your Form Load or Startup(), you can set it:
WriteMessage = (s) => Message = s;
Sure this has some issues, but it's a quick way to get it done. One of those issues is that, you may need to use Dispatcher.invoke if you're not on the UI thread.

Categories

Resources