簡體   English   中英

生成一個新線程以打開一個新窗口並從另一個線程關閉它

[英]Spawn a new thread to open a new window and close it from a different thread

現在我有 C# 代碼可以在不同的線程中生成一個新窗口,這是可行的,但是一旦新生成的窗口打開,它就會關閉並且線程結束。 我將如何做到這一點,以便可以從第一個線程關閉新生成的窗口?

這是當前產卵工作方式的“樹”:

主線程
--使用主線程中的一個函數在單獨的線程中啟動另一個函數打開w窗口,導致該窗口使用那個線程。

基本上我只希望兩個窗口都有自己的線程。 並且能夠從第一個窗口線程控制生成的輔助窗口。

我敢打賭你在做什么是這樣的:

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

因為這會使窗口立即消失,就像您描述的那樣。

試試這個:

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

ShowDialog 旋轉它自己的消息泵,並且僅在窗口關閉時返回。

這只是一個簡單的例子。 它比我寫的第一個更健壯。 它通過使用 p/invoke 消除了現有的競爭條件。

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);
    }
}

你可以這樣做:

在 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() 調用,只是您不必每次都分配另一個線程,因為線程依賴於本地數據或未完成的子線程會帶來風險。

對於我正在處理的項目,我創建了一個會彈出的表單,在任務運行時保持打開狀態,然后關閉。

它包含一個具有以下設置的 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());
    }

您是如何從第二個線程創建新窗口的? 創建窗口后,線程將執行什么操作?

如果沒有看到代碼,我可能會想到問題是您的第二個線程沒有在Windows消息隊列中泵送消息。

您是否在第二個線程上調用Application.Run

順便說一句:請注意您的設計有一些限制。 第一個線程將無法直接控制第二個窗口。 每當您嘗試從第一個線程操作第二個窗口上的任何UI元素時,都必須使用Control.Invoke以確保實際的UI操作發生在正確的線程上。

我正在編寫一個線程應用程序,並使用創建的線程上的 UI 將繪圖功能分派到 DC。

當我們移植應用程序以從命令提示符運行時,我們自然會遇到一些問題,因為沒有創建或不需要調度程序線程 - 所以我從應用程序入口點創建了另一個線程,該線程本質上稱為 ShowDialog()(唯一的在主窗體上旋轉消息泵的方法) - 使用覆蓋的 OnShown 在進行呼叫時永久隱藏和最小化窗體。 這使我仍然可以調度到表單並處理來自其他多個線程的所有繪圖。

這當然是一種丑陋的方法,但這是一種快速完成的方法,並且按預期工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM