private void InitRichTextBoxRuntime() { if (IsInitialized) { throw new InvalidOperationException("The underlying RichTextBox has already been initialized."); } if (!IsHandleCreated) { base.CreateHandle(); } // We can't pass our actual handle property to another thread, so we make a copy and pass that. if (_thisHandle == IntPtr.Zero) { _thisHandle = Handle; } // Note: a task gets closed automatically on regular shutdown, whereas a thread needs explicit cleanup // or Environment.Exit(). Tasks seem to work just as well and are easier to use, so I'm using a task // for now. if (_asyncTask == null) { _asyncTask = new Task(() => { // The RichTextBox is *created* on this thread, which is what allows it to be async, but we // *declared* it on the main thread, which means we can still reference it, as long as we do // it through an Invoke() or BeginInvoke(). // TODO: We shouldn't be setting ReadOnly here, but just for testing purposes _richTextBoxInternal = new RichTextBox_CH { ReadOnly = true, BackColor = SystemColors.Window }; // And the same sort of setup with the ApplicationContext, just in case we want to call into // it too _asyncRTBAppContext = new RTB_AppContext(this, _richTextBoxInternal, _waitHandle); // This starts a second message loop, which is what we want: the RichTextBox will have its // own UI thread Application.Run(_asyncRTBAppContext); }); _asyncTask.Start(); // We have to use signaling because we're not waiting for the task to finish, but only for the // ApplicationContext's constructor to finish. And besides, Application.Run() means the task will // never finish anyway. _waitHandle.WaitOne(); // This is why we need to pass our handle and run CreateHandle() on the RichTextBox (see below); // this is what puts the RichTextBox inside our main UI (while still keeping it asynchronous) _richTextBoxInternal.Invoke(RTB_DockToUI, true); // "Set Dock to DockStyle.Fill" as it were _richTextBoxInternal.BeginInvoke(new Action(() => _richTextBoxInternal.Location = new Point(0, 0))); SetRichTextBoxSizeToFill(); } IsInitialized = true; }
private void InitRichTextBoxDesignTime() { if (IsInitialized) { throw new InvalidOperationException("The underlying RichTextBox has already been initialized."); } _richTextBoxInternal = new RichTextBox_CH(); Controls.Add(_richTextBoxInternal); _richTextBoxInternal.Dock = DockStyle.Fill; IsInitialized = true; }
internal RTB_AppContext(RichTextBoxAsync owner, RichTextBox_CH richTextBox, AutoResetEvent waitHandle) { _owner = owner; _richTextBox = richTextBox; _richTextBox.KeyDown += _richTextBox_KeyDown; _richTextBox.MouseDown += _richTextBox_MouseDown; // CreateHandle() is not exposed, so to get at it we have to subclass RichTextBox and expose it // ourselves. We could call CreateControl() which is exposed, but that will only work if the // control is visible, and we don't want to have to set it to visible right off the bat. if (!_richTextBox.IsHandleCreated) { _richTextBox.CreateHandle(); } // Notify the main thread that we're done initializing waitHandle.Set(); }