1

我正在开发一个 MDI 应用程序,用户可以在其中创建同一表单的多个实例(称为 ListForm)。ListForm 的每个实例都有一个 flowlayoutpanel,其中包含一组唯一的用户控件。ListForm 还包含一个 StatusStrip ProgressBar 和一个名为“ReadAll”的按钮。

每个用户控件都有一个“读取”按钮,单击该按钮将执行读取操作。此操作最多可能需要 3 秒才能完成。

我想要做的是当用户单击“ReadAll”按钮时,子窗体会生成一个后台线程,该线程遍历 flowlayoutpanel.controls 集合并调用每个用户控件的 .PerformClick() 方法。这将更新表单中的所有用户控件。

问题在于,看起来所有表单实例的事件处理程序都被调用,导致 ListForm 的所有实例中的所有用户控件都被更新。此外,当我从后台工作人员报告进度时,所有 ListForm 实例的进度条都会更新。不需要此功能。

如何确保仅更新生成后台工作人员的 ListForm?是否有唯一标识子表单的首选方法?

在此先感谢您的帮助。代码如下...

public partial class ListForm: Form
{
    // Background Worker Thread for Read / Write All tasks
    private static BackgroundWorker bw = new BackgroundWorker();

    public ListForm()
    {
        InitializeComponent();

        // Configure the Background Worker that reads and writes all variable data
        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);


    }

    private void btnReadAll_Click(object sender, EventArgs e)
    {

        if (bw.IsBusy != true)
        {
            // Start the ReadAll parameters thread
            btnReadAll.Text = "Cancel Read";
            btnWriteAll.Enabled = false;
            bw.RunWorkerAsync("R");
        }
        else if (bw.WorkerSupportsCancellation == true)
        {
            // Cancel the ReadAll parameters thread
            bw.CancelAsync();
        }
    }

    // ******************************  Background Thread Methods ***************************
    public delegate void DoUIWorkHandler();

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        DoUIWorkHandler DoReadClick;
        DoUIWorkHandler DoWriteClick;

        int CurrentControlCount = 1;
        string StatusText = "";
        int ProgressValue = 0;
        string argument = e.Argument as string;

        // *******************Perform a time consuming operation and report progress. 
        try
        {
            foreach (UserControl c in this.flowLayoutPanel1.Controls)
            {
                if ((worker.CancellationPending == true))
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // Update the status and return it to the UI
                    StatusText = "Updating: (" + (CurrentControlCount).ToString() + " of " + flowLayoutPanel1.Controls.Count.ToString() + ") " + c.ParamProperties.strDHIndexDescription;
                    ProgressValue = (int)(((float)CurrentControlCount / (float)flowLayoutPanel1.Controls.Count) * 100);
                    worker.ReportProgress(ProgressValue, StatusText);
                    System.Threading.Thread.Sleep(20);
                    CurrentControlCount++;

                    // Update the contorl
                    if (c.InvokeRequired)
                    {
                        if (argument == "R")
                        {
                            DoReadClick = c.btnRead.PerformClick;
                            c.Invoke(DoReadClick);
                        }
                        else
                        {
                            DoWriteClick = c.btnWrite.PerformClick;
                            c.Invoke(DoWriteClick);
                        }

                    }
                }
            }
        }
        catch(InvalidCastException ex)
        {
            // Catch any functions that are in the Layout panel
            string ErrorStr = "Could not cast a Function control to a Parameter control. \n\r\r Exception: " + ex.Message;
            srvcAppLogger.Logger.Log(new clsApplicationLogger.LoggerMessage(ErrorStr, "bw_DoWork", "frmVariableHandlerGUI"));
        }
        catch (Exception ex)
        {
            string ErrorStr = "An unecpected exception occured. Error: " + ex.Message.ToString();
            srvcAppLogger.Logger.Log(new clsApplicationLogger.LoggerMessage(ErrorStr, "bw_DoWork", "frmVariableHandlerGUI"));
        }
    }

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.tsStatus.Text = e.UserState.ToString();
        this.tsProgressBar.Value = e.ProgressPercentage;
    }
4

2 回答 2

0

要识别对象,您可以使用 HashCode 或创建 Id 属性,然后在自定义 EventArgs 中使用它。

private Guid _controlId;

    public ListForm()
    {
        _controlId = Guid.NewGuid();
        ...
    }

