Создайте новый поток, чтобы открыть новое окно и закрыть его из другого потока. - PullRequest
13 голосов
/ 15 января 2011

Прямо сейчас у меня есть код C # для порождения нового окна в другом потоке, это работает, но как только открывается новое порожденное окно, оно закрывается и поток заканчивается.Как бы я сделал так, чтобы новое порожденное окно могло быть закрыто из первого потока?

Вот «дерево» того, как работает порождение в настоящее время:

Основной поток
-Использует функцию в главном потоке, чтобы запустить другую функцию в отдельном потоке, чтобы открыть окно w, в результате чего окно использует этот поток.

По сути, я просто хочу, чтобы каждое из двух окон имело свой собственный поток.И иметь возможность управлять порожденным вторичным окном из первого потока окна.

Ответы [ 6 ]

28 голосов
/ 15 января 2011

Бьюсь об заклад, то, что вы делаете, выглядит примерно так:

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

, потому что это заставляет окно исчезнуть немедленно, как вы описываете.1007 *

ShowDialog запускает собственный насос сообщений и возвращается только после закрытия окна.

15 голосов
/ 15 января 2011

Это всего лишь быстрый пример.Это немного надежнее, чем первый, который я написал.Он устраняет существующее состояние гонки с помощью 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 голосов
/ 08 ноября 2013

Вы можете сделать это следующим образом:

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

Затем, после того как вы настроили данные для обработки, вызовите другой класс илиmethod

MyClassOrMethodInstance.startProcessing.Set();

И если вам нужно дождаться окончания обработки, вставьте:

MyClassOrMethodInstance.processingFinished.WaitOne(time_out_ms);

Это эквивалентно вызову Thread.Join (), только то, что у вас неткаждый раз выделять другой поток с учетом рисков, связанных с потоками, если они зависят от локальных данных или незавершенных дочерних потоков.

1 голос
/ 22 июня 2017

Для проекта, над которым я работаю, я создал всплывающую форму, которая остается открытой, пока задача выполняется, и закрывается после нее.

Он содержит одну панель ProgressBar со следующими настройками:

  • progressBar1.Style=ProgressBarStyles.Marquee
  • progressBar1.MarqueeAnimationSpeed = <- задайте свою пользовательскую скорость в миллисекундах здесь </li>

Если вы хотите, вы можете установить 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());
    }
1 голос
/ 05 августа 2011

Я пишу приложение, которое является многопоточным и использует пользовательский интерфейс в созданном потоке для отправки функциональности рисования на контроллер домена.

Когда мы портировали приложение для запуска из командной строки, у нас, естественно, осталсяНемного проблем, так как поток диспетчера не был создан или не нужен - поэтому я создал другой поток из точки входа приложения, который по сути называется ShowDialog () (единственный способ запустить насос сообщений) в главной форме - с переопределенным OnShownнавсегда скрыть и минимизировать форму при совершении вызова.Это позволило мне по-прежнему отправлять в форму и обрабатывать все мои рисунки из других множественных потоков.

Это, конечно, уродливый подход, но это был быстрый способ сделать это, и он работает, как и ожидалось.

1 голос
/ 15 января 2011

Как вы создали новое окно из второго потока?И что делает поток после создания окна?

Не видя код, я бы предположил, что проблема в том, что ваш второй поток не качает сообщения в очереди сообщений Windows.

Вы звоните Application.Run во второй теме?

Кстати: обратите внимание, что у вашего дизайна есть некоторые ограничения.Первый поток не сможет напрямую контролировать второе окно.Всякий раз, когда вы пытаетесь манипулировать любым элементом пользовательского интерфейса во втором окне из первого потока, вы должны будете использовать Control.Invoke, чтобы убедиться, что фактическое манипулирование пользовательским интерфейсом происходит в правильном потоке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...