Hi,
As a sample class, RichText doesn’t have all the features of the GoText class. When AutoResizes is true (the default), it really means “fixed width, however long it needs to be with auto-wrapping”.
The SendMessage calls are used because (at least at the time this sample was written) there wasn’t support in the .NET RichTextBox to format and render the Richtext to an image in the way we use it.
Let me research this a bit…
OK… this method
private int CalculateWidth(RichTextBox box) {
Point p = box.GetPositionFromCharIndex(box.TextLength);
for (int i = 0; i < box.Lines.Length; i++) {
Point lp = box.GetPositionFromCharIndex(box.Lines.Length);
if (lp.X > p.X) {
p = lp;
}
}
return p.X + 1;
}
(borrowed from: Measure rich text strings)
and added to Measure in GoDiagram’s RichText:
private SizeF Measure() {
RectangleF drect = this.Bounds;
int width = 999999999; // new - was: Math.Max((int)Math.Ceiling(drect.Width), 10);
int height = 10; // doesn’t matter?
RichTextBox box = GetRichTextBox();
box.Size = new Size(width, height);
box.Rtf = this.Rtf;
width = CalculateWidth(box); // new
box.Size = new Size(width, height); // new
… remainder unchanged
will get you CLOSER to what you want. I still see some behavior where it seems to wrap where it shouldn’t… like the CalculateWidth isn’t working perfectly (when you grab random code off the internet, you get what you pay for).
Thanks Jake.
I tried changing the Paint() and GetImage() code with same changes proposed for Measure() and it becomes extremely huge and deformed.
hmm… works for me, I double checked.
If you’ve made changes to RichText… go back to the version from NodeLinkDemo and insert my changes there. In fact, just try them in NodeLinkDemo first.
I didn’t change Paint or GetImage.
Thanks Jake.
private int CalculateWidth(RichTextBox box)
{
Point p = box.GetPositionFromCharIndex(box.TextLength);
for (int i = 0; i < box.Lines.Length; i++)
{
Point lp = box.GetPositionFromCharIndex(box.Lines.Length);
if (lp.X > p.X)
{
p = lp;
}
}
return p.X + 1;
}
private SizeF Measure()
{
/* JAKE MOD
RectangleF drect = this.Bounds;
int width = Math.Max((int)Math.Ceiling(drect.Width), 10);
int height = 10; // doesn’t matter?
RichTextBox box = GetRichTextBox();
box.Size = new Size(width, height);
/
/ JAKE MOD /
RectangleF drect = this.Bounds;
int width = 999999999; // new - was: Math.Max((int)Math.Ceiling(drect.Width), 10);
int height = 10; // doesn’t matter?
RichTextBox box = GetRichTextBox();
box.Size = new Size(width, height);
box.Rtf = this.Rtf;
width = CalculateWidth(box); // new
box.Size = new Size(width, height); // new
/ END JAKE MOD */
box.Rtf = this.Rtf;
Image img = new Bitmap(width, height);
Graphics g = Graphics.FromImage(img);
float dpix = g.DpiX;
float dpiy = g.DpiY;
IntPtr imgdc = g.GetHdc();
FORMATRANGE fr;
fr.hdc = imgdc;
fr.hdcTarget = imgdc;
fr.rc.left = 0;
fr.rc.top = 0;
fr.rc.right = (int)(width * 1440 / dpix); // convert to TWIPS
fr.rc.bottom = 999999999;
fr.rcPage.left = 0;
fr.rcPage.top = 0;
fr.rcPage.right = (int)(width * 1440 / dpix);
fr.rcPage.bottom = 999999999;
fr.chrg.cpMin = 0;
fr.chrg.cpMax = -1;
IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr));
Marshal.StructureToPtr(fr, lpar, false);
IntPtr result = SendMessage(box.Handle, EM_FORMATRANGE, new IntPtr(0), lpar); // 0=measure
FORMATRANGE fr2 = (FORMATRANGE)Marshal.PtrToStructure(lpar, typeof(FORMATRANGE));
float newheight = (fr2.rc.bottom - fr2.rc.top) * dpiy / 1440;
if (newheight > 999999)
newheight = 20;
SizeF size = new SizeF(width, newheight);
SendMessage(box.Handle, EM_FORMATRANGE, new IntPtr(0), new IntPtr(0)); // free up cached data
Marshal.FreeCoTaskMem(lpar);
g.ReleaseHdc(imgdc);
g.Dispose();
return size;
}
private void UpdateSize()
{
SizeF newsize = Measure();
SetSizeKeepingLocation(newsize);
}
public class RichTextBoxControl : System.Windows.Forms.RichTextBox, IGoControlObject
{ // nested class
public RichTextBoxControl()
{
this.AllowDrop = false;
this.AutoSize = false;
this.ScrollBars = RichTextBoxScrollBars.ForcedBoth;
this.WordWrap = false;
}
public GoControl GoControl
{
get { return myGoControl; }
set
{
GoControl old = myGoControl;
if (old != value)
{
myGoControl = value;
if (value != null)
{
RichText gorichtext = value.EditedObject as RichText;
if (gorichtext != null)
{
this.Rtf = gorichtext.Rtf;
this.BackColor = gorichtext.BackgroundColor;
}
}
}
}
}
public GoView GoView
{
get { return myGoView; }
set
{
myGoView = value;
this.ZoomFactor = myGoView.DocScale;
}
}
protected override bool ProcessDialogKey(System.Windows.Forms.Keys key)
{
if (key == System.Windows.Forms.Keys.Escape)
{
GoControl ctrl = this.GoControl;
if (ctrl != null)
ctrl.DoEndEdit(this.GoView);
this.GoView.Focus();
return true;
}
else if (key == System.Windows.Forms.Keys.Tab)
{
GoControl ctrl = this.GoControl;
if (ctrl != null)
{
RichText gotext = ctrl.EditedObject as RichText;
if (gotext != null)
{
gotext.Rtf = this.Rtf;
}
ctrl.DoEndEdit(this.GoView);
this.GoView.Focus();
}
return true;
}
else
{
return base.ProcessDialogKey(key);
}
}
protected override void OnLeave(EventArgs evt)
{
GoControl ctrl = this.GoControl;
if (ctrl != null)
{
RichText gotext = ctrl.EditedObject as RichText;
if (gotext != null)
{
gotext.Rtf = this.Rtf;
}
ctrl.DoEndEdit(this.GoView);
}
base.OnLeave(evt);
}
// TextBoxControl state
private GoControl myGoControl = null;
private GoView myGoView = null;
} // end of TextBoxControl
public override bool OnSingleClick(GoInputEventArgs evt, GoView view)
{
if (!CanEdit()) return false;
if (!view.CanEditObjects()) return false;
if (evt.Shift || evt.Control) return false;
DoBeginEdit(view);
return true;
}
public override void DoBeginEdit(GoView view)
{
if (view == null) return;
if (this.Editor != null) return; // already editing
view.StartTransaction();
RemoveSelectionHandles(view.Selection);
myEditor = CreateEditor(view);
this.Editor.EditedObject = this; // associate editor with this text object
view.EditControl = this.Editor; // add GoControl object to view layer
System.Windows.Forms.Control ctrl = this.Editor.GetControl(view); // create Control in view
if (ctrl != null)
{
ctrl.Focus();
}
}
public override GoControl CreateEditor(GoView view)
{
GoControl editor = new GoControl();
editor.ControlType = typeof(RichTextBoxControl);
// make somewhat bigger, for a border
RectangleF rect = this.Bounds;
rect.X -= 2;
rect.Y -= 2;
rect.Width += 4 + SystemInformation.VerticalScrollBarWidth * view.DocScale;
rect.Height += 4;
editor.Bounds = rect;
return editor;
}
public override GoControl Editor
{
get { return myEditor; }
}
public override void DoEndEdit(GoView view)
{
if (this.Editor != null)
{
this.Editor.EditedObject = null; // disassociate
if (view != null)
{
view.EditControl = null; // remove GoControl from view
}
myEditor = null;
if (view != null)
{
view.RaiseObjectEdited(this);
view.FinishTransaction(GoUndoManager.TextEditName);
}
}
}
public override void ChangeValue(GoChangedEventArgs e, bool undo)
{
switch (e.SubHint)
{
case ChangedRtf:
this.Rtf = (String)e.GetValue(undo);
return;
case ChangedBackgroundColor:
this.BackgroundColor = (Color)e.GetValue(undo);
return;
case ChangedAutoResizes:
this.AutoResizes = (bool)e.GetValue(undo);
return;
default:
base.ChangeValue(e, undo);
return;
}
}
public const int ChangedRtf = 1551;
public const int ChangedBackgroundColor = 1552;
public const int ChangedAutoResizes = 1553;
private const int
flagAutoResizes = 0x0100; // if true, reset bounding rect when text changes
private static RichTextBox myRichTextBox = null;
private String myString = “”;
private Color myBackgroundColor = Color.White;
private int myInternalTextFlags = flagAutoResizes;
[NonSerialized]
private Bitmap myImage = null;
[NonSerialized]
private GoControl myEditor = null;
}
public class GeneratorRichText : GoSvgGenerator
{
public GeneratorRichText() { this.TransformerType = typeof(RichText); }
public override void GenerateDefinitions(Object obj)
{
base.GenerateDefinitions(obj);
RichText rt = (RichText)obj;
Image image = rt.GetImage();
if (image != null && image.Width > 0 && image.Height > 0)
this.Writer.DefineObject(image);
}
public override void GenerateBody(Object obj)
{
RichText rt = (RichText)obj;
base.GenerateBody(obj);
Image image = rt.GetImage();
if (image != null && this.Writer.FindShared(image) != null)
{
String id = this.Writer.FindShared(image);
RectangleF r = rt.Bounds;
WriteStartElement(“use”);
float xscale = r.Width / image.Width; // already checked to be non-zero
float yscale = r.Height / image.Height;
WriteAttrVal(“transform”, “translate(” + r.X.ToString() + “,” + r.Y.ToString() + “) scale(” + xscale.ToString() + “,” + yscale.ToString() + “)”);
if (id != null)
WriteAttrVal(“xlink:href”, “#S” + id);
WriteEndElement();
}
}
}
}
goView.Refresh();
}
}
By the way, AutoResize is at false. I don’t want to auto-resize. I just want it to look exactly as it is when I’m in edit mode.
If you don’t AutoResize, it’s going to wrap. The editor doesn’t grow while you are typing and making lines longer… but the Bounds have to grow to fit the new text when you edit more.