Пустые компоненты в форме при ее обновлении из потока

Я создал форму, которая перемещается по экрану влево, но все компоненты формы пусты.

Я поместил код для движения в комментарии, и все было хорошо, поэтому проблема в моем коде движения, но я не знаю, в чем проблема.

System.Threading.Thread thread;

private void Form1_Load(object sender, EventArgs e)
{
    thread = new System.Threading.Thread(loop);
    thread.Start();
}

void loop()
{
    this.BeginInvoke((Action)delegate () {
        int x = 0;
        int y = 0;
        int MoveRate = 1;
        Point TopLeft = this.Location;
        Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
        while (true)
        {
            x = x + MoveRate;
            this.Location = new Point(x, 150);
            System.Threading.Thread.Sleep(10);
        }
    });     
}

Это должно привести к перемещению формы влево, однако компоненты формы остаются пустыми.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Давайте посмотрим на метод loop():

void loop()
{
    this.BeginInvoke((Action)delegate () {
        int x = 0;
        int y = 0;
        int MoveRate = 1;
        Point TopLeft = this.Location;
        Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
        while (true)
        {
            x = x + MoveRate;
            this.Location = new Point(x, 150);
            System.Threading.Thread.Sleep(10);
        }
    });     
}

Этот код немедленно вызывает делегата в основном потоке пользовательского интерфейса. Делегат запускает цикл while(true), который никогда не завершается. Как только цикл начинает выполняться, поток пользовательского интерфейса полностью закрывается без всякой надежды когда-либо отвечать на другие сообщения о событиях, включая события рисования.

Попробуйте вместо этого:

void loop()
{
    int x = 0;
    int MoveRate = 1;

    while(true)
    {
        x += MoveRate;
        this.BeginInvoke((Action)delegate () { this.Location = new Point(x, 150); });
        System.Threading.Thread.Sleep(16);
    } 
}

Это все тот же код (за исключением того, что ничего не делало), но теперь он организован так, что делегат вызывается внутри цикла. Поток пользовательского интерфейса блокируется только на короткое время, а затем управление возвращается обратно потоку цикла, который на некоторое время переходит в спящий режим, прежде чем снова беспокоить пользовательский интерфейс. Вызов даже достаточно прост, я смог переписать его как одну строку.

Заметьте, я также увеличил время сна, потому что это по-прежнему дает вам 60 кадров в секунду.

Вариант того же процесса Async для тестирования чего-то другого. (Основная причина, по которой ваш поток работает не так, как ожидалось, уже объяснялась. Если вы используете поток, а затем вызываете поток пользовательского интерфейса в закрытом цикле, это более или менее похоже на то, чтобы ваш код вообще не запускался в другом потоке : Форма не успевает обновлять себя или свои элементы управления).

Этот метод добавляет завершение к процедуре прокрутки, когда форма прокручивается за пределы текущего экрана. Когда это условие выполнено, цикл while завершается, и задача завершается, перемещая форму в центр экрана.

Задача запускается в событии Shown. Я думаю, что это более уместно, чем мероприятие Load (Форма готова к представлению здесь).

Обратите внимание, что ни эта задача, ни поток не добавляют никаких проверок для события Form.FormClosing, чтобы отменить асинхронную процедуру: если форма закрывается во время прокрутки, у вас, скорее всего, будет исключение (форма была удалена, поэтому больше нет ручка).

private async Task Scroller(int ScreenWidth)
{
    int x = 0;
    int MoveRate = 2;

    while (true)
    {
        x += MoveRate;
        this.BeginInvoke(new MethodInvoker(() => { this.Location = new Point(x, 150);}));
        await Task.Delay(10);
        if (x > ScreenWidth) break;
    };
}

private async void Form_Shown(object sender, EventArgs e)
{
    int ScreenWidth = Screen.FromHandle(this.Handle).Bounds.Width;
    await this.Scroller(ScreenWidth);
    this.Location = new Point((ScreenWidth - this.Width) / 2 , 150);
}

Другие вопросы по теме