c# - WPF Window won't update certain features from separate thread - c#

In my main class I create a wpf window (p) and a function to display the window with a given message on it in a label (content).
public partial class MainWindow : Window
{
///Creates Window
public static Wils0n.Window1 p = new Wils0n.Window1();
/// <summary>
/// creates notif bar with message
/// </summary>
/// <param name="message">Message for notif bar</param>
public static void NotifProc(string message)
{
///sets global string to message
Commands.MyGlobals.inputtext = message;
p.Dispatcher.Invoke(new Action(delegate ()
{
MainWindow.p.Topmost = true;
MainWindow.p.Top = 0;
///sets label content to global string (this only works once)
MainWindow.p.content.Content = Commands.MyGlobals.inputtext;
MainWindow.p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (MainWindow.p.Width / 2);
MainWindow.p.Show();
Thread.Sleep(2000);
while (MainWindow.p.Top != -114)
{
MainWindow.p.Top -= 3;
Thread.Sleep(15);
}
MainWindow.p.Hide();
}
));
}
}
Then in another class in another namespace I call it like such..
namespace Wilson.Utils
{
class Commands
{
public void start()
{
///creates notification window thats reads "hello world"
MainWindow.NotifProc("hello world");
///creates notification window thats reads "test1"
///For some reason reads "hello world"
MainWindow.NotifProc("test1");
///creates notification window thats reads "test2"
///For some reason reads "hello world"
MainWindow.NotifProc("test2");
}
///creates global string
public static class MyGlobals
{
public static string inputtext = "";
}
}
}
This works perfectly fine the first time only, if I call it again it with a different message it wont update the message but the notif still works it just displays the old message.
I have also had this problem with changing MainWindow.p.Opacity and MainWindow.p.Background
Without Dispatcher.Invoke I get an error reading "cannot access object because a different thread owns it".
If I remove Commands.MyGlobals.inputtext = message; and put Commans.MyGlobals.inputtext = "test1" before MainWindow.NotifProc("test1"); it still doesn't work.
I have also tried creating a class like such:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate () { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
and adding:
p.content.Refresh();
but had no luck.
Any help would be great. :/
EDIT
I managed to find a fix although it is probably not the best:
instead of creating a new window at the beginning I create a new window each time the window animation is called. VS said the thread must be STA so I called it in an STA thread and lastly I added a check at the end of the window animation so that no new windows can be created until the first is done.
My new NotifProc function looks like this:
public static void NotifProc(string message)
{
Tools.MyGlobals.notifmessage = message;
var thread = new Thread(new ThreadStart(STAThread));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private static void STAThread()
{
Wils0n.Window1 p = new Wils0n.Window1();
p.content.Content = Tools.MyGlobals.notifmessage;
p.content.Refresh();
p.Topmost = true;
p.Top = 0;
p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
p.Show();
Thread.Sleep(2000);
while (p.Top != -114)
{
p.Top -= 3;
Thread.Sleep(15);
}
p.Close();
}

Instead of calling Thread.Sleep in the UI thread, and thus blocking it, you should simply animate the Window position like this:
public static void NotifProc(string message)
{
p.Content.Content = message;
p.Left = SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
p.Top = 0;
p.Topmost = true;
p.Show();
var animation = new DoubleAnimation
{
To = -114,
BeginTime = TimeSpan.FromSeconds(2),
Duration = TimeSpan.FromSeconds(0.57)
};
animation.Completed += (s, e) => p.Hide();
p.BeginAnimation(TopProperty, animation);
}
If you want to call the method from a thread other than the UI thread, do it like this:
Application.Current.Dispatcher.Invoke(() => NotifProc(...));
or
MainWindow.p.Dispatcher.Invoke(() => NotifProc(...));

Related

Accessing Object Fields from another Thread c#

I need to access the TimerData.textSet field to change the text displayed from a another thread that controls the timing. But a InvalidOperationException is thrown. Is there a solution to this problem.
namespace Scilca.KBL
{
public class TimerData
{
public Run textSet;
public ProgressBar statusTimer;
public TimerData(Run run, ProgressBar statusTimer)
{
textSet = run;
this.statusTimer = statusTimer;
}
}
/// <summary>
/// Interaction logic for KBLSessionWindow.xaml
/// </summary>
public partial class KBLSessionWindow : Window
{
private int leftTime = 60;
private static Run run;
private static ProgressBar progressBar;
public int TimeLeftOver
{
get { return leftTime; }
}
public KBLSessionWindow()
{
InitializeComponent();
run = runSecondTime;
progressBar = timeProgress;
Thread timerThread = new Thread(new ParameterizedThreadStart(startTimer));
timerThread.Start(new TimerData(run, progressBar));
}
public void startTimer(Object td)
{
TimerData timerData = (TimerData)td;
int time = 60;
while (true)
{
Thread.Sleep(1000);
time -= 1;
if (time == 0)
time = 60;
Console.WriteLine(timerData.textSet.Text);
timerData.textSet.Text = time.ToString(); // InvalidOperationException
timerData.statusTimer.Value = time * 100 / 60;
}
}
}
}
It looks like you are trying to access an UI element from a non-ui thread.
Whenever you update your UI elements from a thread other than the main thread, you need to use:
Application.Current.Dispatcher.Invoke(() =>
{
// your code here.
timerData.textSet.Text = time.ToString();
timerData.statusTimer.Value = time * 100 / 60;
});
otherwise you will get a System.InvalidOperationException
From MSDN:
An System.InvalidOperationException is thrown when a method of an object is called when the state of the object cannot support the method call. The exception is also thrown when a method attempts to manipulate the UI from a thread that is not the main or UI thread.
Unrelated to your issue, I would suggest using a timer (see below) instead of creating a new thread every time, this would be more efficient since Timer is using the Thread Pool:
Timer myTimer = new Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
// code here will run every second
}

Access dynamic control from class in a thread

I'm working on a small project which deals with polling devices to check states of I/O controls. I had implemented a small project dealing with a particular device, but have decided that i would like to eventually implement different devices, and so have moved over to an class : interface approach. This has caused a few problems however, since i moved a lot of code around.
Before i moved the code around and such, i was accessing dynamic form controls by using a delegate as such;
if (result != null)
{
this.Invoke((MethodInvoker)delegate
{
txtOutput1.Text = (result[4] == 0x00 ? "HIGH" : "LOW"); // runs on UI thread
if (result[4] == 0x00)
{
this.Controls["btn" + buttonNumber].BackColor = Color.Green;
}
else
{
this.Controls["btn" + buttonNumber].BackColor = Color.Red;
}
});
}
This worked fine until i moved certain methods to a new class which inherits from an interface. I don't want to just set the dynamic buttons to public, and i'm not sure i can create get;set; for dynamic buttons, considering theres lots of them and are created on startup. Another problem is the this.invoke" command. I believe the invoke command doesn't work unless it's placed on a form...and now it's been moved to a class, so i need to look at another way of doing this.
Does anyone have any ideas as to where i should be heading with this?
EDIT 1:
The program is designed as a monitoring system for hardware devices that handle inputs/outputs. using these i can check if, for example, a door alarm has been triggered and such. The program itself in terms of forms / design is very simple. Currently i have a single form, which generates buttons based on information in a database, for example if there are 10 devices configured, there are 10 buttons. each of these shows green / red dependant on the hardware state.
My main form triggers a thread for each device which monitors it, but because i wished to have multiple types of device i moved them to different classes and an interface which handles all of the common methods. Currently i have a device class, which implements an interface. With regards to this question, i need to now access an instance of the single main form from which i am updating, rather than creating a new instance, so that i can use the new method i created when i moved said logic into the form itself.
EDIT 2:
IdeviceInterface bfdeviceimp = new bf2300deviceimp();
// some other declarations and initialize components
private void btnConnect_Click(object sender, EventArgs e)
{
updateUI();
}
public void updateUI()
{
DBConnector mDBConnector = new DBConnector();
int count = mDBConnector.Count() - 1;
DataTable dataTable = mDBConnector.Select("SELECT * FROM devices");
int x = 12;
int y = 65;
for (int i = 0; i <= count && i < 25; i++)
{
Button btnAdd = new Button();
btnAdd.Text = dataTable.Rows[i]["deviceDescription"].ToString();
btnAdd.Location = new Point(x, y);
btnAdd.Tag = i;
btnAdd.Name = "btn" + i.ToString();
btnAdd.BackColor = Color.Green;
var temp = i + 1;
this.Controls.Add(btnAdd);
this.Controls[btnAdd.Name].MouseClick += (sender, e) =>
{
int index = temp;
generalMethods.generatePopup(sender, e, index);
};
string address = dataTable.Rows[i]["deviceIP"].ToString();
int port = int.Parse(dataTable.Rows[i]["devicePort"].ToString());
ThreadStart workerThread = delegate { start(address, port, i); };
new Thread(workerThread).Start();
x = (x + 75);
if (i != 0 && (i % 5) == 0)
{
x = 12;
y = y + 30;
}
if (i == 25)
{
Button btnPreviousPage = new Button();
btnPreviousPage.Text = "<";
btnPreviousPage.Location = new Point(150, 350);
btnPreviousPage.Tag = "left";
this.Controls.Add(btnPreviousPage);
Button btnNextPage = new Button();
btnNextPage.Text = ">";
btnNextPage.Location = new Point(225, 350);
btnNextPage.Tag = "right";
this.Controls.Add(btnNextPage);
}
}
}
public void start(string address, int port, int i)
{
if (timer == null)
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += delegate(object sender, ElapsedEventArgs e) { timerElapsed(sender, e, address, port, i); };
}
timer.Enabled = true;
// MessageBox.Show("Thread " + i + " Started.");
}
public void timerElapsed(object sender, ElapsedEventArgs e, string address, int port, int i)
{
bfdeviceimp.newconnect(address, port, i);
}
and then finally my device class:
class bf2300deviceimp : IdeviceInterface
{
public void newconnect(string address, int port, int buttonNumber)
{
//send data
byte[] bData = new byte[71];
bData[0] = 240;
bData[1] = 240;
bData[2] = 0;
bData[3] = 1;
bData[68] = 240;
bData[69] = 240;
bData[70] = this.newCalculateCheckSum(bData);
try
{
byte[] result = this.newSendCommandResult(address, port, bData, 72);
//form1.setAlarmColour(result, buttonNumber);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Where would you suggest i put the statechanged handler?
You should to use an events-based approach for solving this problem, as is often the case when it comes to passing information between forms. Each of your devices should have a custom event that they define which is fired when the state of that device changes. The event should probably just be defined in the interface for interacting with that device. The form, when it creates the various device classes should subscribe to the event and in the event handler it should update the button/textbox appropriately.
This might be a fair bit to take in if you're not used to this style of programming. Feel free to ask for more details in the comments and I can elaborate on why I did something the way I did or what it actually does.
public Form1()
{
InitializeComponent();
//not sure if this is on initialization or in a button click event handler or wherever.
IDevice device = new SomeDevice();
device.StatusChanged += GetHandlerForDevice(1);
device.DoStuff();
IDevice device2 = new SomeDevice(); //could be another class that implements IDevice
device.StatusChanged += GetHandlerForDevice(2);
device.DoStuff();
}
/// <summary>
/// The handlers for device status changed only vary based on the button number for each one.
/// This method takes a button number and returns an event handler that uses that button number.
/// </summary>
/// <param name="buttonNumber"></param>
/// <returns></returns>
private EventHandler<StatusChangedEventArgs> GetHandlerForDevice(int buttonNumber)
{
//use currying so that the event handler which doesn't have an appropriate signature
//can be attached to the status changed event.
return (sender, args) => device_StatusChanged(sender, args, buttonNumber);
}
private void device_StatusChanged(object sender, StatusChangedEventArgs args, int buttonNumber)
{
this.Invoke((MethodInvoker)delegate
{
txtOutput1.Text = (args.CurrentStatus == IDevice.Status.Green ? "HIGH" : "LOW"); // runs on UI thread
if (args.CurrentStatus == IDevice.Status.Green)
{
this.Controls["btn" + buttonNumber].BackColor = Color.Green;
}
else
{
this.Controls["btn" + buttonNumber].BackColor = Color.Red;
}
});
}
public interface IDevice
{
event EventHandler<StatusChangedEventArgs> StatusChanged;
Status CurrentStatus { get; }
public enum Status
{
Green,
Red
}
void DoStuff();
// rest of interface ...
}
public class StatusChangedEventArgs : EventArgs
{
public IDevice.Status CurrentStatus { get; set; }
//can add additional info to pass from an IDevice to a form if needed.
}
public class SomeDevice : IDevice
{
public event EventHandler<StatusChangedEventArgs> StatusChanged;
private IDevice.Status _currentStatus;
/// <summary>
/// Gets the current status of the device this object represents.
/// When set (privately) it fires the StatusChanged event.
/// </summary>
public IDevice.Status CurrentStatus
{
get { return _currentStatus; }
private set
{
_currentStatus = value;
if (StatusChanged != null)
{
StatusChangedEventArgs args = new StatusChangedEventArgs();
args.CurrentStatus = value;
StatusChanged(this, args);
}
}
}
public void DoStuff()
{
//... do stuff
CurrentStatus = IDevice.Status.Green; //will fire status changed event
}
}
Move all the logic inside a method in the form and use it externally.
In your form create a property
public SynchronizationContext SyncContext { get; set;}
In form constructor add:
this.SyncContext = WindowsFormsSynchronizationContext.Current;
Make interface:
public Interface IChangeClient
{
void Process(<some_type> result); // place your logic here
}
(or somethng like that) and implement it in your Form to change your buttons and text.
Extend your original interface to use (maybe as a parameter) SynchronizationContext and IBackgroundChangeClient.
And than your code would look like this:
if (result != null)
{
oSyncContext.Post(new System.Threading.SendOrPostCallback(
delegate(object state)
{
IBackgroundChangeClient client = (state as object[])[0] as IBackgroundChangeClient
//i dont konw the type of this
var innerResult= (state as object[])[1];
client.Process(innerResult);
}), new object[] { oBackgroundChangeClient, result[4]});
}

C# Threading - an array of threads, where each thread contains a form with an image

I have an array of five threads. Each thread contains the same form, each form is put on to the screen in a different location (still working on that method :P).
I am trying to have each form load its contents (an image) before the other forms have finishing being placed. At the moment this works for the first form, but the others are blank or disappear :P
Originally each form would be placed but the method would need to finish before all the forms contents were displayed.
Any help would be appreciated, thanks :)
public partial class TrollFrm : Form
{
int number = 0;
public TrollFrm()
{
InitializeComponent();
startThreads();
}
private void TrollFrm_Load(object sender, EventArgs e)
{
}
private void TrollFrm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
public void startThreads()
{
Thread[] ThreadArray = new Thread[5];
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
}
public void createForm()
{
Form frm = new TrollChildFrm();
Random randomX = new Random();
Random randomY = new Random();
number++;
int xValue;
int yValue;
if (number % 2 == 0) //number is even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) + 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) - 200;
}
else //number is not even.
{
xValue = (Convert.ToInt32(randomX.Next(1, 1920))) - 200;
yValue = (Convert.ToInt32(randomY.Next(1, 1080))) + 200;
}
frm.Show();
frm.Location = new Point(xValue, yValue);
Thread.Sleep(1000);
}
Your forms are not displaying correctly because they are not running on a thread with a message loop. The general rule is that all UI element accesses must occur on the main UI thread.
Since you have a call to Thread.Sleep(1000) I am going to assume that you want to wait 1 second between the initial display of each form. In that case I would use a System.Windows.Forms.Timer who's Tick event will call createForm directly. Enable the timer, let 5 Tick events come through, and then disable the timer. I see no need to create any threads at all.
The reason your forms aren't displaying is because you are running inside one method on the main UI thread. Instead, you could create a method that spawns a new form and launch that at certain intervals on another thread (making sure the form handling is done on the main UI thread). So you could do something like:
(Pseudo Code)
private const int TIME_THRESHOLD = 100;
int mElapsedTime = 0;
Timer mTimer = new Timer();
.ctor
{
mTimer.Elapsed += mTimer_Elapsed;
}
private void mTimer_Elapsed(...)
{
mElapsedTime++;
if (mElapsedTime >= TIME_THRESHOLD)
{
mElapsedTime = 0;
SpawnForm();
}
}
private void SpawnForm()
{
// Make sure your running on the UI thread
if (this.InvokeRequired)
{
this.BeginInvoke(new Action(SpawnForm));
return;
}
// ... spawn the form ...
}
This is just an example of what I was proposing - it would not look exactly like this in the code, but this should give you an idea of the execution steps.
I would suggest to use Thread.Sleep(1000) in this manner
Caller section
for (int i = 0; i < 5; i++)
{
ThreadArray[i] = new Thread(new ThreadStart(createForm));
ThreadArray[i].Start();
}
Thread.Sleep(1000);
Also in the method that executing the work for the thread.
while(!something)
{
Thread.Sleep(1000)
}

C# WinForms position a form relative to another form

In my application I have a mainform. When the open button is clicked I want to show a second (borderless) form whith the text loading. I've got this working so far.
But what I want is that the loading form is centered relative to the mainform. How do I do this?
SOLUTION:
private void tsbOpen_Click(object sender, EventArgs e)
{
if (_fileDialog.ShowOpenDialog() == DialogResult.OK)
{
_progress = new frmProgress(); // _progress is a member var
backgroundWorker1.RunWorkerAsync("open");
_progress.ShowDialog(this);
}
}
You can set StartPosition to CenterParent and pass the mainform as an Owner.
I created a subform named ProcessingRequest and I put some text and an animated gif on it.
I have a Property in my main form that calculates the location my sub form should be in.
private Point ProcessingLocation { get { return new Point(this.Location.X + this.Width / 2 - new ProcessingRequest().Width / 2, this.Location.Y + this.Height / 2 - new ProcessingRequest().Height / 2); } }
I have a class that makes a new thread to show the sub form.
public class ShowProgress
{
static private System.Drawing.Point point;
static private ProcessingRequest p;
static public void ShowProgressForm(System.Drawing.Point myPoint)
{
point = myPoint;
Thread t = new Thread(new ThreadStart(ShowProgress.ShowForm));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
static private void ShowForm()
{
p = new ProcessingRequest();
p.StartPosition = FormStartPosition.Manual;
p.Location = point;
p.TopMost = true;
Application.Run(p);
}
static public void CloseForm()
{
p.Invoke(new CloseDelegate(ShowProgress.CloseFormInternal));
}
static private void CloseFormInternal()
{
p.Close();
}
}
public delegate void CloseDelegate();
Then in my main form I simply put
ShowProgress.ShowProgressForm(ProcessingLocation);
//heavy processing code goes here or whatever
ShowProgress.CloseForm();
:)
Martijn try this
at the start of the method put some code like this
public sub Bah()
{
if (me.InvokeRequired)
{
me.Invoke(new action(Bah));
return
}
myform.showdialog...
}
dont know if this code compiles to 100% but you get the idea
Get the position of the main form coordinates and its size and take the size of child form and put some simple mathematics on it.

How might I create and use a WebBrowser control on a worker thread?

I am creating an application that does screen shots of websites using the following method http://pietschsoft.com/post/2008/07/C-Generate-WebPage-Thumbmail-Screenshot-Image.aspx
I tried to make the application multithreaded but I have run into the following error:
[ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.]
Any suggestions how to fix this issue? My code is basically as follows:
List<string> lststrWebSites = new List<string>();
lststrWebSites.Add("http://stackoverflow.com");
lststrWebSites.Add("http://www.cnn.com");
foreach (string strWebSite in lststrWebSites)
{
System.Threading.ThreadStart objThreadStart = delegate
{
Bitmap bmpScreen = GenerateScreenshot(strWebSite, -1, -1);
bmpScreen.Save(#"C:\" + strWebSite + ".png",
System.Drawing.Imaging.ImageFormat.Png);
};
new System.Threading.Thread(objThreadStart).Start();
}
The GenerateScreenShot() function implementation is copied from the above URL:
public Bitmap GenerateScreenshot(string url)
{
// This method gets a screenshot of the webpage
// rendered at its full size (height and width)
return GenerateScreenshot(url, -1, -1);
}
public Bitmap GenerateScreenshot(string url, int width, int height)
{
// Load the webpage into a WebBrowser control
WebBrowser wb = new WebBrowser();
wb.ScrollBarsEnabled = false;
wb.ScriptErrorsSuppressed = true;
wb.Navigate(url);
while (wb.ReadyState != WebBrowserReadyState.Complete)
{ Application.DoEvents(); }
// Set the size of the WebBrowser control
wb.Width = width;
wb.Height = height;
if (width == -1)
{
// Take Screenshot of the web pages full width
wb.Width = wb.Document.Body.ScrollRectangle.Width;
}
if (height == -1)
{
// Take Screenshot of the web pages full height
wb.Height = wb.Document.Body.ScrollRectangle.Height;
}
// Get a Bitmap representation of the webpage as it's rendered in
// the WebBrowser control
Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
wb.Dispose();
return bitmap;
}
WebBrowser, like many ActiveX controls, has strict threading requirements. The thread that creates it must be initialized with Thread.SetApartmentState() to switch it to STA. And the thread must pump a message loop, you get one from Application.Run().
That makes talking to the browser pretty tricky. Here's code to get you started. Beware that the Completed callback runs on a background thread. Don't forget to call Dispose() to shut down the thread.
using System;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;
class WebPagePump : IDisposable {
public delegate void CompletedCallback(WebBrowser wb);
private ManualResetEvent mStart;
private SyncHelper mSyncProvider;
public event CompletedCallback Completed;
public WebPagePump() {
// Start the thread, wait for it to initialize
mStart = new ManualResetEvent(false);
Thread t = new Thread(startPump);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
mStart.WaitOne();
}
public void Dispose() {
// Shutdown message loop and thread
mSyncProvider.Terminate();
}
public void Navigate(Uri url) {
// Start navigating to a URL
mSyncProvider.Navigate(url);
}
void mSyncProvider_Completed(WebBrowser wb) {
// Navigation completed, raise event
CompletedCallback handler = Completed;
if (handler != null) handler(wb);
}
private void startPump() {
// Start the message loop
mSyncProvider = new SyncHelper(mStart);
mSyncProvider.Completed += mSyncProvider_Completed;
Application.Run(mSyncProvider);
}
class SyncHelper : Form {
WebBrowser mBrowser = new WebBrowser();
ManualResetEvent mStart;
public event CompletedCallback Completed;
public SyncHelper(ManualResetEvent start) {
mBrowser.DocumentCompleted += mBrowser_DocumentCompleted;
mStart = start;
}
public void Navigate(Uri url) {
// Start navigating
this.BeginInvoke(new Action(() => mBrowser.Navigate(url)));
}
void mBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
// Generated completed event
Completed(mBrowser);
}
public void Terminate() {
// Shutdown form and message loop
this.Invoke(new Action(() => this.Close()));
}
protected override void SetVisibleCore(bool value) {
if (!IsHandleCreated) {
// First-time init, create handle and wait for message pump to run
this.CreateHandle();
this.BeginInvoke(new Action(() => mStart.Set()));
}
// Keep form hidden
value = false;
base.SetVisibleCore(value);
}
}
}
Try setting the ApartmentState of the thread hosting the browser control:
var thread = new Thread(objThreadStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Does changing the attribute on your Main method from STAThread to MTAThread help?
Example:
[STAThread]
public static void Main()
{
changes to:
[MTAThread]
public static void Main()
{

Categories

Resources