现在我有C#代码在不同的线程中生成一个新窗口,这是有效的,但是一旦新生成的窗口打开,它就会关闭并且线程结束。我怎么做才能从第一个线程关闭新生成的窗口?
以下是产卵目前如何运作的“树”:
主线程
- 在主线程中使用一个函数在另一个线程中启动另一个函数来打开w窗口,导致窗口使用该线程。
基本上我只想让每个窗口都有自己的线程。并且能够从第一个窗口线程控制生成的辅助窗口。
答案 0 :(得分:28)
我打赌你正在做的事情是这样的:
new Thread(() => new TestForm().Show()).Start();
因为这会使窗口立即消失,就像你描述的那样。
请改为尝试:
new Thread(() => new TestForm().ShowDialog()).Start();
ShowDialog旋转自己的消息泵,仅在窗口关闭时返回。
答案 1 :(得分:15)
这只是一个简单的例子。它比我写的第一个更强大。它通过使用p / invoke消除了现有的竞争条件。
已更新仍有竞争条件。这个应该完美。
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";
}
}
答案 2 :(得分:2)
你可以这样做:
在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;
/// <summary>
/// The main entry point for the application.
/// </summary>
[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()调用,只是你不必每次都分配另一个线程,如果它们依赖于本地数据或未完成的子线程,那么线程会带来风险。
答案 3 :(得分:1)
你是如何从第二个线程创建新窗口的?在创建窗口后线程会做什么?
在没有看到代码的情况下,我猜测问题是你的第二个线程没有在Windows消息队列中抽取消息。
你是否在第二个帖子上打电话给Application.Run
?
顺便说一句:请注意您的设计有一些限制。第一个线程将无法直接控制第二个窗口。每当您尝试从第一个线程操作第二个窗口上的任何UI元素时,您将不得不使用Control.Invoke
来确保在正确的线程上进行实际的UI操作。
答案 4 :(得分:1)
我正在编写一个线程化的应用程序,并使用创建的线程上的UI将绘图功能分派给DC。
当我们将应用程序移植到命令提示符时,我们自然会因为没有创建或需要调度程序线程而留下一些问题 - 所以我从应用程序入口点创建了另一个基本上称为ShowDialog()的线程(在主窗体上旋转消息泵的唯一方法) - 使用覆盖的OnShown永久隐藏并在调用时最小化窗体。这使我仍然可以调度到表单并处理来自其他多个线程的所有绘图。
这当然是一种丑陋的方法,但这是完成它的快速方法,并且它按预期工作。
答案 5 :(得分:1)
对于我正在处理的项目,我创建了一个弹出的窗体,在任务运行时保持打开状态,然后关闭。
它包含一个ProgressBar,其中包含以下设置:
progressBar1.Style=ProgressBarStyles.Marquee
progressBar1.MarqueeAnimationSpeed =
&lt; - 以毫秒为单位设置您的自定义速度如果您愿意,可以将表单的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
命令,以显示如何从另一个表单与此表单进行交互的示例。或许还应该提到Main
中的Program.cs
应该具有[STAThread]
属性,如下所示。
[STAThread]
static void Main(string[] args)
{
System.Globalization.CultureInfo.DefaultThreadCurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}