循环中的触发事件不是按顺序更新UI [英] Firing events in loop are not updating UI in sequence

查看:104
本文介绍了循环中的触发事件不是按顺序更新UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在UI上更新长期运行操作的状态。我已经创建了一个基于演示表单的应用程序,任务它有多行,每行都有几天,每行datagrid的默认值为0,一旦计算文件计算一天的迭代,它将更新UI并为此设置1天。



我正在使用线程,代理和事件来实现这一点,如果我在两个事件调用之间放置Thread.Sleep(100),它会按预期工作。如果我将Thread.Sleep(100)放在最后一个嵌套for循环内,那么它会按预期更新UI但是一旦我删除它并运行循环而没有睡眠,那么它会跳过UI上的一些列并直接更新最后几个/随机列,正如您在附加图像链接中所看到的(我的代码输出图像没有线程休眠),只有最后一列是得到更新..



如果我没有弄错,所有的事件都按顺序被解雇,那么他们也应该按顺序更新UI,但它没有发生,我不会知道为什么。我不想做这个睡眠的事情,因为我在实际的应用程序中有大约14个调用UI状态更新,它将在循环下运行,所以如果它放入睡眠(100)然后它将花费我很多,有什么办法没有睡觉就去做?



计算文件:

  public   class  Class1:IGenerate 
{
public event MessageEventHandler OnMessageSending;
public void LongOperationMethod(BindingList< Status> _statusData)
{
if (OnMessageSending!= null
{
MessageEventArgs me = new MessageEventArgs();

/// 周的循环
for int k = 0 ; k < 2 ; ++ k)
{
/// 每天循环
int i = 0 ; i < 7 ; ++ i)
{
/// 计算日常
int j = 0 ; j < 1000 ; ++ j)
{
// 要做
}

me.weekNo = k;
me.DayNo = i;
OnMessageSending(me);

}
}
me.Message = 流程已成功完成......;
OnMessageSending(me);
}
其他
{
throw new ArgumentException( 事件没有上升,所以我们不能继续工作。);
}
}
}



UI文件:

 < span class =code-keyword> public   partial   class  Form1:Form 
{
BindingList< Status> _statusData = new BindingList< Status>();
委托 void StringParameterDelegate( string value );
Class1 cls = new Class1();
public Form1()
{
InitializeComponent();
labelProgress.Text = ;
}

私有 void button1_Click_1( object sender,EventArgs e)
{
for int i = 1 ; i < = 2 ; ++ i)
{
_statusData.Add( new 状态{Week = + i,Day1 = 0 ,Day2 = 0 ,Day3 = 0 ,Day4 = 0 ,Day5 = 0 ,Day6 = 0 ,Day7 = 0 });
}

dataGridView1.DataSource = _statusData;
}

private void button2_Click( object sender,EventArgs e)
{
Thread t1 = new Thread(()= > StartingThread(_statusData));
t1.Start();
}

void StartingThread(BindingList< Status> _statusData)
{
IGenerate generate = new Class1();
generate.OnMessageSending + = new MessageEventHandler(generate_OnMessageSending);
generate.LongOperationMethod(_statusData);
}

private void generate_OnMessageSending(MessageEventArgs e)
{
int weekNo = e.weekNo;
int dayNo = e.DayNo;
.dataGridView1.BeginInvoke( new MethodInvoker(()= > dataGridView1.Rows [e.weekNo] .Cells [e.DayNo + 1 ]。Value = 1 ));
this .labelProgress.BeginInvoke( new MethodInvoker(()= > this .labelProgress.Text = e.Message));

}
}

解决方案

我认为问题是当你通过活动时参数,你不传递它是一个值,但作为参考。请参阅使用C#传递参数 [ ^ ]



因为你只创建一个<$的实例c $ c> MessageEventArgs me = new MessageEventArgs(); ,您在内存中只有一个存储数据的位置。

这意味着当您更改成员时在那种情况下,这种变化也会反映在事件方法中。

睡眠工作的原因是因为你减慢了进程的速度,以便在数据网格改变之前将值打印在数据网格中在循环中。



您应该在 MessageEventArgs 类中添加一个或多个带参数的构造函数。

例如:

  public  MessageEventArgs( int  weekNo, int  dayNo)
{
this .WeekNo = weekNo;
.DayNo = dayNo;
}





这样就很容易为每条消息创建一个新的事件参数实例。

  public   void  LongOperationMethod(BindingList< status> _statusData) 
{
if (OnMessageSending!= null
{
/// 周的循环
for int k = 0 ; k < 2 ; ++ k)
{
/// 每天循环
int i = 0 ; i < 7 ; ++ i)
{
/// 计算每日
for int j = 0 ; j < 1000 ; ++ j)
{
// 要做
}

OnMessageSending( new MessageEventArgs(k,i));

}
}
OnMessageSending( new MessageEventArgs( 流程成功完成......));
}
其他
{
throw new ArgumentException( 事件没有上升,所以我们不能继续工作。);
}
}
< / 状态 >


