如果我们想将它写入控件,如何在异步方法中使用属性。在尝试不调用时写入时抛出异常。所以我想处理Counter类中的调用问题,而不是在Form1中。我已经为这个问题写了一个例子。在此代码中,TextBox.Text行抛出跨线程异常。
internal class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Counter myCounter = new Counter(1000);
myCounter.IndexValueChanged += myCounter_IndexValueChanged;
myCounter.StartCountAsync();
}
void myCounter_IndexValueChanged(object sender, IndexValueChangedEventArgs e)
{
textBox1.Text = e.Index.ToString();
}
}
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
public Counter(int maxNumber)
{
_maxNumber = maxNumber;
}
public async void StartCountAsync()
{
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
if (IndexValueChanged != null)
IndexValueChanged(this, new IndexValueChangedEventArgs(_index));
Thread.Sleep(100);
}
});
}
}
class IndexValueChangedEventArgs
{
int indexNum;
public IndexValueChangedEventArgs(int index)
{
indexNum = index;
}
public int Index
{
get { return indexNum; }
}
}`
答案 0 :(得分:1)
要在ui线程上调用委托,您需要一个Windows句柄。我不知道你为什么要在Counter类中进行调用,但最简单的方法似乎是:给你的Counter实例引用你的Form并在该引用上调用Invoke-Method。 (从我的手机写,因此很难添加代码示例,明天将编辑)
编辑:但我真的认为表格的责任是检查它是否在正确的线程和其他调用中进行更改(Begin)调用自身。
编辑:这是你的Counter类,添加了&#34;父母&#34;参考:
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
Control _parent;
public Counter(Control parent, int maxNumber)
{
_maxNumber = maxNumber;
_parent = parent;
}
public async void StartCountAsync()
{
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
// introduce local variable for thread safety
IndexValueChangedEventHandler handler = IndexValueChanged;
if (handler != null)
{
if (_parent == null || !_parent.InvokeRequired)
handler(this, new IndexValueChangedEventArgs(_index));
else
// use BeginInvoke
_parent.BeginInvoke(handler, this, new IndexValueChangedEventArgs(_index));
}
Thread.Sleep(100);
}
});
}
}
你在你的按钮处理程序中使用它:
private void button1_Click(object sender, EventArgs e)
{
Counter myCounter = new Counter(this, 1000);
myCounter.IndexValueChanged += myCounter_IndexValueChanged;
myCounter.StartCountAsync();
}
最好在Counter类中使用BeginInvoke,因为Invoke会等到UI线程执行委托,因此您的计数器可能比预期慢(或者您的Web客户端将使用比所需更长的网络资源)。希望这有助于你。
编辑:引入了局部变量&#34; handler&#34;。否则,消费者可能在您对其进行空检查后取消注册该事件,并且在您调用它时它为空。
答案 1 :(得分:1)
在您的特定示例中,首先使用后台线程对我没有意义。当然,如果你在UI线程上调用Thread.Sleep
,这显然是不好的,但是你不需要调用Thread.Sleep
来获得async
方法等待。
public async void StartCountAsync()
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
if (IndexValueChanged != null)
IndexValueChanged(this, new IndexValueChangedEventArgs(_index));
await Task.Delay(100);
}
}
如果从UI线程调用,那么在延迟之后,此方法将继续在UI线程上执行,因此调用IndexValueChanged
将起作用。
答案 2 :(得分:0)
您可以更改应用程序的行为以允许跨线程调用来修改表单元素:
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
或者,如果有人说这不是推荐的,不安全且不专业,
您可以使用Counter类中的AsyncOperationManager。 WebClient.DownloadFileAsync方法在内部使用相同的方法。这将确保在UI线程上调用事件处理程序。
这是你的最终Counter类,它应该在UI线程上调用委托。
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
public Counter(int maxNumber)
{
_maxNumber = maxNumber;
}
public async void StartCountAsync()
{
AsyncOperation asyncCountOperation = AsyncOperationManager.CreateOperation(null);
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
asyncCountOperation.Post(new SendOrPostCallback(InvokeDelegate), _index);
Thread.Sleep(100);
}
});
}
private void InvokeDelegate(object index)
{
if (IndexValueChanged != null)
{
IndexValueChanged(this, new IndexValueChangedEventArgs((int)index));
}
}
}