问题
I have a time consuming task that tests a couple of network connections. In my example below I have confined it to one connection. Normally the connection returns quickly but it could happen that the connection cannot be made so that the socket times out. During this time I would like to display an "idler" gif in the Form, when the connection succeeds the app should change the Image in the Form to some green check icon or in case of a failing connection a red icon "stopper" should be displayed.
Somehow I cannot get the idler gif become visible and animated. To simulate a failing connection one can enter an invalid port # or non existent address.
Any clues what I'm missing or doing wrong?
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
///
#region BackgroundWorker
private System.ComponentModel.BackgroundWorker backgroundWorker = new System.ComponentModel.BackgroundWorker();
private delegate void SomeLongRunningMethodHandler(object sender, EventArgs e);
#endregion
private System.ComponentModel.IContainer components = null;
Button button,button2;
static Socket socket;
static bool success;
private static bool done;
private Label lbl1;
private Label lbl2;
private TextBox address;
private TextBox port;
private PictureBox p;
private static String port_number,host;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void RunTest(object o,EventArgs e)
{
p.Visible = true;
SomeLongRunningMethodHandler synchronousFunctionHandler =
default(SomeLongRunningMethodHandler);
synchronousFunctionHandler =
TestConnection;
synchronousFunctionHandler.Invoke(o, e);
}
private void TestConnection(object o, EventArgs e)
{
host = address.Text;
port_number = port.Text;
if (null != socket)
{
socket.Close();
}
Thread.Sleep(1000);
IPEndPoint myEndpoint = new IPEndPoint(0, 0);
IPHostEntry remoteMachineInfo = Dns.GetHostEntry(host);
IPEndPoint serverEndpoint = new IPEndPoint(remoteMachineInfo.AddressList[0],
int.Parse(port_number));
socket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(serverEndpoint);
success = true;
p.Image = global::BlockingUI.Properties.Resources.accept;
}
catch
{
success = false;
p.Image = global::BlockingUI.Properties.Resources.stopper;
}
done = true;
}
private void ExitApp(object o, EventArgs e)
{
Application.Exit();
}
}
回答1:
If you truly want to use a BackgroundWorker
, this (or something close to it), should point you in the right direction. You are creating a BackgroundWorker
object but then doing nothing with it. BackgroundWorker
objects are not allowed to access UI elements as they are owned by the UI thread, but you can pass in UI values, as I do here with a Tuple
(you could create your own class to hold these values if you want too) and then modify the UI from the UI thread once the worker is complete.
private struct ConnectionProperties
{
public string Address;
public string Port;
}
private void RunTest(object o, EventArgs e)
{
BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
worker.RunWorkerCompleted += TestComplete;
worker.DoWork += TestConnection;
p.Visible = true;
//worker.RunWorkerAsync(new Tuple<string, string>(address.Text, port.Text));
worker.RunWorkerAsync(new ConnectionProperties{ Address = address.Text, Port = port.Text });
}
private void TestConnection(object sender, DoWorkEventArgs e)
{
bool success = false;
//var connection = e.Argument as Tuple<string, string>;
var connection = (ConnectionProperties)e.Argument;
if (null != socket)
{
socket.Close();
}
Thread.Sleep(1000);
IPEndPoint myEndpoint = new IPEndPoint(0, 0);
IPHostEntry remoteMachineInfo = Dns.GetHostEntry(connection.Address);
IPEndPoint serverEndpoint = new IPEndPoint(remoteMachineInfo.AddressList[0],
int.Parse(connection.Port));
socket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(serverEndpoint);
success = true;
}
catch
{
success = false;
}
e.Result = success;
}
// Define other methods and classes here
private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
var success = (bool)e.Result;
if (success)
{
p.Image = global::BlockingUI.Properties.Resources.accept;
}
else
{
p.Image = global::BlockingUI.Properties.Resources.stopper;
}
}
else
{
//unexpected error, show message or whatever
}
}
回答2:
Well, I had done this with my own thread. Normally, you run the thread with the long running task, and when needed, call Control.Invoke() with a delegate pointing to a function that will operate over the UI. It looks that you're changing the UI using the background worker thread, which is not allowed.
Also, in Winforms is necessary to call Control.Invalidate() to force a re-painting of the UI and show the new icons.
来源:https://stackoverflow.com/questions/22404710/updating-ui-thread-using-backgroundworker-in-c-sharp-form-application