다른 스레드에서 TextBox에 쓰는 중입니까?
C # Windows Form 응용 프로그램이 스레드에서 텍스트 상자에 쓰는 방법을 알 수 없습니다. 예를 들어 Program.cs에는 형식을 그리는 표준 main ()이 있습니다.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
그런 다음 Form1.cs에 있습니다.
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public static void SampleFunction()
{
while(true)
WindowsFormsApplication1.Form1.ActiveForm.Text += "hi. ";
}
내가 이것에 대해 완전히 잘못하고 있습니까?
최신 정보
다음은 bendewey에서 제공하는 작업 코드 샘플입니다.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for(int i = 0; i<5; i++)
{
AppendTextBox("hi. ");
Thread.Sleep(1000);
}
}
}
MainForm에서 텍스트 상자를 설정하는 함수를 만들어 InvokeRequired를 확인합니다.
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
ActiveForm.Text += value;
}
정적 메서드에서는 호출 할 수 없습니다.
WindowsFormsApplication1.Form1.AppendTextBox("hi. ");
어딘가에 Form1에 대한 정적 참조가 있어야하지만 이것은 실제로 권장되거나 필요하지 않습니다. SampleFunction을 정적이 아닌 상태로 만들 수 있습니다. 그러면 호출 할 수 있습니다.
AppendTextBox("hi. ");
다른 스레드에 추가되고 필요한 경우 Invoke 호출을 사용하여 UI에 마샬링됩니다.
전체 샘플
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for(int i = 0; i<5; i++)
{
AppendTextBox("hi. ");
Thread.Sleep(1000);
}
}
}
또는 당신은 좋아할 수 있습니다
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread( SampleFunction ).Start();
}
void SampleFunction()
{
// Gets executed on a seperate thread and
// doesn't block the UI while sleeping
for ( int i = 0; i < 5; i++ )
{
this.Invoke( ( MethodInvoker )delegate()
{
textBox1.Text += "hi";
} );
Thread.Sleep( 1000 );
}
}
}
I would use BeginInvoke
instead of Invoke
as often as possible, unless you are really required to wait until your control has been updated (which in your example is not the case). BeginInvoke
posts the delegate on the WinForms message queue and lets the calling code proceed immediately (in your case the for-loop in the SampleFunction
). Invoke
not only posts the delegate, but also waits until it has been completed.
So in the method AppendTextBox
from your example you would replace Invoke
with BeginInvoke
like that:
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.BeginInvoke(new Action<string>(AppendTextBox), new object[] {value});
return;
}
textBox1.Text += value;
}
Well and if you want to get even more fancy, there is also the SynchronizationContext
class, which lets you basically do the same as Control.Invoke/Control.BeginInvoke
, but with the advantage of not needing a WinForms control reference to be known. Here is a small tutorial on SynchronizationContext
.
You need to perform the action from the thread that owns the control.
That's how I'm doing that without adding too much code noise:
control.Invoke(() => textBox1.Text += "hi");
Where Invoke overload is a simple extension from Lokad Shared Libraries:
/// <summary>
/// Invokes the specified <paramref name="action"/> on the thread that owns
/// the <paramref name="control"/>.</summary>
/// <typeparam name="TControl">type of the control to work with</typeparam>
/// <param name="control">The control to execute action against.</param>
/// <param name="action">The action to on the thread of the control.</param>
public static void Invoke<TControl>(this TControl control, Action action)
where TControl : Control
{
if (!control.InvokeRequired)
{
action();
}
else
{
control.Invoke(action);
}
}
What's even easier is to just use the BackgroundWorker control...
Most simple, without caring about delegates
if(textBox1.InvokeRequired == true)
textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";});
else
textBox1.Text = "Invoke was NOT needed";
Here is the what I have done to avoid CrossThreadException
and writing to the textbox from another thread.
Here is my Button.Click
function- I want to generate a random number of threads and then get their IDs
by calling the getID()
method and the TextBox value while being in that worker thread.
private void btnAppend_Click(object sender, EventArgs e)
{
Random n = new Random();
for (int i = 0; i < n.Next(1,5); i++)
{
label2.Text = "UI Id" + ((Thread.CurrentThread.ManagedThreadId).ToString());
Thread t = new Thread(getId);
t.Start();
}
}
Here is getId
(workerThread) code:
public void getId()
{
int id = Thread.CurrentThread.ManagedThreadId;
//Note that, I have collected threadId just before calling this.Invoke
//method else it would be same as of UI thread inside the below code block
this.Invoke((MethodInvoker)delegate ()
{
inpTxt.Text += "My id is" +"--"+id+Environment.NewLine;
});
}
Have a look at Control.BeginInvoke method. The point is to never update UI controls from another thread. BeginInvoke will dispatch the call to the UI thread of the control (in your case, the Form).
To grab the form, remove the static modifier from the sample function and use this.BeginInvoke() as shown in the examples from MSDN.
참고URL : https://stackoverflow.com/questions/519233/writing-to-a-textbox-from-another-thread
'programing' 카테고리의 다른 글
Rubocop에 특정 디렉터리 또는 파일을 무시하도록 지시하는 방법 (0) | 2020.12.01 |
---|---|
Base64 인코딩 이미지 (0) | 2020.12.01 |
Visual Studio 셸 (독립 실행 형 셸)의 장점은 무엇입니까? (0) | 2020.12.01 |
하위 모듈을 만들지 않고 모델을 하위 폴더로 구조화하는 우아한 방법 (0) | 2020.12.01 |
jQuery에 의존하는 Javascript 위젯을 알 수없는 환경에 포함하는 방법 (0) | 2020.12.01 |