生成一个新线程以打开一个新窗口并从另一个线程关闭它

现在我有C#代码在不同的线程中生成一个新窗口,这是有效的,但是一旦新生成的窗口打开,它就会关闭并且线程结束。 我如何制作它以便可以从第一个线程关闭新生成的窗口?

这是产卵目前如何运作的“树”:

主线程
– 在主线程中使用一个函数在另一个线程中启动另一个函数来打开w窗口,导致窗口使用该线程。

基本上我只想让每个窗口都有自己的线程。 并能够从第一个窗口线程控制生成的辅助窗口。

这只是一个简单的例子。 它比我写的第一个更强大。 它通过使用p / invoke消除了现有的竞争条件。

更新 Still仍有竞争条件。 这个应该是完美的。

using System; using System.Drawing; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; class MainUIThreadForm : Form { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainUIThreadForm()); } private IntPtr secondThreadFormHandle; public MainUIThreadForm() { Text = "First UI"; Button button; Controls.Add(button = new Button { Name = "Start", Text = "Start second UI thread", AutoSize = true, Location = new Point(10, 10) }); button.Click += (s, e) => { if (secondThreadFormHandle == IntPtr.Zero) { Form form = new Form { Text = "Second UI", Location = new Point(Right, Top), StartPosition = FormStartPosition.Manual, }; form.HandleCreated += SecondFormHandleCreated; form.HandleDestroyed += SecondFormHandleDestroyed; form.RunInNewThread(false); } }; Controls.Add(button = new Button { Name = "Stop", Text = "Stop second UI thread", AutoSize = true, Location = new Point(10, 40), Enabled = false }); button.Click += (s, e) => { if (secondThreadFormHandle != IntPtr.Zero) PostMessage(secondThreadFormHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); }; } void EnableStopButton(bool enabled) { if (InvokeRequired) BeginInvoke((Action)(() => EnableStopButton(enabled))); else { Control stopButton = Controls["Stop"]; if (stopButton != null) stopButton.Enabled = enabled; } } void SecondFormHandleCreated(object sender, EventArgs e) { Control second = sender as Control; secondThreadFormHandle = second.Handle; second.HandleCreated -= SecondFormHandleCreated; EnableStopButton(true); } void SecondFormHandleDestroyed(object sender, EventArgs e) { Control second = sender as Control; secondThreadFormHandle = IntPtr.Zero; second.HandleDestroyed -= SecondFormHandleDestroyed; EnableStopButton(false); } const int WM_CLOSE = 0x0010; [DllImport("User32.dll")] extern static IntPtr PostMessage(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam); } internal static class FormExtensions { private static void ApplicationRunProc(object state) { Application.Run(state as Form); } public static void RunInNewThread(this Form form, bool isBackground) { if (form == null) throw new ArgumentNullException("form"); if (form.IsHandleCreated) throw new InvalidOperationException("Form is already running."); Thread thread = new Thread(ApplicationRunProc); thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = isBackground; thread.Start(form); } } 

这是后人的第一个例子:

 using System; using System.Drawing; using System.Threading; using System.Windows.Forms; class MainUIThreadForm : Form { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainUIThreadForm()); } SecondUIThreadForm secondThreadForm; public MainUIThreadForm() { Text = "First UI"; Button button; Controls.Add(button = new Button { Text = "Start second UI thread", AutoSize = true, Location = new Point(10, 10) }); button.Click += (s, e) => { if (secondThreadForm == null || !secondThreadForm.IsHandleCreated) secondThreadForm = SecondUIThreadForm.Create(); }; Controls.Add(button = new Button { Text = "Stop second UI thread", AutoSize = true, Location = new Point(10, 40) }); button.Click += (s, e) => { if (secondThreadForm != null && secondThreadForm.IsHandleCreated) secondThreadForm.Invoke((Action)(() => secondThreadForm.Close())); }; } } class SecondUIThreadForm : Form { static void Main2(object state) { Application.Run((Form)state); } public static SecondUIThreadForm Create() { SecondUIThreadForm form = new SecondUIThreadForm(); Thread thread = new Thread(Main2); thread.SetApartmentState(ApartmentState.STA); thread.Start(form); return form; } public SecondUIThreadForm() { Text = "Second UI"; } } 

我打赌你正在做的是这样的:

 new Thread(() => new TestForm().Show()).Start(); 

因为这会使窗口立即消失,就像你描述的那样。

试试这个:

  new Thread(() => new TestForm().ShowDialog()).Start(); 

ShowDialog旋转自己的消息泵,仅在窗口关闭时返回。

你可以这样做:

