Добавить элемент управления в форму из другого потока

Я пытался отложить добавление элементов управления в свою основную форму, чтобы ускорить время ее запуска. Ну, я запускаю следующее исключение:

Недопустимая межпоточная операция: доступ к элементу управления «Form1» осуществляется из потока, отличного от потока, в котором он был создан.

Я попытался просто решить проблему на меньшем примере, но проблема остается. Вот мой код:

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace AddConrolFromAnotherThread {
    public partial class Form1 : Form {

        public Form1() {
            InitializeComponent();
        }


        private void AddButton() { 
            if(this.InvokeRequired){
                this.Invoke(new MethodInvoker(this.AddButton));
            }
            Random random = new Random(2);
            Thread.Sleep(20);
            Button button = new Button();
            button.Size = new Size(50,50);
            button.Location = 
                new Point(random.Next(this.Width),random.Next(this.Height));
                this.Controls.Add(button);
        }

        private void buttonStart_Click(object sender, EventArgs e) {
            Thread addControlThread = 
                new Thread(new ThreadStart(this.AddButton));
            addControlThread.Start();
        }
    }
}

Я использовал метод Invoke и проверил, истинно ли InvokeRequiered, но InvokeRequiered продолжает оставаться «истинным». Я действительно этого не понимаю. По крайней мере, я бы ожидал исключения StackOverflow, поскольку это рекурсивный вызов.

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


person Gico    schedule 12.02.2010    source источник


Ответы (4)


Проблема в вашем коде в том, что вы добавляете две кнопки.

Поместите код после блока if в блок else.

private void AddButton() { 
        if(this.InvokeRequired){
            this.Invoke(new MethodInvoker(this.AddButton));
        }
        else {
           Random random = new Random(2);
           Thread.Sleep(20);
           Button button = new Button();
           button.Size = new Size(50,50);
           button.Location = new Point(random.Next(this.Width),random.Next(this.Height));
           this.Controls.Add(button);
        }
    }
person Jehof    schedule 12.02.2010

Вы можете сделать это как ....

private void AddButton() { 
    if(this.InvokeRequired) {
        Invoke((MethodInvoker)delegate ()
        {
            Random random = new Random(2);
            Thread.Sleep(20);
            Button button = new Button();
            button.Size = new Size(50,50);
            button.Location = new 
            Point(random.Next(this.Width),random.Next(this.Height));
            this.Controls.Add(button);
     });
}
person Community    schedule 15.10.2018

Вместо этого используйте анонимные методы. Объяснение ниже.

Если у нас есть такая форма:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Thread t = new Thread(new ThreadStart(Start));
        t.Start();
    }

    private void UpdateText()
    {
        button1.Text = "New Text";
    }

    void Start()
    {
        UpdateText();
    }
}

Это вызовет исключение.

Измените UpdateText на:

private delegate void MyDelegate();

private void UpdateText()
{
    if (button1.InvokeRequired)
    {
       button1.Invoke(new MyDelegate(UpdateText));
    }
    button1.Text = "New Text";
}

или используйте анонимный метод:

void Start() 
{
    this.Invoke((MyDelegate)delegate
    {
        UpdateText();
    });
}

private void UpdateText()
{
    button1.Text = "New Text";
}
person sashaeve    schedule 12.02.2010
comment
Привет, Саш. Спасибо за быстрый ответ. Но я не уверен, что мы понимаем друг друга. Я проверил, требует ли форма вызов Invoke. И в моем случае я не обновляю элемент управления, я просто пытаюсь добавить новый элемент в форму. - person Gico; 12.02.2010
comment
Разница не так велика — вам нужно создать элемент управления, который вы хотите добавить, в том же потоке, что и Form (например, после InitializeComponent), а затем просто добавить его из отдельного потока, используя подходы, которые я описал. - person sashaeve; 12.02.2010

Использовать поток только для добавления кнопки очень дорого! Вместо этого используйте ThreadPool.

person Islam Yahiatene    schedule 12.02.2010