How to open Windows Form from with mdiParent in timer method? - c#

I have a MainForm and method which opens new window:
private void OpenWindow(object source, ElapsedEventArgs e)
{
var form = new SomeForm();
form.MdiParent = this;
form.Show();
}
And timer:
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(OpenWindow);
timer.Interval = 10000;
timer.Enabled = true;
And it throws error on setting MdiParent: form.MdiParent = this;
Cross-thread operation not valid: Control 'MainForm' accessed from a
thread other than the thread it was created on.
How can I solve this problem?

You could use one of the other Timers because they handle the threading differently.
Explained here: Why there are 5 Versions of Timer Classes in .NET?

Based on Threading Model article, I think this should works in your cas:
var form = new SomeForm();
if (form.InvokeRequired)
{
form.Invoke(new MethodInvoker(delegate {
form.MdiParent = this;
}));
}
form.Show();
Or like this:
Invoke(new Action(() =>
{
form.MdiParent = this;
form.Show();
}));

Related

C# WPF Cancel ProgressBar from a different Thread

I have a UserControl with a big table that is displaying values using a lot of converters. I am trying to display a ProgressBar in a new Window with Indeterminate State that is closing automatically when the UserControl Loaded event is fired.
This is the Thread creation in the backcode of my UserControl :
Thread progressBarThread = new Thread(new ThreadStart(delegate {
ProgressBarWindow progressBarWindow = new ProgressBarWindow();
progressBarWindow.IsIndeterminate = true;
progressBarWindow.LaunchProgressBarInBackground();
progressBarWindow.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => { Dispatcher.FromThread(progressBarThread).InvokeShutdown(); };
This code is "working", it is opening progressBarWindow but when I shutdown the thread using InvokeShutdown (the ugliest way to do it, i agree). The problem is that the DoWork from my backgroundWorker.
Here is the DoWork function :
private void BackgroundWorker_WaitUntilShouldStop(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
while (!bw.CancellationPending)
{
Thread.Sleep(500);
}
}
I would like to call my function contained in ProgressBarWindow to stop the DoWork from runnning and close the ProgressBarWindow normaly using :
progressBar.StopProgressBarInBackground();
This method is calling backgroundWorker.CancelAsync();
This will result in backgroundWorker terminating and progressBarWindow closing automatically.
But I don't have access to progressBar that is inside the progressBarThread. I tried to pass my UserControl using :
progressBarThread.Start(this);
this being the main window.
When trying to pass a variable from the main thread, this error is thrown :
An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code Additional information: The calling thread cannot access this object because a different thread owns it.
Does someone have a nice and correct way to do it without using myThread.InvokeShutdown() ?
Edit 1 :
I found a solution to my problem using a volatile variable :
volatile bool _isLoaded;
void CreateAndStopProgressBarWhenIsLoaded()
{
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += (sender, e) => {
if (_isLoaded)
progressBar.StopProgressBarInBackground();
};
// Try to stop `progressBar` every 500 ms
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
dispatcherTimer.Start();
progressBar.ShowDialog();
// Will only be reached once progressBar.ShowDialog(); returns
dispatcherTimer.Stop();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
_isLoaded = this.IsLoaded;
progressBarThread.Join(); // Wait for progressBarThread to end
};
}
Now the question is do you have a better solution ?
Edit 2 :
Here is my final solution thanks to #AlexSeleznyov :
void CreateAndStopProgressBarWhenIsLoaded()
{
Controls.ProgressBar.ProgressBar pb = null;
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
Thread progressBarThread = new Thread(new ThreadStart(delegate
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb = progressBar;
manualResetEvent.Set();
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb.Dispatcher.Invoke(delegate {
manualResetEvent.WaitOne();
pb.StopProgressBarInBackground();
});
progressBarThread.Join();
};
}
You might try this approach, to cache ProgressBar instance and then use it from another thread. Dispatcher.Invoke eradicates need for CheckAccess I've mentioned in comments.
void CreateAndStopProgressBarWhenIsLoaded()
{
var pb = new Controls.ProgressBar.ProgressBar[1];
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb[0] = progressBar;
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb[0].Dispatcher.Invoke(()=>pb[0].Close());
};
}
I think that you can use BackgroundWorker.RunWorkerCompleted in ProgressBarWindow - it will be invoked when you cancel a backgroundWorker.
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close the window
}
}

BackgroundWorker using C#

