尝试更新datagridview时,界面冻结 [英] interface freeze when trying to update datagridview

查看:129
本文介绍了尝试更新datagridview时,界面冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用下面的代码复制文件,并将状态列设置为 datagridview 以通知用户连接已启动,但是当我按按钮执行时是
方法接口冻结...



我搜索了很多,我知道使用 task.run(); 是不可能的,因为它不包括在 .not 4 它的一个新功能 .net 4.5 我也知道 Task.Factory.StartNew(); 可以使用而不是使用task.run(),但它有很多隐含线程的风险,我知道使用显式线程是还有很好的选择一个



我想要一点点帮助,继续我的项目,不断学习,而不是堆积在那个无聊的点

  public void PatchUpdates()
{
try
{
foreach(DataGridViewRow在DGV_OfficeList.Rows中的OfficeListRow)
{
string OfficeIPAddress = OfficeListRow.Cells [3] .Value.ToString();

foreach(DataGridViewRow在DGV_FileList.Rows中的FileListRow)
{
string SoruceFileNamePath = FileListRow.Cells [4] .Value.ToString();

string DestinationFileNamePath = @\\+ OfficeIPAddress + @\usb1_1\test\+ Path.GetFileName(SoruceFileNamePath);

//检查连接到远程服务器是否可用
var vResult = CheckOffice(OfficeIPAddress);

if(vResult == 1)
{
DGV_OfficeList [4,DGV_OfficeList.CurrentCell.RowIndex] .Value =Connected;
File.Copy(SoruceFileNamePath,DestinationFileNamePath,true); // copy files ...
}
else if(vResult == 0)
{
DGV_OfficeList [4,DGV_OfficeList.CurrentCell.RowIndex] .Value =disconnected;
break;
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}

检查下面的办公室代码

  public int CheckOffice(string _ipAddress)
{
int timeout = 120;
string data =PingTestData;
byte [] buffer = Encoding.ASCII.GetBytes(data);

Ping PingSender = new Ping();
PingOptions options = new PingOptions();

options.DontFragment = true;

PingReply reply = PingSender.Send(_ipAddress,timeout,buffer,options);

if(reply.Status == IPStatus.Success)
{
return 1;
}
else
{
return 0;
}
}

下面我如何尝试制作线程,解决我的问题

  public void PatchUpdates()
{
try
{
foreach(DataGridViewRow OfficeListRow在DGV_OfficeList.Rows)
{
string OfficeIPAddress = OfficeListRow.Cells [2] .Value.ToString();

foreach(DataGridViewRow在DGV_FileList.Rows中的FileListRow)
{
string SoruceFileNamePath = FileListRow.Cells [4] .Value.ToString();
string DestinationFileNamePath = @\\+ OfficeIPAddress + @\usb1_1\test\+ Path.GetFileName(SoruceFileNamePath);


线程foregroundthread = new Thread(()=&CheckOffice(OfficeIPAddress));

foregroundthread.Start();

//检查连接到远程服务器是否可用
if(CheckOffice(OfficeIPAddress)== 1)
{
DGV_OfficeList [3,DGV_OfficeList.CurrentCell.RowIndex ] .Value =已连接;
//file.copy(sorucefilenamepath,destinationfilenamepath,true); // copy files ...
}
else if(CheckOffice(OfficeIPAddress)== 0)
{
DGV_OfficeList [3,DGV_OfficeList.CurrentCell.RowIndex] .Value =断开
break;
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}

我也尝试过这个,但正如我所说的。运行在dot net 4不可用

  var task = Task.Run(()=> 
{
var result = CheckOffice(OfficeIPAddress);

this.BeginInvoke((Action)(()=>
{
if(result == 1)
{
DGV_OfficeList [4,DGV_OfficeList.CurrentCell.RowIndex] .Value =Connected;
//file.copy(sorucefilenamepath,destinationfilenamepath,true); //复制文件...
}
else if(result == 0)
{
DGV_OfficeList [4,DGV_OfficeList.CurrentCell.RowIndex] .Value =disconnect;
}
}));
}
);

------------------ ---------------------------------------更新---------- -----------------------------------------------

  public void PatchUpdates()
{
try
{
foreach (DataGridViewRow OfficeListRow在DGV_OfficeList.Rows)
{
string OfficeIPAddress = OfficeListRow.Cells [3] .Value.ToString();
int RowNum = OfficeListRow.Index;

foreach(DataGridViewRow在DGV_FileList.Rows中的FileListRow)
{
string SoruceFileNamePath = FileListRow.Cells [4] .Value.ToString();
// string DestinationFileNamePath = @\\+ OfficeIPAddress + @\usb1_1\test\+ Path.GetFileName(SoruceFileNamePath);
string DestinationFileNamePath = @F:\test\+ Path.GetFileName(SoruceFileNamePath); // TestPurpose

Thread t2 = new Thread(new ThreadStart(()=>
{
int vResult = CheckOffice(OfficeIPAddress);
UpdateUI(vResult, RowNum,SoruceFileNamePath,DestinationFileNamePath,OfficeIPAddress);
}));
t2.Start();
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error Message,MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

UpdateUI方法更新UI ...



  public void UpdateUI(int vResult,int RowNum,string SoruceFileNamePath,string DestinationFileNamePath,string OfficeIPAddress)
{
尝试
{
var timeNow = DateTime.Now;

if((DateTime.Now - PreviousTime).Milliseconds< = 10)
return;

SynchronizationContext.Post(new SendOrPostCallback(o =>
{
if(vResult == 1)
{
DGV_OfficeList [4,RowNum] .Value =Connected;
//File.Copy(SoruceFileNamePath,DestinationFileNamePath,true);
//MessageBox.Show(\"Pingable+ OfficeIPAddress); // TestPurpose
}
else if(vResult == 0)
{
DGV_OfficeList [4,RowNum] .Value =Disconnected;
//MessageBox.Show(\"不可达); / / TestPurpose
}
}),vResult);

PreviousTime = timeNow;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error,MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}

解决方案

简单的答案:你的代码没有错。设计不好。



长回答



你听过有人说我一次只能做一件事!那就是这里发生了什么您的Windows窗体应用程序的代码正在由1个线程执行,该线程一次只能执行一件事情。当它正在ping,它等待回复。如果回复成功,则会复制一个文件。因为你有一个循环,它一直这样做,直到它完成了所有的行。



虽然这样做,你可能是在UI中点击其他的东西,但你的线程一次只能做一件事。正在忙着做循环中的东西。因此,当您点击时,您只需要等待。



那么如何修复它,以便UI不会冻结?



以简单的英文,你需要这样做。想象你是一个线程:



我是UI线程,我的最终目标是保持UI响应。我不想让用户界面冻结。因此,如果我需要做任何事情,除了UI工作,我将要求别人这样做。当别人正在做其他工作时,我可以自由地做UI工作。



别人是另一个线程。这是一个例子,在代码中阅读我的注释并将其应用于您的应用程序。如果要运行此代码,请使用名为 label1 的标签和两个按钮创建一个窗体 Form1 。将 ButtonClickHandlerAsync 分配给一个按钮的点击处理程序和 Stop_Click 到另一个按钮。



当您单击执行 ButtonClickHandlerAsync 的按钮时,整个操作将启动。当它正在工作时,您可以单击另一个按钮,它将显示一个消息框并保持响应。顺便说一下,我将这段代码从这里,但我在代码中添加了自己的评论,所以你知道发生了什么。

  public partial class Form1:Form {

//我们需要这个,因为这将允许我们与UI控件进行交互。 UI控件只能由
//创建UI控件的线程访问。在这种情况下,启动应用程序的线程是主线程。
private readonly SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;

public Form1(){
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
}

private void Stop_Click(object sender,EventArgs e){

//我是UI线程。我可以这样做,因为T2帮助我做循环。
MessageBox.Show(我在做其他事情);
}

private async void ButtonClickHandlerAsync(object sender,EventArgs e){
button1.Enabled = false;
var count = 0;

//我是UI线程。我还有其他事情要做因此,请使用线程池中的线程运行此循环。
//当你完成运行循环让我知道(这是等待着)
//我是UI线程,所以我要从右边返回
/ /到调用ButtonClickHandlerAsync的点。 (它被一个点击调用,所以当它返回它将没有
//做,所以它将准备好对另一个UI工作做出反应,例如另一个点击或更新UI等
等待Task.Run(()=>
{
//我是线程池中的一个线程,我的名字是T2,我在帮助UI线程,所以UI线程可以做其他的事情。
for(var i = 0; i< = 5000000; i ++){
UpdateUI(i);
count = i;
}
});


//我是UI线程,好像循环完成了,所以我会做以下2行工作
label1.Text = @Counter + count;
button1.Enabled = true;
}

public void UpdateUI(int value){

//我是T2,我是帮助UI线程
var timeNow = DateTime.Now;

if((DateTime.Now - previousTime).Milliseconds< = 50)
return;

//我不因为我没有创建它们,所以可以访问UI控件。所以我只是要求syncContext
//为我做一个SendOrPostCallback
synchronizationContext.Post(new SendOrPostCallback(o =>
{
/ /我是UI线程,我将这样做
label1.Text = @Counter+(int)o;
}),value);

//我是T2。我会这样做,然后回来做更多的工作。
previousTime = timeNow;
}

您如何修复代码? p>

您可以执行 CheckOffice ,并使用线程池中的线程复制文件。如果需要与UI进行交互,该线程可以使用 synchronizationContext 。主要的UI线程可以随时做其他的事情,而线程池的线程检查办公室和复制可能需要很长时间的文件,特别是如果文件是大的。



我是UI线程,我没有时间等待ping回复。
我是UI线程,我没有时间将文件从一个位置复制到另一个位置,可能需要几秒钟或几分钟,我的工作是保持UI响应。



编辑



在OP编写.NET 4的限制之前,我写了上面的答案。但我很确定他们为此创建了一个NuGet软件包。请参阅此处这里



如果您不能使用 async 等待以上相同的概念适用于线程。

 线程t2 = new Thread(new ThreadStart(()=> 
{
for(var i = 0; i< = 5000000; i ++){
UpdateUI(i);
count = i;
}
}));
t2.Start();


I am using below code to copy files and set status column at datagridview to inform user that connection is up but when I press the button to execute is method the interface freeze...

I have searched a lot i know that using task.run(); is not possible since it is not included in .not 4 it a new feature of .net 4.5 i also know that Task.Factory.StartNew(); can be used instead of using task.run() but it has a lot of risk as implicit thread and i know that using explicit threading is also good choose a

i want a little help to go on with my project and keep learning instead of getting stacked at that boring point

public void PatchUpdates()
{
    try
    {
        foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
        {
            string OfficeIPAddress = OfficeListRow.Cells[3].Value.ToString();

            foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
            {
                string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();

                string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);

                //check if connection to remote server is available
                var vResult = CheckOffice(OfficeIPAddress);

                if (vResult == 1)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    File.Copy(SoruceFileNamePath, DestinationFileNamePath, true); //copy files...
                }
                else if (vResult == 0)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                    break;
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

check office code below

    public int CheckOffice(string _ipAddress)
    {
        int timeout = 120;
        string data = "PingTestData";
        byte[] buffer = Encoding.ASCII.GetBytes(data);

        Ping PingSender = new Ping();
        PingOptions options = new PingOptions();

        options.DontFragment = true;

        PingReply reply = PingSender.Send(_ipAddress, timeout, buffer, options);

        if (reply.Status == IPStatus.Success)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

below how I have tried to make thread but that wont solve my problem

public void PatchUpdates()
{
    try
    {
        foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
        {
            string OfficeIPAddress = OfficeListRow.Cells[2].Value.ToString();

            foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
            {
                string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();
                string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);


                Thread foregroundthread = new Thread(() => CheckOffice(OfficeIPAddress));

                foregroundthread.Start();

                //check if connection to remote server is available
                if (CheckOffice(OfficeIPAddress) == 1)
                {
                    DGV_OfficeList[3, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    //file.copy(sorucefilenamepath, destinationfilenamepath, true); //copy files...
                }
                else if (CheckOffice(OfficeIPAddress) == 0)
                {
                    DGV_OfficeList[3, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                    break;
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

I have also tried this but as i said thask.run is not available at dot net 4

    var task = Task.Run(() =>
        {
            var result = CheckOffice(OfficeIPAddress);

            this.BeginInvoke((Action)(() =>
            {
                if (result == 1)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "Connected";
                    //file.copy(sorucefilenamepath, destinationfilenamepath, true); //copy files...
                }
                else if (result == 0)
                {
                    DGV_OfficeList[4, DGV_OfficeList.CurrentCell.RowIndex].Value = "disconnected";
                }
            }));
        }
    );

--------------------------------------------------------- Update ---------------------------------------------------------

    public void PatchUpdates()
    {
        try
        {
            foreach (DataGridViewRow OfficeListRow in DGV_OfficeList.Rows)
            {
                string OfficeIPAddress = OfficeListRow.Cells[3].Value.ToString();
                int RowNum = OfficeListRow.Index;

                foreach (DataGridViewRow FileListRow in DGV_FileList.Rows)
                {
                    string SoruceFileNamePath = FileListRow.Cells[4].Value.ToString();
                    //string DestinationFileNamePath = @"\\" + OfficeIPAddress + @"\usb1_1\test\" + Path.GetFileName(SoruceFileNamePath);
                    string DestinationFileNamePath = @"F:\test\" + Path.GetFileName(SoruceFileNamePath); //TestPurpose

                    Thread t2 = new Thread(new ThreadStart(() =>
                    {
                        int vResult = CheckOffice(OfficeIPAddress);
                        UpdateUI(vResult, RowNum, SoruceFileNamePath, DestinationFileNamePath, OfficeIPAddress);
                    }));
                    t2.Start();
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

The UpdateUI method to update UI...

    public void UpdateUI(int vResult, int RowNum, string SoruceFileNamePath, string DestinationFileNamePath,string OfficeIPAddress)
    {
        try
        {
            var timeNow = DateTime.Now;

            if ((DateTime.Now - PreviousTime).Milliseconds <= 10)
                return;

            SynchronizationContext.Post(new SendOrPostCallback(o =>
                    {
                        if (vResult == 1)
                        {
                            DGV_OfficeList[4, RowNum].Value = "Connected";
                            //File.Copy(SoruceFileNamePath, DestinationFileNamePath, true);
                            //MessageBox.Show("Pingable " + OfficeIPAddress); //TestPurpose
                        }
                        else if (vResult == 0)
                        {
                            DGV_OfficeList[4, RowNum].Value = "Disconnected";
                            //MessageBox.Show("Not reachable"); //TestPurpose
                        }
                    }), vResult);

            PreviousTime = timeNow;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

解决方案

Short answer: There is nothing wrong with your code. The design is not good.

Long Answer

Have you heard someone say "I can only do one thing at a time!" Well that is what is going on here. Your windows forms application's code is being executed by 1 thread and that thread can only do one thing at a time. When it is pinging, it waits for the reply. If the reply is successful it then copies a file. Since you have a loop it keeps doing this until it has completed all rows.

While it is doing that, you are probably clicking other things in the UI, but your thread "can only do 1 thing at a time". It is busy doing the stuff in the loop. Therefore, when you are clicking, you just have to wait.

So how do I fix it so the UI does not freeze?

In plain English, you need to do this. Imagine you are a thread:

I am the UI thread and my ultimate goal is to keep the UI responsive. I do not want the UI to freeze. Therefore, if I need to do anything except UI work, I am going to ask someone else to do it. While that someone else is doing other work, I will be free to do UI work.

That someone else is another thread. Here is an example, read my comments in the code and apply it to your application. If you want to run this code, create a form Form1 with a label named label1 and two buttons. Assign ButtonClickHandlerAsync to one button's click handler and Stop_Click to the other button.

The whole action starts when you click the button which executes ButtonClickHandlerAsync. While it is doing work, you can click the other button and it will show a message box and stay responsive. By the way I have copied this code from here but I added my own comments inside the code so you know what is going on.

public partial class Form1 : Form {

   // We need this because this will allow us to interact with UI controls. UI controls can only be accessed by the thread that 
   // created the UI control. In this case it is the thread which started the application so the main thread.
   private readonly SynchronizationContext synchronizationContext;
   private DateTime previousTime = DateTime.Now;

   public Form1() {
      InitializeComponent();
      synchronizationContext = SynchronizationContext.Current;
   }

   private void Stop_Click(object sender, EventArgs e) {

      // I am the UI thread. I can do this because T2 is helping me do the loop.
      MessageBox.Show( "I am doing other things." );
   }

   private async void ButtonClickHandlerAsync(object sender, EventArgs e) {
      button1.Enabled = false;
      var count = 0;

      // I am the UI thread. I have other things to do. So please run this loop by using a thread from the thread pool.
      // When you are done running the loop let me know (This is what the await does)
      // I am the UI thread so I am going to return back from right here
      // to the point where ButtonClickHandlerAsync was called from. (it was called by a click, so when it returns it will have nothing
      // to do. Therefore, it will be ready to react to another UI job such as another click or update the UI etc.
      await Task.Run( () =>
      {
         // I am a thread from the thread pool. My name is T2. I am helping the UI thread so the UI thread can do other things.
         for( var i = 0; i <= 5000000; i++ ) {
            UpdateUI( i );
            count = i;
         }
      } );


      // I am the UI thread. Ok looks like the loop is done. So I will do the following 2 lines of work
      label1.Text = @"Counter " + count;
      button1.Enabled = true;
   }

   public void UpdateUI(int value) {

      // I am T2. I am helping the UI thread.
      var timeNow = DateTime.Now;

      if( ( DateTime.Now - previousTime ).Milliseconds <= 50 )
         return;

      // I do not have access to the UI controls since I did not create them. So I am just going to ask the synchronizationContext
      // to do this for me by giving it a SendOrPostCallback
      synchronizationContext.Post( new SendOrPostCallback( o =>
      {
         // I am the UI thread. I will do this.
         label1.Text = @"Counter " + ( int ) o;
      } ), value );

      // I am T2. I will do this and then return and do more work.
      previousTime = timeNow;
   }

How can you fix your code?

You can do CheckOffice and copying the files using a thread from the threadpool. That thread can use the synchronizationContext to if it needs to interact with the UI. The main UI thread can stay free to do other things while the thread from the thread pool is checking the office and copying a file which could take a long time, especially, if the file is big.

"I am the UI thread. I have no time to wait for a ping reply." "I am the UI thread. I have no time to copy a file from one location to another location which can take seconds or minutes. My job is to keep the UI responsive."

EDIT

I wrote the above answer before the OP wrote the restriction of .NET 4. But I am pretty sure they have created a NuGet package for this. See here and here.

If you cannot use async and await, the same concepts above apply to threading.

Thread t2 = new Thread( new ThreadStart(() =>
{
   for( var i = 0; i <= 5000000; i++ ) {
      UpdateUI( i );
      count = i;
   }
} ) );
t2.Start();

这篇关于尝试更新datagridview时,界面冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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