在Winforms

时间:2016-09-15 14:26:03

标签: c# winforms

在WPF中,可以使用类似的东西:

Application.Current.Dispatcher.BeginInvoke(new Action(() => Form1.grid.Items.Refresh()));

访问主线程之外的UI函数。但是在Winforms中,没有相同的功能。从我的“工作”线程访问我的Form1类中存在的BindingList最简单的方法是什么?目前,在尝试访问“Form1.record_list”时出现以下错误:

System.InvalidOperationException: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.

编辑:到目前为止我很欣赏这个帮助,但我迷失了“this.Invoke”。我在单独线程中的方法没有“调用”。

这是我目前为止的代码示例。

        public static void listen(IPEndPoint server_ip)
    {
        Console.WriteLine("In listen");
        while (true)
        {
            try
            {
                byte[] received_bytes = udp_client.Receive(ref server_ip);
                string received_data = Encoding.ASCII.GetString(received_bytes);
                Record record = JsonConvert.DeserializeObject<Record>(received_data);
                Form1.record_list.Add(record); //This is where I assume the problem spawns
            }

            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }




public partial class Form1 : Form
{
    public static BindingList<Record> record_list = new BindingList<Record> { };
    public static DataGridViewCellStyle style = new DataGridViewCellStyle();
    public Form1()
    {
        InitializeComponent();
        Thread thread = new Thread(SCMClient.connect);
        thread.IsBackground = true;
        thread.Start();
        FillData();           
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        foreach (DataGridViewRow row in dataGridView.Rows)
        {
            for (var i = 0; i < row.Cells.Count; i++)
            {
                Console.WriteLine(row.Cells[i].Value);
                if (row.Cells[i].Value as string == "OK")
                {
                    row.Cells[i].Style.BackColor = Color.Red;
                    Console.WriteLine("IF WAS TRUE");

                }
            }
        }
    }

我认为这里的具体问题是当我将Records添加到Forms1.record_list时。我不确定如何在不导致跨线程错误的情况下将项添加到该列表...

3 个答案:

答案 0 :(得分:4)

您必须仅从UI线程访问您的UI。但您可以使用Control.Invoke方法 - FormControl - 来确保您的代码是从UI线程运行的。

此外,您有一个静态BindingList,但我认为您希望以特定形式显示该列表的内容。您应该使BindingList成为实例成员...或获取对有效表单的引用。 Control.Invoke方法不是静态的。

有几种方法可以做到这一点。我会这样做:

首先,在Form类中创建一个方法,将记录添加到列表中。

public void AddRecord(Record r) {
    if(this.InvokeRequired) {
        this.Invoke(new MethodInvoker(() => this.AddRecord(r)));
    } else {
        this.record_list.Add(r);
    }
}

其次,您需要引用表单(在下一步中,即theForm变量)。

然后,在您的侦听器方法中,调用AddRecord方法,而不是直接在BindingList中添加记录。

public static void listen(IPEndPoint server_ip)
{
    Console.WriteLine("In listen");
    while (true)
    {
        try
        {
            byte[] received_bytes = udp_client.Receive(ref server_ip);
            string received_data = Encoding.ASCII.GetString(received_bytes);
            Record record = JsonConvert.DeserializeObject<Record>(received_data);
            theForm.AddRecord(record); // You need a Form instance.
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

答案 1 :(得分:0)

您正在寻找适用于WinForms和WPF的SynchronizationContext.Current

请注意,您需要从UI线程中获取其值,因为它是每个线程。

答案 2 :(得分:0)

以下内容将以winforms方式进行。曾尝试过一次。以下代码有助于从另一个线程更新UI线程中的标签。

string _string = "Call from another thread";
this.Invoke((MethodInvoker)delegate {
label1.Text = _string;
});