在Program.cs中

 using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Threading; namespace TwoWindows { static class Program { public static Form1 form1; public static Form2 form2; ///  /// The main entry point for the application. ///  [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); form1 = new Form1(); form2 = new Form2(); form1.Form2Property = form2; form2.Form1Property = form1; form1.Show(); form2.Show(); Application.Run(); } } } 

在Form1.cs中:

 namespace TwoWindows { public partial class Form1 : Form { public Form2 Form2Property { get; set; } public Form1() { InitializeComponent(); } protected override void OnClosed(EventArgs e) { if (Form2Property.IsDisposed) Application.Exit(); } } } 

和Form2.cs:

 namespace TwoWindows { public partial class Form2 : Form { public Form1 Form1Property { get; set; } public Form2() { InitializeComponent(); } protected override void OnClosed(EventArgs e) { if (Form1Property.IsDisposed) Application.Exit(); } } } 

这样,您可以在同一个线程上获取两个表单,并使用一个表单来控制另一个表单。 如果你需要使用线程,我建议使用属于类的一部分的专用线程,而不是在一个可以多次调用的方法中生成。 然后使用ManualResetEvent或AutoResetEvent来控制线程处理。 我非常喜欢使用类似这样的方法,因为它是安全的,并且不会花费太多资源来初始化线程。

 public class MyClassOrForm { Thread myProcessingThread; public AutoResetEvent startProcessing = new AutoResetEvent(false); public AutoResetEvent processingFinished = new AutoResetEvent(false); public AutoResetEvent killProcessingThread = new AutoResetEvent(false); public MyClassOrForm() { myProcessingThread = new Thread(MyProcess); } private void MyProcess() { while (true) { if (startProcessing.WaitOne()) { // Do processing here processingFinished.Set(); } if (killProcessingThread.WaitOne(0)) return; } } } 

然后,一旦设置了要处理的数据,就可以调用另一个类或方法

 MyClassOrMethodInstance.startProcessing.Set(); 

如果您需要等待该处理完成,则插入:

 MyClassOrMethodInstance.processingFinished.WaitOne(time_out_ms); 

这相当于一个Thread.Join()调用,只是你不必每次都分配另一个线程,如果它们依赖于本地数据或未完成的子线程,那么线程会带来风险。

你是如何从第二个线程创建新窗口的? 在创建窗口后线程会做什么?

在没有看到代码的情况下,我猜测问题是你的第二个线程不会在Windows消息队列中抽取消息。

你在第二个线程上调用Application.Run吗?

顺便说一句:请注意您的设计有一些限制。 第一个线程将无法直接控制第二个窗口。 每当您尝试从第一个线程操作第二个窗口上的任何UI元素时,您将不得不使用Control.Invoke来确保在正确的线程上进行实际的UI操作。

我正在编写一个线程化的应用程序,并使用创建的线程上的UI将绘图function分派给DC。

当我们移植应用程序以从命令提示符运行时,由于没有创建或需要调度程序线程,我们自然会留下一些问题 – 所以我从app入口点创建了另一个基本上称为ShowDialog()的线程(仅限于在主窗体上旋转消息泵的方法 – 使用覆盖的OnShown永久隐藏并在调用时最小化窗体。 这使我仍然可以调度到表单并处理来自其他多个线程的所有绘图。

这当然是一种丑陋的方法,但这是完成它的快速方法,并且它按预期工作。

对于我正在处理的项目,我创建了一个弹出的窗体,在任务运行时保持打开状态,然后关闭。

它包含一个ProgressBar,其中包含以下设置:

  • progressBar1.Style=ProgressBarStyles.Marquee
  • progressBar1.MarqueeAnimationSpeed = < - 在此设置您的自定义速度,以毫秒为单位

如果需要,可以将表单的TopMost属性设置为true

以下是表单的代码:

 public partial class BusyForm : Form { public BusyForm(string text = "Busy performing action ...") { InitializeComponent(); this.Text = text; this.ControlBox = false; } public void Start() { System.Threading.Tasks.Task.Run(() => { this.ShowDialog(); }); } public void Stop() { BeginInvoke((Action)delegate { this.Close(); }); } public void ChangeText(string newText) { BeginInvoke((Action)delegate { this.Text = newText; }); } } 

以下是在代码中使用表单的代码:

  BusyForm busyForm = new BusyForm(text: "Opening database ..."); busyForm.Start(); //do your stuff here busyForm.Stop(); 

更新:我遇到了线程的一些潜在问题。 这是代码的更新版本。 对于某些背景信息,此表单有一个进度条,在任务繁忙时显示。 我添加了ChangeText命令,以显示如何从另一个表单与此表单进行交互的示例。 还应该提一下,你在Program.cs中的Main应该具有[STAThread]属性,如下所示。

  [STAThread] static void Main(string[] args) { System.Globalization.CultureInfo.DefaultThreadCurrentCulture = System.Globalization.CultureInfo.InvariantCulture; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }