1

我是 C# 程序的新手。我想创建在主 UI 中移动标签而不卡住 UI 的线程,直到移动完成我构建了一些东西但它没有工作告诉我我的问题是什么

private void button1_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(Movelb);
        t.IsBackground = true;
        t.Start();enter code here
    }


private void DOsomeThing()
        {
            label2.Visible = true;
            label2.Location = new Point(0, 205);
            for (int i = 0; i < 533; i++)
            {
                label2.Location = new Point(i, 205);
                Thread.Sleep(10);
            }
            label1.Text="false";
        }
private void Movelb()
            {
                if (this.InvokeRequired)
                {
                    threadDel d = new threadDel(DOsomeThing);
                    this.BeginInvoke(d);
                }
                else
                    DOsomeThing();


    }
4

4 回答 4

1

不要使用线程来绘制表单或修改/更新表单内容。Windows 编程中推荐的范例是每个窗体或窗口一个线程。如果你想创建从不同线程运行的表单,那么你必须

  1. 首先创建新线程
  2. 在新线程上创建表单

这样,新线程将充当新表单的消息处理程序。但即便如此,您仍然应该在该线程中对表单进行所有操作(如果表单想要修改在不同线程上运行的另一个表单中的内容,则可能需要一些额外的线程安全通信技巧)。

要为窗口内容设置动画,您应该使用System.Windows.Forms.Timer它,它在表单的线程上与其他消息同步执行。不过,您需要将动画重新实现为状态机而不是for()循环构造。这意味着标签位置的变量将需要嵌入到 Form 类中,以便可以在 Timer 消息调用中保留更新。

于 2012-12-29T19:08:11.017 回答
1

您需要先了解事件模型。在 Windows、Android 或 Linux 等事件驱动的环境中……“自动”任务(例如坐标动画或其他属性)通常使用计时器完成,这些计时器不断将事件重新发送回推进动画/进程的处理程序。在您的特定示例中 - 如果您需要移动标签,请使用 Widows.Forms.Timer。阻止处理具有冗长任务的事件的 UI 线程是不合适的,因为 UI 线程将停止并且您的应用程序将冻结或变得生涩。现在,另一方面,在很多情况下,添加额外线程确实有很大帮助,什么时候?不是您的情况,因为与重绘相比,您只更改了与 CPU 无关的标签坐标,因此您使用额外线程的解决方案效率较低且比使用计时器复杂得多。

事件如何运作?

操作系统内部有一个无限循环,它会被键盘、鼠标和其他事件打断,但循环会无限循环,直到您关闭 Windows(或 Android 或 XWidnws...)。在循环结束时,操作系统查看“原始”鼠标/键事件并将它们分派到适当的应用程序队列中。它通过检查每个应用程序窗口列表来了解它,谁在顶部,因此它知道什么窗口/应用程序在某个 X,Y 鼠标坐标下。当事件被分派到您的应用程序时,您的工作是非常快速地处理它并在队列中查找另一个事件(队列绑定到 UI 线程/Windows)。

计时器如何工作?计时器是一种特殊的事件,操作系统可以从其内部“无限循环”定期向您发送。操作系统会跟踪请求通知的应用程序以及通知的频率 - 当时间到来时,它会将 WM_TIMER(在 MS Windows 上)添加到您的 Windows 队列中。这样你就不会阻塞任何东西,而是在你的代码中获得一个每 X 毫秒调用一次的方法。当您使用 .NET Timer 类时 - 它只是 Windows 用户 API 中 CreateTimer() KillTimer() 的包装器(我不记得确切的函数名称)。.NET Timer 也知道如何吞下 WM_TIMER 并为您调用 C# 事件/委托。

我希望这有帮助!

于 2012-12-29T19:00:55.663 回答
0

您的代码没有任何用处。它只是启动一个新的后台线程,然后调用一个委托,在同一个 UI 线程上执行,该线程已经启动......后台线程。

换句话说,您不能在工作线程中移动标签,因为移动标签会导致重绘,而这无法从后台线程完成。

于 2012-12-29T18:00:35.493 回答
0

我也有一个想法在一个线程中做一些工作 - 虽然这项艰巨的工作已经完成......应该修改 main-gui-form,这样用户就会发现一个进步。做了一些查找并进入“委托”、“事件处理程序”和“非常高级的代码片段”。我花了一些时间来修复,我想出了这个非常简单的例子。看一看。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProcessingUI

// You will find a form with "button1": will do some work in a seperate thread, and
//   therefore you are allowed to do action in main-gui-form while this work is done, 
//   due to async. operation.
//   While work is done in seperate thread - the main-gui-form will have a label modified... 
//   having the steps: 1,2,3,0.


// Also found... "button2": will do some work in same thread as gui, and
//   therefore you are not allowed to do action in main-gui-form while this work is done,
//   due to sync. operation (only one thread is established).
//   While work is done in one-and-only-thread - the main-gui-form will have a label modified... 
//   having the steps: 1,2,3,0.


{

    public delegate void UpdateTextDelegate();

    public partial class Form1 : Form
    {

        public delegate void SetStatusText(string statusText);
        public SetStatusText mySetStatusTextDelegate;

        public Form1()
        {
            InitializeComponent();
            mySetStatusTextDelegate = new SetStatusText(SetStatusTextMethod);
        }

        private void button1_Click(object sender, EventArgs e)   // do work from new thread.
        {
            Worker w = new Worker(this);
            Thread thread1 = new Thread(new ThreadStart(w.DoWork));
            thread1.Start();
        }

        private void button2_Click(object sender, EventArgs e)   // do work from local class - form is locked during 1-3 steps.
        {
            SetStatusTextMethod("1");
            Thread.Sleep(3000);

            SetStatusTextMethod("2");
            Thread.Sleep(3000);

            SetStatusTextMethod("3");
            Thread.Sleep(3000);

            SetStatusTextMethod("0");
        }

        public void SetStatusTextMethod(string statusText)
        {
            label1.Text = statusText;
            label1.Refresh();
        }
    }

    public class Worker
    {
        Form1 guiForm;    // holds form where "control-to-be-changes" is found.
        public Worker(Form1 _guiForm)
        {
            guiForm = _guiForm;
        }

        public void DoWork()   // while steps are being done - form can easily be moved around... is not locked!
        {
            // put "1/3" on form.
            guiForm.Invoke(guiForm.mySetStatusTextDelegate, "1");
            Thread.Sleep(3000);

            // put "2/3" on form.
            guiForm.Invoke(guiForm.mySetStatusTextDelegate, "2");
            Thread.Sleep(3000);

            // put "3/3" on form.
            guiForm.Invoke(guiForm.mySetStatusTextDelegate, "3");
            Thread.Sleep(3000);

            guiForm.Invoke(guiForm.mySetStatusTextDelegate, "0");
        }
    }


}
于 2021-01-08T17:15:31.373 回答