I was trying to update status on UI for a Long Running Operating. I've created a demo form based application, task it have multiple rows, each row is having days and default values in each column of datagrid is 0, once computation file computes one iteration for one day it will update UI and set 1 for that day.

I am using threading, delegates and events to implement this and it is working as expected if I put Thread.Sleep(100) between two event calls. If I put Thread.Sleep(100) inside last nested for loop then it updates UI as expected but as soon as I remove it and run loop without sleep, then it skips some of the columns on UI and directly update last few/random columns, as you can see in attached image link(Image of output of my code without thread sleep) only last column is getting updated..

If I am not mistaken all the events are getting fired in sequence then they should update UI in sequence too but it's not happening and I don't know why. I don't want to do this Sleep thing because I have around 14 calls in actual application for UI status update and it will run under a loop so if It put sleep(100) then it will cost me a lot, is there any way to do it without SLEEP?

Computation file:

public class Class1 : IGenerate
{
    public event MessageEventHandler OnMessageSending;
    public void LongOperationMethod(BindingList<Status> _statusData)
    {
        if (OnMessageSending != null)
        {
            MessageEventArgs me = new MessageEventArgs();

            /// Loop for Weeks
            for (int k = 0; k < 2; ++k)
            {
                /// Loop for each day
                for (int i = 0; i < 7; ++i)
                {
                    /// Calculation on everyday
                    for (int j = 0; j < 1000; ++j)
                    {
                        // to do
                    }

                    me.weekNo = k;
                    me.DayNo = i;
                    OnMessageSending(me);

                }
            }
            me.Message = "Process completed successfully...";
            OnMessageSending(me);
        }
        else
        {
            throw new ArgumentException("Event hasn`t been rised, so we cannot continue working.");
        }
    }
}


UI file:

public partial class Form1 : Form
{
    BindingList<Status> _statusData = new BindingList<Status>();
    delegate void StringParameterDelegate(string value);
    Class1 cls = new Class1();
    public Form1()
    {
        InitializeComponent();
        labelProgress.Text = "";
    }

    private void button1_Click_1(object sender, EventArgs e)
    {
        for (int i = 1; i <= 2; ++i)
        {
            _statusData.Add(new Status { Week = "Week" + i, Day1 = 0, Day2 = 0, Day3 = 0, Day4 = 0, Day5 = 0, Day6 = 0, Day7 = 0 });
        }

        dataGridView1.DataSource = _statusData;
     }

    private void button2_Click(object sender, EventArgs e)
    {
        Thread t1 = new Thread(() => StartingThread(_statusData));
        t1.Start();
    }

    void StartingThread(BindingList<Status> _statusData)
    {
        IGenerate generate = new Class1();
        generate.OnMessageSending += new MessageEventHandler(generate_OnMessageSending);
        generate.LongOperationMethod(_statusData);
    }

    private void generate_OnMessageSending(MessageEventArgs e)
    {
        int weekNo = e.weekNo;
        int dayNo = e.DayNo;
        this.dataGridView1.BeginInvoke(new MethodInvoker(() => dataGridView1.Rows[e.weekNo].Cells[e.DayNo + 1].Value = 1));
        this.labelProgress.BeginInvoke(new MethodInvoker(() => this.labelProgress.Text = e.Message));
     
    }
}

解决方案

I think the problem is that you when you pass the event argument, you don't pass it is a value but as a reference. See Parameter passing in C#[^]

As you only create one instance of MessageEventArgs me = new MessageEventArgs();, you only have one place in the memory where your data is stored.
This means that when you change a member in that instance, that change will be reflected also in the event method.
The reason the sleep works is because you slow down the process enough for the values to be printed in the data grid before they are changed in the loop.

You should add one or more constructors with parameters to your MessageEventArgs class.
For example:

public MessageEventArgs(int weekNo, int dayNo)
{
    this.WeekNo = weekNo;
    this.DayNo = dayNo;
}



That way it is easy to create a new instance of the event argument for every message.

public void LongOperationMethod(BindingList<status> _statusData)
{
    if (OnMessageSending != null)
    {
        /// Loop for Weeks
        for (int k = 0; k < 2; ++k)
        {
            /// Loop for each day
            for (int i = 0; i < 7; ++i)
            {
                /// Calculation on everyday
                for (int j = 0; j < 1000; ++j)
                {
                    // to do
                }

                OnMessageSending(new MessageEventArgs(k, i));

            }
        }
        OnMessageSending(new MessageEventArgs("Process completed successfully..."));
    }
    else
    {
        throw new ArgumentException("Event hasn`t been rised, so we cannot continue working.");
    }
}
</status>


这篇关于循环中的触发事件不是按顺序更新UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