I am totally lost. I just seem to get no response from it.
BackgroundWorker NewWorker;
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();
}
void NewWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
MessageBox.Show(Results.ToString());
}
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(1);
worker.ReportProgress((i * 10));
}
}
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Finished");
}
None of the message boxes appear, indicating it's finished.
What have I missed?
You're never calling RunWorkerAsync on the BackgroundWorker that you have configured.
You think you are, because you're calling StartWorker which, (I presume) contains the code NewWorker.RunWorkerAsync(); somewhere in it, however NewWorker, inside of StartWorker, is referring to an entirely different BackgroundWorker that you have not configured to do anything.
You have an instance field, NewWorker, and a local variable NewWorker inside of StartBackgroundWorker that is shadowing the instance field.
If you really need the background worker to be an instance field, then don't shadow it in StartBackgroundWorker and use the instance field throughout. If you don't need it to be an instance field (which, for the record, is likely, and needlessly promoting variables to instance fields makes for messier programs) then you simply need to start the BackgroundWorkder you created in StartBackgroundWorker. If StartWorker really has enough code that it needs to be in another method, then it probably means it should accept a BackgroundWorker as a parameter, so you can pass in the worker you created in StartBackgroundWorker.
The problem is that you never start your NewWorker. That's because your "global" NewWorker is always null.
See the fixed code:
NewWorker = new BackgroundWorker(); // this line is now fixed.
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
StartWorker();
Why do you need to make the background worker a field? Just make it in the constructor and everything else like. In its events you can just cast the object as the BackgroundWorker.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}
Francis is right... Any attempt to call MessageBox in a thread other than the GUI thread will block. You'll have to invoke:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => {
MessageBox.Show("Hello World!");
}));
Thank you to all your answers. The description supplied makes more sense to me now.
Also to the previous debate: the message box does block the thread. It got stuck at 10% and then crashed my computer (VM box).
Now my only issue is that e.results is returning an array of items. But it is in object.
I am finding it difficult to get the data back.
public void StartBackgroundWorker()
{
BackgroundWorker NewWorker = new BackgroundWorker();
NewWorker.DoWork += new DoWorkEventHandler(NewWorker_DoWork);
NewWorker.ProgressChanged += new ProgressChangedEventHandler(NewWorker_ProgressChanged);
NewWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(NewWorker_RunWorkerCompleted);
NewWorker.WorkerReportsProgress = true;
NewWorker.RunWorkerAsync();
}
void NewWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> ReturnResults = new List<string>();
BackgroundWorker worker = sender as BackgroundWorker;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select StatusCode from Win32_PingStatus where address = 'Metabox-PC'");
ManagementObjectCollection objCollection = searcher.Get();
foreach (ManagementObject Results in objCollection)
{
ReturnResults.Add(Results["StatusCode"].ToString());
}
e.Result = ReturnResults;
// Perform a time-consuming operation and report progress.
System.Threading.Thread.Sleep(1);
}
private void NewWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
txtPingable.text = e.Result;
}

Creating new window then closing it causes memory leak