也尝试以这种方式管理事件观察者:

private void btnReadAll_Click(object sender, EventArgs e)
    {
        if (bw.IsBusy != true)
        {
            bw.DoWork += bw_DoWork;
            bw.ProgressChanged += bw_ProgressChanged);
            bw.RunWorkerCompleted +=bw_RunWorkerCompleted;

            // Start the ReadAll parameters thread
            btnReadAll.Text = "Cancel Read";
            btnWriteAll.Enabled = false;
            bw.RunWorkerAsync("R");
        }
        else if (bw.WorkerSupportsCancellation == true)
        {
            // Cancel the ReadAll parameters thread
            bw.CancelAsync();
        }

        bw.DoWork -= bw_DoWork;
        bw.ProgressChanged -= bw_ProgressChanged;
        bw.RunWorkerCompleted -= bw_RunWorkerCompleted;
    }
于 2013-10-17T23:57:22.927 回答
0

您有一个 BackgroundWorker 实例,并且您创建的每个 ListForm 都注册到该工作人员。因此,您必须将 Form 的实例传递给工作人员。

创建一个带有两个属性的小助手类。这只是一个例子。你也可以传递一个标识符或者你喜欢的任何东西:

public struct ReadAllArguments
{
    public bool Read;
    public ListForm CallingForm;

    public ReadAllArguments(bool read, ListForm callingForm)
    {
        Read = read; CallingForm = callingForm;
    }
}

你可以像这样通过它:

...
    if (bw.IsBusy != true)
        {
            // Start the ReadAll parameters thread
            btnReadAll.Text = "Cancel Read";
            btnWriteAll.Enabled = false;
            bw.RunWorkerAsync(new ReadAllArguments(true, this));
        }
...

后来这样读:

private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        DoUIWorkHandler DoReadClick;
        DoUIWorkHandler DoWriteClick;

        int CurrentControlCount = 1;
        string StatusText = "";
        int ProgressValue = 0;
        ReadAllArguments arguments = e.Argument as ReadAllArguments;
        if (this != arguments.ListForm)
          return;

        ...

                        if (arguments.Read)
                        {
                            DoReadClick = c.btnRead.PerformClick;
                            c.Invoke(DoReadClick);
                        }
                        else
                        {
                            DoWriteClick = c.btnWrite.PerformClick;
                            c.Invoke(DoWriteClick);
                        }
       ...

您会意识到您甚至可以将 Work-Method 从您的 Form 中移出,因为没有直接的依赖关系并且您不需要访问“this”-Qualifier。你已经通过了你的论点中的一切。在用该参数替换每个“this”之后,您可以将一个 Work-Method 注册到您的 Worker 的 DoWork-Event。这会更干净,更优雅......

这是一个如何执行此操作的示例:

public partial class ListForm: Form
{
    // Background Worker Thread for Read / Write All tasks
    private static BackgroundWorker bw = new BackgroundWorker();

    static ListForm()
    {
        //We move the do-work out of the instance constructor, because the work that has to be done, is not connected to our instances. So we've only one definition of our work that has to be done
        bw.DoWork += new DoWorkEventHandler(TheWorkThatHasToBeDone);
    }

    public ListForm()
        {
             InitializeComponent();

             // Configure the Background Worker that reads and writes all variable data
             bw.WorkerReportsProgress = true;
             bw.WorkerSupportsCancellation = true;
             //no more registering on instance level 
             bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
             bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);    
        }

    //Your new instance-independent doWork-Method - static here
    private static void TheWorkThatHasToBeDone(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        ReadAllArguments arguments = e.Argument as ReadAllArguments;
        //You call the instance-Method here for your specific instance you want the work to be done for
        arguments.ListForm.bw_DoWork(worker, arguments);
    }


    //Your old bw_DoWork-Method with nicer arguments - you should change the method name...
    private void bw_DoWork(BackgroundWorker worker, ReadAllArguments arguments)
        {
            DoUIWorkHandler DoReadClick;
            DoUIWorkHandler DoWriteClick;

            int CurrentControlCount = 1;
            string StatusText = "";
            int ProgressValue = 0;

            // *******************Perform a time consuming operation and report progress. 
            try
            {
                ...
            }
        }

将这些东西从表单代码中移出而不是使用静态成员这样做会更加优雅,但我认为这个想法很明确。

于 2013-10-17T21:08:18.183 回答