i am trying to give my text editor multiple pages mode the problem is when the richtextbox reaches the last line it resizes and add a scroll bar which is not what i want, i made a code to transfer the last line of the richtextbox to the one that follows but it's moving the whole text instead and it's kind of sluggish, any help would be appreciated
public partial class Form1 : Form
{
protected static bool GetVisibleScrollbars(Control ctl)
{
int wndStyle = Win32.GetWindowLong(ctl.Handle, Win32.GWL_STYLE);
bool vsVisible = (wndStyle & Win32.WS_VSCROLL) != 0;
return vsVisible;
}
public Form1()
{
InitializeComponent();
}
List<RichTextBox> pages=new List<RichTextBox>();
int currentdocindex = 0;
public void AddPage()
{
RichTextBox B = new RichTextBox();
B.Size = richTextBox1.Size;
panel1.Controls.Add(B);
B.Location = new Point(pages[pages.Count - 1].Location.X, pages[pages.Count - 1].Location.Y + richTextBox1.Height + 20);
pages.Add(B);
B.SelectionIndent = 20;
B.SelectionRightIndent = 20;
B.Enter += new EventHandler(richTextBox_Enter);
}
private void richTextBox_Enter(object sender, EventArgs e)
{
int i = 0;
foreach (RichTextBox box in pages)
{
if (box == (RichTextBox)sender)
{
currentdocindex=i;
break;
}
i++;
}
label1.Text = (currentdocindex + 1).ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
pages.Add(richTextBox1);
richTextBox1.SelectionIndent = 20;
richTextBox1.SelectionRightIndent = 20;
}
private void richTextBox1_Enter(object sender, EventArgs e)
{
int i = 0;
foreach (RichTextBox box in pages)
{
if(box==(RichTextBox)sender)
{
currentdocindex=i;
break;
}
i++;
}
}
bool added = false;
private void timer1_Tick(object sender, EventArgs e)
{
int correntPageIndex = currentdocindex;
if (GetVisibleScrollbars(pages[currentdocindex]))
{
if (!added)
{
AddPage();
added = true;
}
}
else
{
added = false;
}
}
if(GetVisibleScrollbars(pages[correntPageIndex]))
{
string LastLineText = pages[correntPageIndex].Lines[pages[correntPageIndex].Lines.Count() - 1];
int LastLineStartIndex = pages[correntPageIndex].Text.LastIndexOf(LastLineText);
pages[correntPageIndex].SelectionStart = LastLineStartIndex;
pages[correntPageIndex].SelectionLength = pages[correntPageIndex].Text.Length - 1;
LastLineText = pages[correntPageIndex].SelectedRtf;
pages[correntPageIndex].Text = pages[correntPageIndex].Text.Remove(LastLineStartIndex);
pages[correntPageIndex + 1].SelectionStart = 0;
pages[correntPageIndex+1].SelectedRtf = LastLineText;
}
}
}
public class Win32
{
// offset of window style value
public const int GWL_STYLE = -16;
// window style constants for scrollbars
public const int WS_VSCROLL = 0x00200000;
public const int WS_HSCROLL = 0x00100000;
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
}
RichTextBox is a pain for this sort of thing, because to mutate a small portion of text you have to actually select the text first (which it appears you're attempting to do) and ensure the change only affects that text. It's a little nasty on the memory usage, but you might be better served by determining how many characters you want per page and subscribing to the KeyDown Event to determine when you move to a new page. Try to adapt something like this and see if it works better.
public void MyKeyDownHandler(object sender, System.Windows.Forms.KeyEventArgs e)
{
if(this.CurrentPageControl.RTB.Text.Length >= MY_LIMITING_CONSTANT_I_SET)
{
MyPageUserControl mpuc = new MyPageUserControl();
mpuc.RTB.Text = this.CurrentPageControl.RTB.Text.Split(' ').Last();
thePageCollectionIPresumeYouHave.Add(mpuc);
this.CurrentPageControl = thePageCollectionIPresumeYouHave.Last();
mpuc.RTB.Focus();
}
}
Caveat: I did that entirely from memory and without a chance to read all of your code ( I had to skim) because I'm at work.
Another Caveat: I assumed you put your RichTextBoxes in a custom "page" control. If you didn't, I hope my code shows you why you might want to.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm trying to move a picture box fast as it is representing a bullet. However, there is a flickering effect and it obscures the image and it is very hard to see the bullet move. I have tried to use double buffering and Invalidating the picture box before its moved but to no avail. Any suggestions? Maybe im using double buffering wrong? (I have it set to be enabled when the form is loaded.)
Code
On the Form:
public void Shoot(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
PictureBox bulletImage = new PictureBox();
DoubleBuffered = true;
StandardBullet bullet = new StandardBullet(PB_CHARA.Location.X, PB_CHARA.Location.Y, FRM_GAME.MousePosition.X, FRM_GAME.MousePosition.Y, this.ClientRectangle, bulletImage);
Controls.Add(bulletImage);
}
}
Within the Standard Bullet class:
public class StandardBullet
{
public string ImageName = "DataBaseMod.Properties.Resources.StandardBullet_3x";
public int sizeX = 15;
public int sizeY = 19;
public int x = 0;
public int y = 0;
int charaPostitionX;
int charaPostitionY;
PictureBox bulletPoint;
public int[] vector = new int[2];
private System.Timers.Timer bulletTimer;
private System.Timers.Timer RemoveTimer;
System.Drawing.Rectangle FRMBounds;
//public delegate void UpdateControlsDelegate();
public StandardBullet(int charaPostiX, int charaPostiY, int MousePostiX, int MousePostiY, System.Drawing.Rectangle FRMboundaries, PictureBox bulletImage)
{
FRMBounds = FRMboundaries;
bulletPoint = bulletImage;
bulletPoint.Name = ImageName;
string filename = ImageName;
bulletPoint.BackgroundImage = DataBaseMod.Properties.Resources.StandardBullet_3x;
var size = new System.Drawing.Size(sizeX, sizeY);
bulletPoint.Size = size;
bulletPoint.BackgroundImageLayout = ImageLayout.Stretch;
charaPostitionX = charaPostiX;
charaPostitionY = charaPostiY;
x = charaPostiX;
y = charaPostiY;
vector[0] = charaPostiX - MousePostiX;
vector[1] = charaPostiY - MousePostiY;
vectorCalc();
bulletTimer = new System.Timers.Timer(10);
RemoveTimer = new System.Timers.Timer(100);
bulletTimer.Elapsed += TickHandler;
bulletTimer.AutoReset = true;
bulletTimer.Enabled = true;
RemoveTimer.Elapsed += removeTickHandler;
RemoveTimer.Enabled = true;
RemoveTimer.AutoReset = true;
}
public void TickHandler(object sender, ElapsedEventArgs e)
{
x = x + vector[0];
y = y + vector[1];
moveBullet();
}
public void removeTickHandler(object sender, ElapsedEventArgs e)
{
RemoveBullet();
}
public void moveBullet()
{
bulletPoint.BeginInvoke(new MethodInvoker(() => { bulletPoint.Location = new System.Drawing.Point(x, y); }));
}
public void vectorCalc()
{
if (vector[0] >= 1)
{
vector[0] = -10;
}
else if (vector[0] <= -1)
{
vector[0] = 10;
}
if (vector[1] >= 1)
{
vector[1] = -10;
}
else if (vector[1] <= -1)
{
vector[1] = 10;
}
}
public void RemoveBullet()
{
if (
(FRMBounds.Left >= bulletPoint.Bounds.Left) ||
( FRMBounds.Right <= bulletPoint.Bounds.Right) ||
(FRMBounds.Top >= bulletPoint.Bounds.Top) ||
(FRMBounds.Bottom <= bulletPoint.Bounds.Bottom)
)
{
Death();
return;
}
}
public void Death()
{
try
{
bulletTimer.Enabled = false;
bulletPoint.Invoke(new MethodInvoker(() => { FRM_GAME.KillBullet(bulletPoint); }));
RemoveTimer.Enabled = false;
}
catch(Exception e)
{
}
}
}
Thanks!
EDIT: I am going to remove this as i think the error may have been casued by my computer. I had two other games running whilst this one and i think this may have caused the poor rendering. Ran my code this morning and everything is fine. Sorry for this.
Using WinForms for dynamic graphics purposes is not a good idea. Instead of controls, try using Graphics and draw desired object on form (or panel or anywhere you want to).
The premise is really simple - I have a class that is running some expensive computation and I would like to show a ProgressBar to inform the user. Since I might have many computation cycles, I want to have a simple ProgressBar form that I can use many times in my code. That form does not need to know anything else but it's title, the maximum value for the ProgressBar and when to perform a step and increment. The update call is done from the Other Class, the form just displays that.
The following code is from a Windows Forms version of a ProgressBar (exported as a dll) that achieves the goal. I am trying to recreate the same functionality with WPF. From my research so far, this cannot be done. I would love if someone can prove me wrong and demonstrate a pseudo code for both the WPF codebehind and the implentation in the Other Class.
Windows Form
public partial class ProgressForm : Form {
private bool abortFlag;
string _format;
/// <summary>
/// Set up progress bar form and immediately display it modelessly.
/// </summary>
/// <param name="caption">Form caption</param>
/// <param name="format">Progress message string</param>
/// <param name="max">Number of elements to process</param>
public ProgressForm( string caption, string format, int max )
{
_format = format;
InitializeComponent();
Text = caption;
label1.Text = (null == format) ? caption : string.Format( format, 0 );
progressBar1.Minimum = 0;
progressBar1.Maximum = max;
progressBar1.Value = 0;
Show();
Application.DoEvents();
}
public void Increment()
{
++progressBar1.Value;
if( null != _format )
{
label1.Text = string.Format( _format, progressBar1.Value );
}
Application.DoEvents();
}
public bool getAbortFlag()
{
return abortFlag;
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Aborting...";
abortFlag = true;
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
{
button1.Text = "Aborting...";
abortFlag = true;
return true;
}
return base.ProcessDialogKey(keyData);
}
public void SetText(string text)
{
label1.Text = text;
System.Windows.Forms.Application.DoEvents();
}
public void SetProgressBarMinMax(int min, int max)
{
progressBar1.Minimum = min;
progressBar1.Maximum = max;
progressBar1.Value = 0;
}
public void IncrementProgressBar()
{
progressBar1.Value++;
System.Windows.Forms.Application.DoEvents();
}
public void HideProgressBar()
{
progressBar1.Visible = false;
System.Windows.Forms.Application.DoEvents();
}
public void ShowProgressBar()
{
progressBar1.Visible = true;
System.Windows.Forms.Application.DoEvents();
}
}
Some Method in Other Class
public class OtherClass(){
int counter = 0;
int n = numberOfIterations;
string s = "{0} of " + n.ToString() + String.Format(" elements processed.", counter.ToString());
string caption = "Duplicating ..";
using (ProgressForm pf = new ProgressForm(caption, s, n)){
//Something heavy to compute
foreach (var sheet in sheetsToDuplicate)
{
if (pf.getAbortFlag())
{
return;
}
counter ++;
pf.Increment();
}
}
}
I think this should be pretty straightfoward but I cannot find a single source out there to show how this can be achieved. For me, the important points to hightlight are:
The WPF (Window) ProgressBar control does not have reference to the Other Class. (because it will be shipped as a dll and should be able to adopt any scenario)
The loop is run in the Other Class.
Thank you.
(Short Screen-Cast explaining the problem)
I'm making a Clipboard program, that allows you to see what's in your clipboard.
It looks like this:
It seems to work fine copying on-the-fly.
The problem is that I want to be able to go back to a previous img/txt in clipboard and use it - that's when I use the check-mark button.
It works, the only problem is that it copies it twice into the list of images / listbox I'm using. It also happens when I initialize the listbox/picturebox.
Here's the code:
namespace Clipboard_Wizard
{
public partial class FormMain : Form
{
//register the program in the clipboard viewer chain
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
private const int WM_DRAWCLIPBOARD = 0x0308; // WM_DRAWCLIPBOARD message
private IntPtr _clipboardViewerNext; // Our variable that will hold the value to identify the next window in the clipboard viewer chain
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
ChangeClipboardChain(this.Handle, _clipboardViewerNext); // Removes our from the chain of clipboard viewers when the form closes.
}
List<Image> img = new List<Image>();
int ImgCount = 0;
int ImgIndex = -1;
Image tmp;
public FormMain()
{
InitializeComponent();
_clipboardViewerNext = SetClipboardViewer(this.Handle); // Adds our form to the chain of clipboard viewers.
//set listbox/picturebox to whatever already on clipboard
if (Clipboard.ContainsImage())
{
tmp = Clipboard.GetImage();
pictureBox1.Image = tmp;
img.Add(tmp);
ImgCount++;
ImgIndex++;
btnSlctImg.Enabled = true;
label3.Text = "Image 1 / 1";
}
else if (Clipboard.ContainsText())
{
listBox1.Items.Add(Clipboard.GetText());
}
}
// clears everything from the form and the clipboard
private void btnClear_Click(object sender, EventArgs e)
{
Clipboard.Clear();
listBox1.Items.Clear();
img.Clear();
pictureBox1.Image = null;
ImgCount = 0;
ImgIndex = -1;
btnSlctImg.Enabled = false;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
/*if (Clipboard.ContainsImage())
{
tmp = Clipboard.GetImage();
pictureBox1.Image = tmp;
img.Add(tmp);
ImgCount++;
ImgIndex = ImgCount - 1;
}
else if (Clipboard.ContainsText())
{
listBox1.Items.Add(Clipboard.GetText());
listBox1.TopIndex = listBox1.Items.Count - 1;
}*/
}
private void btnUp_Click(object sender, EventArgs e)
{
if(ImgIndex == -1)
{
MessageBox.Show("No image.");
}
else if (ImgIndex < ImgCount - 1)
{
ImgIndex++;
pictureBox1.Image = img[ImgIndex];
label3.Text = "Image " + (ImgIndex + 1).ToString() + " / " + ImgCount.ToString() ;
}
else
{
MessageBox.Show("This is the last image.");
}
}
private void btnDown_Click(object sender, EventArgs e)
{
if(ImgIndex == -1)
{
MessageBox.Show("No image.");
}
else if (ImgIndex > 0)
{
ImgIndex--;
pictureBox1.Image = img[ImgIndex];
label3.Text = "Image " + (ImgIndex + 1).ToString() + " / " + ImgCount.ToString();
}
else
{
MessageBox.Show("This is the first image.");
}
}
private void btnDeselect_Click(object sender, EventArgs e)
{
listBox1.SelectedIndex = -1;
}
//sets clipboard to selected txt from listbox
private void btnSlct_Click(object sender, EventArgs e)
{
string slctTxt = listBox1.SelectedItem.ToString();
if (slctTxt != null || slctTxt != "")
{
Clipboard.SetText(slctTxt);
}
}
//sets clipboard to selected image
private void btnSlctImg_Click(object sender, EventArgs e)
{
Image slctImg = pictureBox1.Image;
if (slctImg != null)
{
Clipboard.SetImage(slctImg);
}
}
private void listBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
btnSlctTxt.Enabled = true;
}
else
{
btnSlctTxt.Enabled = false;
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m); // Process the message
if (m.Msg == WM_DRAWCLIPBOARD)
{
//btnUpdate.PerformClick();
IDataObject iData = Clipboard.GetDataObject(); // Clipboard's data
if (iData.GetDataPresent(DataFormats.Text))
{
string text = (string)iData.GetData(DataFormats.Text); // Clipboard text
listBox1.Items.Add(text);
listBox1.TopIndex = listBox1.Items.Count - 1;
}
else if (iData.GetDataPresent(DataFormats.Bitmap))
{
tmp = (Bitmap)iData.GetData(DataFormats.Bitmap); // Clipboard image
pictureBox1.Image = tmp;
img.Add(tmp);
ImgCount++;
ImgIndex = ImgCount - 1;
label3.Text = "Image " + ImgCount.ToString() + " / " + ImgCount.ToString();
btnSlctImg.Enabled = true;
}
}
}
}
}
Update: It seems the problem is whenever I call the Clipboard.SetImage(...) or Clipboard.SetText(...) - it does it twice. Still don't understand why though.
You have defined a WndProc to catch changes to the clipboard and add the contents to lists.
In your btnSlctImg_Click to do just that:
if (slctImg != null) { Clipboard.SetImage(slctImg); }
So, of course the clipboard is changed, the WndProc is triggered and the currently selected image is added once again to the list you have..
To avoid that you may need to test the lists to see if the image or the text are already in the list. For text this is trivial but for images this is anything but simple. You may have to create and store fingerprints to decide if an image is already in the list.
Here is a post that has examples of creating an MD5 hash for an image.
A simpler trick would be a flag you set in the btnSlctImg_Click right before the Clipboard.SetImage and test and clear in the WndProc. You could still get duplicates, but only if the same data are copied by the user outside of the program..
I wrote a Clipboard utility some time ago and posted it on CodeProject ClipSpy+
I think it will help you with what you are doing!
I'm able now to synchronize my two RichTextBox using this potion of code:
private const int SB_HORZ = 0x0;
private const int SB_VERT = 0x1;
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 4;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(int hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
internal int HScrollPos
{
private get { return GetScrollPos((int)this.Handle, SB_HORZ); }
set
{
SetScrollPos((IntPtr)this.Handle, SB_HORZ, value, true);
PostMessageA((IntPtr)this.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
}
}
internal int VScrollPos
{
get { return GetScrollPos((int)this.Handle, SB_VERT); }
set
{
SetScrollPos((IntPtr)this.Handle, SB_VERT, value, true);
PostMessageA((IntPtr)this.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
}
}
I can synchronize the RichTextBoxes while key down ,up and Vscroll event.
Indeed this is not my goal, I want to synchronize my RichTextBoxes basing on the content,
What do I need:
Get the current line form non-selected RichTextBox.
Set Scroll bar position using line number in the other RichTextBox (without losing the focus from the current one).
Get line number using scroll bar position.
Note: you are welcome to ask if you need any more details.
Thanks in advance.
From what I understand, you need to synchronize scrolling on 2 RichTextBoxes based on the line number. Leave me a comment if I misunderstood it.
RichTextBox extended :
public class RichTextBoxEx : RichTextBox
{
// combination of multiple events that may cause focus(caret) to change
public event EventHandler FocusChanged;
public RichTextBoxEx()
{
this.KeyPress += (s, e) => RaiseFocusChanged();
this.KeyDown += (s, e) => RaiseFocusChanged();
this.KeyUp += (s, e) => RaiseFocusChanged();
this.MouseClick += (s, e) => RaiseFocusChanged();
}
private void RaiseFocusChanged()
{
var focusChanged = FocusChanged;
if (focusChanged != null)
{
focusChanged(this, null);
}
}
public int GetFirstSelectedLine()
{
var index = GetFirstCharIndexOfCurrentLine();
return GetLineFromCharIndex(index);
}
public int GetFirstVisibleLine()
{
var index = GetCharIndexFromPosition(new Point(1, 1));
return GetLineFromCharIndex(index);
}
public void ScrollToLine(int line)
{
if (line < 0)
throw new ArgumentOutOfRangeException("line cannot be less than 0");
// save the current selection to be restored later
var selection = new { SelectionStart, SelectionLength };
// select that line and scroll it to
Select(GetFirstCharIndexFromLine(line) + 1, 0);
ScrollToCaret();
// restore selection
Select(selection.SelectionStart, selection.SelectionLength);
}
}
Usage :
void Main()
{
var mainScreenArea = Screen.PrimaryScreen.WorkingArea;
var rich1 = new RichTextBoxEx() { Width = mainScreenArea.Width / 2 - 10, Dock = DockStyle.Left };
var rich2 = new RichTextBoxEx() { Width = mainScreenArea.Width / 2 - 10, Dock = DockStyle.Right };
rich1.LoadFile(__RTF_FILE_0__);
rich2.LoadFile(__RTF_FILE_1__);
// pick one :
// synchronize by focus
rich1.FocusChanged += (s, e) => rich2.ScrollToLine(rich1.GetFirstSelectedLine());
// synchronize by viewbox
// rich1.VScroll += (s, e) => rich2.ScrollToLine(rich1.GetFirstVisibleLine());
var form = new Form();
form.Controls.Add(rich1);
form.Controls.Add(rich2);
form.WindowState = FormWindowState.Maximized;
form.ShowDialog()
}
This code automatically sizes a RichTextBox according to it's contents. I'm having issues, especially with tables. \t may be ignored. I tried a managed solution, now I'm trying platform invoke. Current Output:
[DllImport("gdi32.dll")]
static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetDC(IntPtr hWnd);
[StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
public SIZE(int cx, int cy)
{
this.cx = cx;
this.cy = cy;
}
}
public static void Main()
{
Form form = new Form();
RichTextBox rtfBox = new RichTextBox();
rtfBox.Rtf = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil Arial;}}\viewkind4\uc1\trowd\trgaph100\cellx1000\cellx2000\pard\intbl\lang1033\f0\fs20 hi\cell bye\cell\row\intbl one\cell two\cell\row\pard\par}";
rtfBox.ScrollBars = RichTextBoxScrollBars.None;
string sInput = "hi\t bye\t\n";// one\t two\t\n";
SIZE CharSize;
form.Controls.Add(rtfBox);
IntPtr hdc = GetDC(IntPtr.Zero);//Context for entire screen
GetTextExtentPoint32(hdc, sInput, sInput.Length, out CharSize);
rtfBox.Width = CharSize.cx;
rtfBox.Height = CharSize.cy;
form.Visible = false;
form.ShowDialog();
}
(Note, for simplicity this is a console application with a reference to System.Windows.Forms.dll)
Have you looked at the ContentsResized event? Add the following method to be called when the event fires:
private void richTextBox_ContentsResized(object sender, ContentsResizedEventArgs e)
{
var richTextBox = (RichTextBox) sender;
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
}
When the RTF content is changed (using Rtf), the RichTextBox should be resized to match its contents. Make sure you also set the WordWrap property to false.
I've tried it with your table example and it does appear to work (albeit with a little offset, which you could possibly solve by adding a few pixels of width to the adjusted size - not sure why that happens):
P.Brian.Mackey EDIT
This answer worked for me. To clarify, here's the final code including border fix:
public static void Main()
{
string sInput = "hi\t bye\t\n";// one\t two\t\n";
SIZE CharSize;
Form form = new Form();
RichTextBox rtfBox = new RichTextBox();
rtfBox.ContentsResized += (object sender, ContentsResizedEventArgs e) =>
{
var richTextBox = (RichTextBox)sender;
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
rtfBox.Width += rtfBox.Margin.Horizontal + SystemInformation.HorizontalResizeBorderThickness;
};
rtfBox.WordWrap = false;
rtfBox.ScrollBars = RichTextBoxScrollBars.None;
rtfBox.Rtf = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil Arial;}}\viewkind4\uc1\trowd\trgaph100\cellx1000\cellx2000\pard\intbl\lang1033\f0\fs20 hi\cell bye\cell\row\intbl one\cell two\cell\row\pard\par}";
form.Controls.Add(rtfBox);
form.ShowDialog();
}
It's much easier to use GetPreferredSize, as described in this answer. Then you don't need to wait for a ContentsResized event,
What about the Height ?
I added
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
at the end.
It also looks like a good candidate for an extension method :
static public class RichTextBoxResizer {
static public void ResizeToContents(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Height = e.NewRectangle.Height;
richTextBox.Width += richTextBox.Margin.Horizontal +
SystemInformation.HorizontalResizeBorderThickness +
SystemInformation.HorizontalScrollBarThumbWidth;
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
}
static public void ResizeToContentsHorizontally(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Width = e.NewRectangle.Width;
richTextBox.Width += richTextBox.Margin.Horizontal +
SystemInformation.HorizontalResizeBorderThickness +
SystemInformation.HorizontalScrollBarThumbWidth;
}
static public void ResizeToContentsVertically(this RichTextBox richTextBox, ContentsResizedEventArgs e) {
richTextBox.Height = e.NewRectangle.Height;
richTextBox.Height += richTextBox.Margin.Vertical +
SystemInformation.VerticalResizeBorderThickness;
}
}
So the event sink looks like :
private void rtfBox_ContentsResized(object sender, ContentsResizedEventArgs e) {
RichTextBox rtb = (RichTextBox)sender;
rtb.ResizeToContents(e);
}