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(); });
}
Related
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();
}));
I have a class SendCountingInfo() and it will send a message to server every 5 minutes. The code inside this class are:
public void StartSendCountingInfo()
{
DoStartSendCountingInfo(300000);
}
private void DoStartSendCountingInfo(int iMiSecs)
{
_pingTimer = new System.Timers.Timer(iMiSecs);
_pingTimer.Elapsed += new System.Timers.ElapsedEventHandler(pingTimer_Elapsed);
_pingTimer.Start();
}
void pingTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
PingRemoteHost();
}
When I try to call it in the Windows Form class, it didn't work.
But, when I remove the timer and call PingRemoteHost() directly, it works. However, the form didn't load properly. It shows blank screen but the method PingRemoteHost() work.
Here is the code inside the windows form:
private void Layout_Load(object sender, EventArgs e)
{
tSystemChecker = new System.Timers.Timer(1000);
tSystemChecker.Elapsed += new System.Timers.ElapsedEventHandler(tSystemChecker_Elapsed);
tSystemChecker.Start();
this.WindowState = FormWindowState.Maximized;
}
void tSystemChecker_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UIThreadWork(this, delegate
{
try
{
SuspendLayout();
DoCheckHardwareStatus();
DoCheckLanguage();
SendCountingInfo sci = new SendCountingInfo();
sci.StartSendCountingInfo();
}
catch (Exception exp)
{
System.Diagnostics.Debug.WriteLine(exp.Message);
System.Diagnostics.Debug.WriteLine(exp.Source);
System.Diagnostics.Debug.WriteLine(exp.StackTrace);
}
ResumeLayout(true);
});
}
Do you have any idea what's wrong?
Use a thread and see if the problem persist
using System.Threading;
//Put this where you want to start the first timer
Thread thread = new Thread(dowork =>
{
public void StartSendCountingInfo();
}
If you are updating the GUI use for your controls
guicontrol.BeginInvoke((MethodInvoker)delegate()
{
guicontrol.Text = "aa";
//etc
});
This case is using C# WPF. I want to instantly disable a button after clicking it to prevent clicking it twice in short succession. I disabled the button in OnClick_Event but still clickable.
Part of source is as below.
private void Button_Click_UpdateBurndownChart(object sender, RoutedEventArgs e)
{
if(threadNotWorking)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
private void startWorkThread()
{
... ...
//after finish required process
updateButton.IsEnabled = true;
}
Is there any way to accomplish this?
you may want to use a dispatcher, there is probably a threading problem (callback function running on seperate thread and trying to access ui which runs on another thread). try this . .
updateButton.Dispatcher.BeginInvoke(
new ThreadStart(() => updateButton.IsEnabled = false),
System.Windows.Threading.DispatcherPriority.Input, null);
instead of
updateButton.IsEnabled = false;
What happens if you were instead to change the order of your events from:
updateButton.IsEnabled = false;
startWorkThread();
To
startWorkThread();
updateButton.IsEnabled = false;
Let me know how this goes.
What it looks like is that you are starting your thread then immediatly enabling your button before your thread has finished. You would be better off using a BackgroundWorker and enable your Button in the RunWorkerCompleted Event. Though you can do something similar by enabling your button using a BeginInvoke at the end of your Process.
public void doWork()
{
System.Threading.Thread.Sleep(10000); //Simulating your Process
Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate() { updateButton.IsEnabled = true; }), System.Windows.Threading.DispatcherPriority.Background);
}
Example with BackgroundWorker
using System.ComponentModel;
public partial class MainWindow : Window
{
BackgroundWorker bgw;
public MainWindow()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
updateButton.IsEnabled = true;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(10000); //Simulating your work
}
private void startWorkThread()
{
bgw.RunWorkerAsync();
}
private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (bgw.IsBusy != true)
{
updateButton.IsEnabled = false;
startWorkThread();
}
}
}
I am currently working on an office add-in and I need to show a notification dialog that displays progress, I'm using Philipp Sumi's wpf-notifyicon.
I need to display the notifyicon from a separate thread as I have a lot of code that already executes on the main thread, this causes the wpf-notifyicon to block and wait because the messages in the windows message queue are not being processed.
I know that I should rather execute this time consuming code on a separate thread, and display the notifyicon from the main thread and update it accordingly, but that is unfortunately not an alternative because this whole solution is single-threaded.
Example:
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
Thread showThread = new Thread(delegate()
{
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
});
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.TextB.Text = "Doing something...";
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.TextB.Text = "Done doing something...";
}
Update
I have been able to progress a little further with this updated code:
I'm creating the TaskbarIcon object on a new thread , and using Application.Run to process the application message loop on that thread...
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
Thread showThread = new Thread(delegate()
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
System.Windows.Forms.Application.Run();
});
showThread.SetApartmentState(ApartmentState.STA);
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Doing something...";
}));
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Done doing something...";
}));
}
I have solved my problem, I had to initialize the notifyIcon on a separate STA thread and use Application.Run in order to start pumping windows messages on that thread.
var myThread = new Thread(delegate()
{
notifyIcon = new NotifyIcon();
Application.Run();
});
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
Then I just had to Invoke the UI of my notification dialog.
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.