C# WinForms中Console.WriteLine无法更新RichTextBox问题排查
问题:Console输出重定向至RichTextBox在YouTube上传项目中仅初始消息显示,后续无更新
我已经查阅了所有同类问题,但仍未找到自身场景失效的原因。测试程序中,通过自定义TextBoxStreamWriter类将Console.WriteLine输出重定向至RichTextBox,按钮点击时可正常显示内容;但在基于YouTube API 3开发的上传项目中,仅初始的「uploading video」消息能显示在RichTextBox,后续上传进度、错误信息、视频ID的Console输出均无法更新控件,上传功能可正常完成。
测试类代码
public class TextBoxStreamWriter : TextWriter { RichTextBox _output = null; public TextBoxStreamWriter(RichTextBox output) { _output = output; } public override void Write(char value) { base.Write(value); _output.AppendText(value.ToString()); // When character data is written, append it to the text box. } public override Encoding Encoding { get { return System.Text.Encoding.UTF8; } } }
测试窗体代码
public partial class Form1 : Form { TextWriter _writer = null; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { _writer = new TextBoxStreamWriter(richTextBox1); // Redirect the out Console stream Console.SetOut(_writer); //Console.WriteLine("Now redirecting output to the text box"); } private void button1_Click(object sender, EventArgs e) { // Writing to the Console now causes the text to be displayed in the text box. Console.WriteLine("Hello world"); } }
项目上传功能代码
/// <summary> /// Upload video to youtube /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { //lblstatus.Text = "uploading video"; Console.WriteLine("uploading video"); try { Thread thead = new Thread(() => { Run().Wait(); }); thead.IsBackground = true; thead.Start(); } catch (AggregateException ex) { Console.WriteLine("Error: " + ex.Message); // MessageBox.Show(ex.Message); } } private async Task Run() { UserCredential credential; using (var stream = new FileStream("client_secret", FileMode.Open, FileAccess.Read)) { credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( GoogleClientSecrets.FromStream(stream).Secrets, // This OAuth 2.0 access scope allows an application to upload files to the // authenticated user's YouTube channel, but doesn't allow other types of access. new[] { YouTubeService.Scope.YoutubeUpload }, "user", CancellationToken.None ); } var youtubeService = new YouTubeService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = Assembly.GetExecutingAssembly().GetName().Name }); var video = new Video(); video.Snippet = new VideoSnippet(); video.Snippet.Title = txtTitle.Text; video.Snippet.Description = txtDescription.Text; string[] tagSeo = Regex.Split(txtTagSeo.Text, ","); video.Snippet.Tags = tagSeo; video.Snippet.CategoryId = "22"; video.Status = new VideoStatus(); video.Status.PrivacyStatus = "public"; // or "private" or "public" var filePath = txtPath.Text; // Replace with path to actual movie file. Console.WriteLine(filePath); using (var fileStream = new FileStream(filePath, FileMode.Open)) { var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*"); videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged; videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived; await videosInsertRequest.UploadAsync(); } } void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress) { switch (progress.Status) { case UploadStatus.Uploading: Console.WriteLine("{0} bytes sent.", progress.BytesSent); //lblstatus.Text = String.Format("{0} bytes sent.", progress.BytesSent); break; case UploadStatus.Failed: //lblstatus.Text = String.Format("An error prevented the upload from completing.{0} ", progress.Exception); Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception); break; } } void videosInsertRequest_ResponseReceived(Video video) { Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id); MessageBox.Show("Video id '{0}' was successfully uploaded.", video.Id); }
问题原因
核心是跨线程访问UI控件:
- 测试程序中,
Console.WriteLine在UI线程(按钮点击事件)执行,直接操作RichTextBox无问题; - 上传项目里,后续的
Console.WriteLine运行在后台线程(你新开的Thread、YouTube API的回调线程),而WinForms控件仅允许创建它的UI线程访问,跨线程直接操作会导致控件不更新(未触发强制线程检查时,不会抛出异常,但内容无法显示)。
修复方案
1. 让TextBoxStreamWriter支持跨线程更新
修改Write方法,通过Invoke确保在UI线程操作RichTextBox:
public class TextBoxStreamWriter : TextWriter { RichTextBox _output = null; public TextBoxStreamWriter(RichTextBox output) { _output = output; } public override void Write(char value) { base.Write(value); // 检查是否需要跨线程调用 if (_output.InvokeRequired) { _output.Invoke(new Action(() => _output.AppendText(value.ToString()))); } else { _output.AppendText(value.ToString()); } } public override Encoding Encoding { get { return System.Text.Encoding.UTF8; } } }
2. 优化异步代码写法(可选但推荐)
按钮点击事件里用Thread包裹Run().Wait()的写法冗余,直接用async void(仅适用于事件处理方法)更符合异步规范:
private async void button2_Click(object sender, EventArgs e) { Console.WriteLine("uploading video"); try { await Run(); } catch (AggregateException ex) { Console.WriteLine("Error: " + ex.Message); } }
修改后,所有Console输出无论在哪个线程执行,都会通过UI线程更新RichTextBox,后续的上传进度、结果消息就能正常显示。
内容的提问来源于stack exchange,提问作者user2180706
相关产品推荐
相关产品推荐

