I have a list of urls in a for loop, loading a url one at a time but FinishLoadingFrameEvent event is called only once.
My complete code is like this
private List<string> urls = //fetch from db;
ManualResetEvent waitEvent = new ManualResetEvent(false);
BrowserView webView = new WPFBrowserView();
string path = //my local path;
public MainWindow()
{
InitializeComponent();
mainLayout.Children.Add((UIElement)webView.GetComponent());
webView.Browser.FinishLoadingFrameEvent += delegate (object sender, FinishLoadingEventArgs e)
{
System.Threading.Thread.Sleep(5000);
if (e.IsMainFrame)
{
DOMDocument document = e.Browser.GetDocument();
var html = document.DocumentElement.InnerHTML;
System.IO.File.WriteAllText(path, html);
waitEvent.Set();
}
};
foreach (var url in urls)
{
webView.Browser.LoadURL(url);
waitEvent.WaitOne();
waitEvent.Reset();
}
}
Am i missing something?
Your code seems to work as expected for my set of URLs.
Here is the complete sample code with all the modifications:
public partial class MainWindow : Window
{
private List<string> urls = new List<string>
{ "google.com", "microsoft.com", "teamdev.com", "teamdev.com/dotnetbrowser" };
ManualResetEvent waitEvent = new ManualResetEvent(false);
BrowserView webView = new WPFBrowserView();
string path = "html.txt";
public MainWindow()
{
InitializeComponent();
mainLayout.Children.Add((UIElement)webView.GetComponent());
webView.Browser.FinishLoadingFrameEvent += delegate (object sender,
FinishLoadingEventArgs e)
{
//System.Threading.Thread.Sleep(5000);
if (e.IsMainFrame)
{
DOMDocument document = e.Browser.GetDocument();
var html = document.DocumentElement.InnerHTML;
System.IO.File.WriteAllText(path, html);
waitEvent.Set();
}
};
foreach (var url in urls)
{
Debug.WriteLine($"Loading {url}");
webView.Browser.LoadURL(url);
waitEvent.WaitOne();
Debug.WriteLine($"{url} loaded");
waitEvent.Reset();
}
}
}
You can notice that I have commented out the Thread.Sleep call in the event handler. Uncommenting it simply makes everything run much slower, but it still works.
Related
I am having this problem that my form would show up but not responding after it detects an event.
Here's my code for listening the event and what it does after it detects the event.
private void checkPrintJobs()
{
EventLogQuery logQuery = new EventLogQuery("Microsoft-Windows-PrintService/Operational", PathType.LogName, "*[System[(EventID = 800)]]");
EventLogWatcher logWatcher = new EventLogWatcher(logQuery);
logWatcher.EventRecordWritten += new EventHandler<EventRecordWrittenEventArgs>(EventWritten);
logWatcher.Enabled = true;
}
private void EventWritten(Object obj, EventRecordWrittenEventArgs arg)
{
PrintQueue myPrintQueue = new PrintQueue(ps, printer, PrintSystemDesiredAccess.AdministratePrinter);
myPrintQueue.Pause();
foreach (var job in myPrintQueue.GetPrintJobInfoCollection())
{
job.Pause();
}
MessageBox.Show("WAIT!");
frmPromptPIN frm = new frmPromptPIN(printer);
frm.Show();
}
If i removed the MessageBox.Show("WAIT!"), the form won't show up at all.
Anyone experiencing this one?
As TnTinMn says you have to invoke your UI code in the UI thread, eg.
private void EventWritten(Object obj, EventRecordWrittenEventArgs arg)
{
PrintQueue myPrintQueue = new PrintQueue(ps, printer, PrintSystemDesiredAccess.AdministratePrinter);
myPrintQueue.Pause();
foreach (var job in myPrintQueue.GetPrintJobInfoCollection())
{
job.Pause();
}
Invoke((MethodInvoker) delegate {
frmPromptPIN frm = new frmPromptPIN(printer);
frm.Show();
});
}
I have about 8 records that I want to print in one batch, each on a separate page. However, the UWP sample for this uses over 600 lines of code to accomplish it. It seems to me that it has to be much, much easier than that. I thought all we'd have to do is add each page to the PrintDocument and send the print job. Apparently not. I'm using this:
async void Print()
{
var printDocument = new PrintDocument();
var printDocumentSource = printDocument.DocumentSource;
var printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
var pages = new List<Page>();
foreach (var item in items)
{
(//Set up variables)
var printPage = new PageToPrint() { //Set properties };
printPage.Set_Up(); //Set up fields
pages.Add(printPage);
}
printDocument.SetPreviewPage(1, page);
printDocument.SetPreviewPageCount(pages.Count, PreviewPageCountType.Final);
foreach (var page in pages)
{
printDocument.AddPage(page);
}
printDocument.AddPagesComplete();
await PrintManager.ShowPrintUIAsync();
}
void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
PrintTask printTask = null;
printTask = e.Request.CreatePrintTask("Kimble Print Job", sourceRequested =>
{
printTask.Completed += PrintTask_Completed;
sourceRequested.SetSource(printDocumentSource);
});
}
private async void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested -= PrintTaskRequested;
});
}
However, it won't generate the print preview. It just sits there spinning and spinning, and if I hit "print" it doesn't succeed (PDF can't open, job never gets to a physical printer.)
I was hoping printing would be at least reasonably easy with the PrintDocument, and I still think it looks like it should be. Am I just missing it here, or does it really take 600+ lines of code to dispatch a simple print job?
However, it won't generate the print preview.
This is because the setPreview method printDocument.SetPreviewPage(1, page); must be put in printDocument.GetPreviewPageevent handle. So you should register the event handle firstly. Same with printDocument.AddPages event handle.You messed up the event handle register and callback function all in one.Here I do a little change of your code and I tested it works well.
protected PrintDocument printDocument;
protected IPrintDocumentSource printDocumentSource;
List<Page> pages = new List<Page>();
Page printPage = new PageToPrint();
public MainPage()
{
this.InitializeComponent();
RegisterForPrinting();
}
private async void BtnPrint_Click(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
public void RegisterForPrinting()
{
printDocument = new PrintDocument();
printDocumentSource = printDocument.DocumentSource;
pages.Add(printPage);
printDocument.GetPreviewPage += GetPrintPreviewPage;
printDocument.AddPages += AddPrintPages;
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
}
private void AddPrintPages(object sender, AddPagesEventArgs e)
{
foreach (var page in pages)
{
printDocument.AddPage(page);
}
printDocument.AddPagesComplete();
}
private void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
{
printDocument.SetPreviewPage(1, printPage);
printDocument.SetPreviewPageCount(pages.Count, PreviewPageCountType.Final);
}
void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
PrintTask printTask = null;
printTask = e.Request.CreatePrintTask("Kimble Print Job", sourceRequested =>
{
printTask.Completed += PrintTask_Completed;
sourceRequested.SetSource(printDocumentSource);
});
}
private async void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested -= PrintTaskRequested;
});
}
Although you may not need all the code of the sample, but I recommend you to follow the official sample structure and build a PrintHelper class.
This is mt first time trying to write a not web based program, and my first time writing anything in C#.
I need a program that monitors folders, but I can't get it to work.
I have used the example from this post Using FileSystemWatcher with multiple files but is trying to make it a form.
My current problem comes in the ProcessQueue function where fileList apparently is defined in another thread.
Whenever a file is actually submitted to the watched folder I get an error that using fileList is a cross thread call
Can anyone explain this error to me, and how to fix it?
namespace matasWatch
{
public partial class Form1 : Form
{
private int n = 1;
private bool isWatching = false;
private List<string> filePaths;
private System.Timers.Timer processTimer;
private string watchedPath;
private FileSystemWatcher watcher;
public Form1()
{
filePaths = new List<string>();
watchedPath = "C:\\Users\\username\\Desktop\\test";
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!isWatching)
{
button1.Text = "Stop";
isWatching = true;
watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += Watcher_FileCreated;
watcher.Error += Watcher_Error;
watcher.Path = watchedPath;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
else {
button1.Text = "Watch";
isWatching = false;
watcher.EnableRaisingEvents = false;
watcher.Dispose();
watcher = null;
}
}
private void Watcher_Error(object sender, ErrorEventArgs e)
{
// Watcher crashed. Re-init.
isWatching = false;
button1_Click(sender, e);
}
private void Watcher_FileCreated(object sender, FileSystemEventArgs e)
{
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new System.Timers.Timer(2000);
processTimer.Elapsed += ProcessQueue;
processTimer.Start();
}
else{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
private void ProcessQueue(object sender, ElapsedEventArgs args)
{
try
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
}
}
}
}
}
I assume that fileList is a windows forms control. The ProcessQueue method is called from a timer thread which is by default a background thread. The fileList control resides in the UI thread. You need to use the Invoke() method of the form passing it in a delegate the updates the fileList control.
Invoke(new Action(() =>
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}));
Try using System.Windows.Forms.Timer instead of System.Timers.Timer so the timer tick event is executed on the UI thread.
See here for more details.
I'm trying to use WebBrowser class, but of course it doesn't work.
My code:
WebBrowser browser = new WebBrowser();
browser.Navigate("http://www.google.com");
while(browser.DocumentText == "")
{
continue;
}
string html = browser.DocumentText;
browser.DocumentText is always "". Why?
You should use DocumentCompleted event, and if you don't have WebForms application, also ApplicationContext might be needed.
static class Program
{
[STAThread]
static void Main()
{
Context ctx = new Context();
Application.Run(ctx);
// ctx.Html; -- your html
}
}
class Context : ApplicationContext
{
public string Html { get; set; }
public Context()
{
WebBrowser browser = new WebBrowser();
browser.AllowNavigation = true;
browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
browser.Navigate("http://www.google.com");
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Html = ((WebBrowser)sender).DocumentText;
this.ExitThread();
}
}
The WebBrowser isn't going to do it's job until the current thread finishes it's work, if you changed it to be something like this:
WebBrowser browser = new WebBrowser();
browser.Navigate("http://www.google.com");
browser.Navigated += (s, e) =>
{
var html = browser.DocumentText;
};
The variable will be set.
But, as others have mentioned, the document completed is a better event to attach to, as at that time, the entire document will be completed (appropriate name!)
WebBrowser browser = new WebBrowser();
browser.Navigate("http://www.google.com");
browser.DocumentCompleted += (s, e) =>
{
var html = browser.DocumentText;
html.ToString();
};
Attach to the DocumentCompleted event, the code is as below
browser.DocumentCompleted += (s, e) =>
{
string html = browser.DocumentText;
};
If you need the DocumentText you should handle the DocumentCompleted event
browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
See event below
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb = (WebBrowser)sender;
string text = wb.DocumentText;
}
Try something like this
string url = string.Empty:
string html = "http://www.google.com/";
string url = html;
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
{
url = "http://" + url;
}
browser.Navigate(new Uri(url));
replace it within your While loop where necessary
I have a for loop and inside there is a navigate method for a browser. and it's suppose to load diffrent sites, but the problem is that it will start to load 1 site and before it will load it, it'll load another site. so I need to like pause it until it's completed.
I started to write an event to when the ProgressChanged event is at 100%.. than I figured I don't have any idea what to do next but I think it's a start.
Please help, Thanks!
Edit: I am using Forms as Roland said.
I assume you are doing windows forms programming. The event you want is DocumentCompleted Here's an example:
public Uri MyURI { get; set; }
public Form1()
{
InitializeComponent();
MyURI = new Uri("http://stackoverflow.com");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
webBrowser1.Url = MyURI;
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if(e.Url == MyURI)
MessageBox.Show("Page Loaded");
}
For a list of URIs it's straight forward.
public int CurrentIndex = 0;
List<Uri> Uris;
public Form1()
{
InitializeComponent();
Uris = new List<Uri> { new Uri("http://stackoverflow.com"), new Uri("http://google.com/") };
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
webBrowser1.Url = Uris[CurrentIndex];
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser browser = (WebBrowser)sender;
if (e.Url == Uris[CurrentIndex])
{
CurrentIndex++;
if (CurrentIndex < Uris.Count)
{
browser.Url = Uris[CurrentIndex];
}
}
}