I am generating new windows via the code below and displaying a webpage there and then close it. But after a while software gives out of memory exception. So there is memory leak happening. What might be the cause and how to solve it ? Thank you.
This is how do i start new window . i am doing a loop so there are being started thousands of new windows. As you can see after 60 seconds new window get itself close.
NewWindowThread<TitleWindow, string>(c => new TitleWindow(c), "the url that is going to be displayed at new window");
private void NewWindowThread<T, P>(Func<P, T> constructor, P param) where T : Window
{
Thread thread = new Thread(() =>
{
T w = constructor(param);
w.Show();
w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
public class TitleWindow : Window
{
WebBrowser webnew = new WebBrowser();
public TitleWindow(string srUrl)
{
DockPanel dk = new DockPanel();
dk.Width = 900;
dk.Height = 600;
this.AddChild(dk);
webnew.Navigated += new NavigatedEventHandler(wbMain_Navigated);
System.Windows.Threading.DispatcherTimer dispatcherTimer3 = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer3.Tick += new EventHandler(dispatcherTimer_Tick3);
dispatcherTimer3.Interval = new TimeSpan(0, 0, 0, 60, 0);
dispatcherTimer3.Start();
webnew.Height = 600;
webnew.Width = 900;
dk.Children.Add(webnew);
webnew.Navigate(srUrl);
this.WindowState = WindowState.Minimized;
}
void dispatcherTimer_Tick3(object sender, EventArgs e)
{
this.Close();
}
}
I immediately suspect that the WebBrowser control is not releasing it's resources when the window is closed as you are not calling it's Dispose() method. The WebBrowser control is a thin wrapper around the ActiveX MSHTML control.
Add a callback on the window Closing or Closed events to do this. e.g. webnew.Dispose().

c# EventArrivedEventHandler stuck in infinite loop, can't reference watcher

I've googled this problem for the past week, it's killing my peace! Please help... EventArrivedEventHandler is stuck in a loop, and if I stop it, then it won't catch events. But when I use a handler method, the thread is still concentrating on the loop, and won't give attention to the new form I'm trying to make in the handler! Strange thing is, if I just use something small, like a MessageBox, it doesn't cause an issue, just trying to instantiate a form causes the buttons to NOT draw. Then shortly after the program stops responding. In case you're wondering where the form code is, it's just a standard form made by .NET, that works everywhere else in the code except for in the event handler.
Thanks!
class MainClass
{
public static void Main()
{
TaskIcon taskbarIcon;
EventWatch myWatcher;
taskbarIcon = new TaskIcon();
taskbarIcon.Show();
myWatcher = new EventWatch();
myWatcher.Start();
Application.Run();
}
}
public class TaskIcon
{
public void Show()
{
NotifyIcon notifyIcon1 = new NotifyIcon();
ContextMenu contextMenu1 = new ContextMenu();
MenuItem menuItem1 = new MenuItem();
MenuItem menuItem2 = new MenuItem();
contextMenu1.MenuItems.AddRange(new MenuItem[] { menuItem1, menuItem2 });
menuItem1.Index = 0;
menuItem1.Text = "Settings";
menuItem1.Click += new EventHandler(notifyIconClickSettings);
menuItem2.Index = 1;
menuItem2.Text = "Exit";
menuItem2.Click += new EventHandler(notifyIconClickExit);
notifyIcon1.Icon = new Icon("app.ico");
notifyIcon1.Text = "Print Andy";
notifyIcon1.ContextMenu = contextMenu1;
notifyIcon1.Visible = true;
}
private static void notifyIconClickSettings(object Sender, EventArgs e)
{
MessageBox.Show("Settings Here");
}
private static void notifyIconClickExit(object Sender, EventArgs e)
{
//taskbarIcon.Visible = false; // BONUS QUESTION: Why can't I hide the tray icon before exiting?
Application.Exit();
}
}
public class EventWatch
{
public void Start()
{
string thisUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1];
WqlEventQuery query = new WqlEventQuery();
query.EventClassName = "__InstanceCreationEvent";
query.Condition = #"TargetInstance ISA 'Win32_PrintJob'";
query.WithinInterval = new TimeSpan(0, 0, 0, 0, 1);
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += new EventArrivedEventHandler(showPrintingForm);
watcher.Start();
}
void showPrintingForm(object sender, EventArrivedEventArgs e)
{
// MessageBox.Show("This will draw just fine");
Form1 myForm;
myForm = new Form1();
myForm.Show(); // This causes a hangup
}
}
My guess would be that the ManagementEventWatcher calls the EventArrived handler from a different thread than the UI thread. Then your showPrintingForm is executed on that thread and accessing UI from a different thread than the UI thread is bad. You need to marshal your code back onto the UI thread.

TopMost form in a thread?

I am using the following code to open a form in a new thread:
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
form.ShowDialog();
}
But the newly created form isn't TopMost even though I set it to true.
How can I make a form in a thread TopMost ?
Usually you don't need another thread, you open the form as usual in modal or non modal mode, if the form needs to do a heavy process then you do the process inside a thread.
Specific to your question one option is to run the form from an Application.Run as described here.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
using (Form1 _form = new Form1())
{
_form.TopMost = true;
Application.Run(_form);
}
}
}
That will launch a new thread with its own message pump and will keep it as a TopMost form.
Just ran into this problem myself. It seems that if the form has an Owner, then TopMost works as expected. If the owning form was created on another thread, though, it's a little tricky to set. Here's what I used:
var form = new Form();
form.Shown += (sender, e) => {
Control.CheckForIllegalCrossThreadCalls = false;
form.Owner = /* Owning form here */;
form.CenterToParent(); // Not necessary
Control.CheckForIllegalCrossThreadCalls = true;
form.TopMost = true; // Works now!
};
Application.Run(form);
Instead of calling ShowDialog directly, try using this.Invoke to gain ownership of the form.
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(ThreadProc);
thread.Start();
}
public void ThreadProc()
{
Form form = new Form();
form.TopMost = true;
this.Invoke((Action)delegate() { form.ShowDialog(); });
}

Categories