从另一个线程更新TextBox / NumericUpDown / Labels / Progress Bars

时间:2012-10-03 09:05:08

标签: c# generics asynchronous extension-methods invoke

我们目前有一个从串行通信中读取数据的应用程序。在整个应用程序中,我们需要在UI中显示这些数据,因此我们创建了一个使用BeginInvoke更新文本框/标签/数字/进度条的方法。

然而,这开始变得麻烦,因为每个控件都需要自己的'Setter',这样我们已经有20个左右的方法,它们基本上都是一样的。

虽然我可以看到一种简单的方法将其泛化为特定控件(例如,标签只有1个,文本框只有1个),但我们可以调用更新数据的理想选择只有1个(可能是扩展名)方法在用户界面。

以下是我们原来的方法:

private void SetCell1Cell2Async(decimal value)
    {
        if (spinCell1Cell2.InvokeRequired)
        {
            spinCell1Cell2.BeginInvoke(new EventHandler(delegate
            {
                spinCell1Cell2.Value = value;
            }));
        }
        else
        {
            if (spinCell1Cell2.IsDisposed) return; // Do not process if the control has been disposed of
            if (spinCell1Cell2.IsHandleCreated)
            {
                // This handle may not be created when creating this form AFTER data is already flowing
                // We could capture this data for future display (i.e. via deferUpdate = true or similar), but it is easier to ignore it
                // i.e. Do Nothing
                return;
            }
            spinCell1Cell2.Value = value;
        }
    }

这是我们当前的方法(适用于使用Text属性显示数据的控件):

delegate void SetTextAsyncCallback(Control ctrl, string text);
public static void SetTextAsync(this Control invoker, string text)
{
    if (invoker.InvokeRequired)
    {
        invoker.BeginInvoke(new SetTextAsyncCallback(SetTextAsync), invoker, text);
    }
    else
    {
        if (invoker.IsDisposed) return; // Do not process if the control has been disposed of
        if (!invoker.IsHandleCreated)
        {
            // This handle may not be created when creating this form AFTER data is already flowing
            // We could capture this data for future display (i.e. via deferUpdate = true or similar), but it is easier to ignore it
            // i.e. Do Nothing
            return;
        } 
        invoker.Text = text;
    }
}

正如您所看到的,此方法适用于使用Text属性显示其数据的任何内容。

理想情况下,我希望能够“传递”要更新的属性,并提供一个方法,该方法需要stringdoubledecimal,{{1等等,但我有点失落,从这里开始。

任何帮助表示感谢。

1 个答案:

答案 0 :(得分:2)

我会在表单中创建一个更通用的解决方案:

delegate void ActionCallback();

private void SetText()
{
    this.SafeInvoke(() =>
        {
            if (spinCell1Cell2.IsDisposed) return;
            if (!spinCell1Cell2.IsHandleCreated) return;
            // Do stuff
        });
}

private void SafeInvoke(ActionCallback action)
{
    if (this.InvokeRequired)
        this.Invoke(action);
    else
        action();
}

使用委托,您只需要声明实际操作一次,而稍后决定执行类型(在SafeInvoke方法中)。如果你想要一些扩展方法,这里有一个例子:

public delegate void ActionCallback();

public static void SafeInvoke(this Form form, ActionCallback action)
{
    if (form.InvokeRequired)
        form.Invoke(action);
    else
        action();
}

public static void SafeSetText(this Control ctl)
{
    var form = ctl.FindForm();
    if (form == null) return; // or do something else

    form.SafeInvoke(() =>
        {
            // Do stuff
        });
}

修改 在再次阅读你的问题后,我注意到这不是你所问的,所以我会给你一个更相关的例子:

public static void SafeUpdate<T>(this T ctl, ActionCallback action) where T : Control
{
    var form = ctl.FindForm();
    if (form == null) return; // or do something else

    SafeInvoke(action);
}

// Then in some other method
{
    textBox1.SafeUpdate(() =>
    {
        textBox1.Text = "123";
        textBox1.ForeColor = Color.Red;
    });
}