How to control a web browser from another thread? - c#

Hi i have a simple webbrowser running in the ui thread and i have a webbrowser.Document.GeteEmetbyId.Innertext
on another thread running in a while loop, but it gives me a error, so i tried using
this.Invoke((MethodInvoker)delegate {
webbrowser.Document.GeteEmetbyId.Innertext;
});
,but that gave me an error too, my question is, is there a simple way to control a webbrowser from another thread.
I tried looking for the answer from google, but i couldn't find anything that works for me.
My Current Code:
private void test_Click(object sender, EventArgs e)
{
th = new Thread(fncTh) { IsBackground = true };
th.Start();
}
void fncTh()
{
while (true)
{
string tehe = wb.Document.GetElementById("txtTehtav").InnerText;
txtVastus.Text = tehe();
}
}

Related

Request WinForm Label Value from Background Worker illegal?

I am wondering if the following will affect the performance of my application.
Every call I make to update the background worker I do the right way without cross threading, but if I request a value from the UserInterface and not update it, is this illegal and could it affect the performance?
Here is a small snippet from my code.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
Settings settings = e.Argument as Settings;
BackgroundWorker worker = sender as BackgroundWorker;
if ((worker.CancellationPending))
{
e.Cancel = true;
}
else
{
if (settings.O1_Enabled)
{
try
{
counter_O1 = Convert.ToInt32(this.rowcount_O1.Text);
Any advice would be greatly appreciated.
No, It is not legal.
You can't access UI in background worker like this. To do so You need to invoke this, for example:
Dispatcher.Invoke(new Action(() =>
{
counter_O1 = Convert.ToInt32(this.rowcount_O1.Text);
}));

Form freezes when opened for second time

I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.

How can I create WPF controls in a background thread?

I have method which create background thread to make some action. In this background thread I create object. But this object while creating in runtime give me an exception :
The calling thread must be STA, because many UI components require this.
I know that I must use Dispatcher to make reflect something to UI. But in this case I just create an object and dont iteract with UI. This is my code:
public void SomeMethod()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Background_Method);
worker.RunWorkerAsync();
}
void Background_Method(object sender, DoWorkEventArgs e)
{
TreeView tv = new TreeView();
}
How can I create objects in background thread?
I use WPF application
TreeView is a UI control. You can only create and manipulate UI controls on a UI thread, so what you're trying to do is not possible.
What you want to do is do all of the time-consuming work on the background thread, and then "call back" to the UI thread to manipulate the UI. This is actually quite easy:
void Background_Method(object sender, DoWorkEventArgs e)
{
// ... time consuming stuff...
// call back to the window to do the UI-manipulation
this.BeginInvoke(new MethodInvoker(delegate {
TreeView tv = new TreeView();
// etc, manipulate
}));
}
I may have got the syntax wrong for BeginInvoke (it's off the top of my head), but there you go anyway...
HTH:
void Background_Method(object sender, DoWorkEventArgs e)
{
// Time Consuming operations without using UI elements
// Result of timeconsuming operations
var result = new object();
App.Current.Dispatcher.Invoke(new Action<object>((res) =>
{
// Working with UI
TreeView tv = new TreeView();
}), result);
}
No one is discussing the case of a separate STA thread in details (even though the concept is exactly the same).
So let's imagine a simple tab control added at a button click
private void button_Click(object sender, RoutedEventArgs e)
{
TabItem newTab = new TabItem() { Header = "New Tab" };
tabMain.Items.Add(newTab);
}
If we move it to another STA thread
private void button_Click(object sender, RoutedEventArgs e)
{
Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
}
private void ThreadStartingPoint()
{
TabItem newTab = new TabItem() { Header = "New Tab" };
tabMain.Items.Add(newTab);
}
of course we get a System.InvalidOperationException
Now, what happens if we add the control
private void AddToParent(string header)
{
TabItem newTab = new TabItem() { Header = header };
tabMain.Items.Add(newTab);
}
using a delegate method?
public void DelegateMethod(string header)
{
tabMain.Dispatcher.BeginInvoke(
new Action(() => {
this.AddToParent(header);
}), null);
}
it does work if you call it
private void button_Click(object sender, RoutedEventArgs e)
{
Thread newThread = new Thread(new ThreadStart(ThreadStartingPoint));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
}
private void ThreadStartingPoint()
{
DelegateMethod("new tab");
}
because of course now we keep the visual tree in the same original thread.
To make your code simply work, you must join a STA COM apartment by calling Thread.SetApartmentState(ApartmentState.STA). Since BackgroundWorker is probably using some shared thread pool, joining a particular apartment may affect other users of this thread pool or may even fail if it has already been set to e.g. MTA before. Even if it all worked out, your newly created TreeView would be locked to this worker thread. You wouldn't be able to use it in your main UI thread.
If you explained in a bit more detail about your true intentions, you would surely get better help.
Try following Code:
public void SomeMethod()
{
System.ComponentModel.BackgroundWorker myWorker = new System.ComponentModel.BackgroundWorker();
myWorker.DoWork += myWorker_DoWork;
myWorker.RunWorkerAsync();
}
private void myWorker_DoWork(object sender,
System.ComponentModel.DoWorkEventArgs e)
{
// Do time-consuming work here
}
void Background_Method(object sender, DoWorkEventArgs e)
{
TreeView tv = new TreeView();
// Generate your TreeView here
UIDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
someContainer.Children.Add(tv);
};
}
I solved my problem. I just used e.Result property of RunWorkerCompleted method. I get data in background thread and then use this data when thread completed. Thank every body for useful methods. Special thank to Veer to give a recommendation about e.Result property.
See the answer on this question:
How to run something in the STA thread?
When you define your thread, set the ApartmentState to STA:
thread.SetApartmentState(ApartmentState.STA);
This should do the trick!

Getting Cross-thread operation not valid [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
public void CheckUnusedTabs(string strTabToRemove)
{
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
I am trying to close a tab in the tabcontrol of windows application using the above code and i encountered the error:
Cross-thread operation not valid.
How to solve this ?
You can only make changes to WinForm controls from the master thread. You need to check whether InvokeRequired is true on the control and then Invoke the method as needed.
You can do something like this to make it work:
public void CheckUnusedTabs(string strTabToRemove)
{
if (TaskBarRef.tabControl1.InvokeRequired)
{
TaskBarRef.tabControl1.Invoke(new Action<string>(CheckUnusedTabs), strTabToRemove);
return;
}
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
call using invoke, because you're accessing the GUI thread using another thread
this.Invoke((MethodInvoker)delegate() {CheckUnusedTabs(""); });
When using threads and UI controls, in winforms, you need to use InvokeRequired to make changes to the controls.
EDIT.
added an example.
Form, with button and label.
try
private void button2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(UpdateProcess);
thread.Start();
}
private void SetLabelText(string val)
{
label1.Text = val;
}
delegate void m_SetLabel(string val);
private void UpdateProcess()
{
int i = 0;
while (true)
{
if (label1.InvokeRequired)
{
m_SetLabel setLabel = SetLabelText;
Invoke(setLabel, i.ToString());
}
else
label1.Text = i.ToString();
i++;
Thread.Sleep(500);
}
}
Cross thread not valid exception is due to the UI controls being accessed from other threads than main thread.see this
http://helpprogramming.blogspot.com/2011/10/invalid-cross-thread-operation.html
Set the following variable:
CheckIllegalCrossThreadValidation = false

WPF application in a loop, how to not have the whole application freeze?

I am having fun with WPF and got a problem. I have googled and found this website that has the same problem of me but without any working solution.
The problem is that I have a button that do some processing of data (around 30 sec). I want to have the button to disable and to have log writing in a text box... the problem is that it doesn't disable and it doesn't wrote any thing on the textbox until the processing is completely done.
Any idea?
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
//Long stuff here
txtLog.AppendText(Environment.NewLine + "Blabla");
//End long stuff here
this.button1.IsEnabled = true;
}
As others have said, use the BackgroundWorker or some other method of doing work asychronously.
You can declare it under your Window, initialize it somewhere like the Loaded event, and use it in the Click event. Here's your method, modified to use BackgroundWorker, assuming you've declared it under the Window as _bw:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw = new BackgroundWorker();
_bw.DoWork += new DoWorkEventHandler((o, args) =>
{
//Long stuff here
this.Dispatcher.Invoke((Action)(() => txtLog.AppendText(Environment.NewLine + "Blabla")));
});
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
//End long stuff here
this.Dispatcher.Invoke((Action)(() => this.button1.IsEnabled = true));
});
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
_bw.RunWorkerAsync();
}
Note that anything that modifies your UI from another thread must be done within a Dispatcher.Invoke or Dispatcher.BeginInvoke call, WPF does not allow you to get or set DependencyProperty values from any thread but the one where the object was created (more about this here).
If you wanted to read from txtLog instead of modifying it, the code would be the same:
//Long stuff here
this.Dispatcher.Invoke((Action)(() =>
{
string myLogText = txtLog.Text;
myLogText = myLogText + Environment.NewLine + "Blabla";
txtLog.Text = myLogText;
}));
That operation is being performed on the UI thread. This means that it will block the Windows message pump from processing until it has completed. no pump = no UI updates. You should launch the job on another thread. I don't know WPF, but in C# I would use either the Thread or BackgroundWorker classes.
do it async. create a backgroundworker process to handle the data and the application will continue to respond. MSDN Resources on the Class. Since WPF is using C# (or VB.net) you can still use the same types of threading objects. I've used the background worker successfully in a WPF app myself.

Categories