I need to have multiline items, but then every other item height become same as multiline. I don't understand how to control it's height. On the picture below, 3 other items have same height as the first one, but they could be smaller. I see an AutoSize property in ToolStripmenuItem, but then I don't know which event I need to use to calculate it's height.
An example, using a generic class that handles a standard ToolStripProfessionalRenderer object and overrides its OnRenderItemText() method to provide a custom, calculated size, of the MenuItems.
You could of course inject your own ToolStripProfessionalRenderer
and also provide a custom ProfessionalColorTable, to define all colors of the ToolStrip (or derived classes, as the ContextMenuStrip) and its MenuItems
An example here (coded in a slightly different language, but the notes and general description nonetheless apply): Change space between Image and Text in ContextMenuStrip
To make your ContextMenuStrip use this custom renderer, initialize with a ToolStripRenderer derived object (here, a standard ToolStripProfessionalRenderer
) and set its corresponding Property:
var renderer = new MenuDesignerRendererTextWrap<ToolStripProfessionalRenderer>();
[ContextMenuStrip].Renderer = renderer;
Caveats:
I suggest setting the text of MenuItems in code, when this text should wrap to a new line.
You can insert line breaks (any of \r
, \n
or \r\n
can do) to break the text in two or more lines.
It's instead hard to do in the Designer. Both the ContextMenuStrip and the MenuItems auto-size themselves as the default behavior. You also actually want the ContextMenuStrip to auto-size to the provided overall heigh of child MenuItems, otherwise you have to do this yourself.
Also, the default rendering of the Item's text uses TextRenderer to render the text onto a Bitmap. One of the direct effects is that word-wrapping doesn't work, so setting a long string in the Designer doesn't cause the renderer to draw it in more than one line by itself (no matter whether you try to force it, setting custom TextFormatFlags).
You could use Graphics.DrawString()
yourself and bypass TextRenderer in the base class code, but the rendering is different (also, besides the scope of this post :)
As mentioned, it's simpler to set the text of a MenuItem in code, forcing line breaks (just \n
here):
[ToolStripMenuItem].Text = "Some text\nMore text in a new line";
internal class MenuDesignerRendererTextWrap<T> : ToolStripProfessionalRenderer where T :
ToolStripRenderer, new() {
public MenuDesignerRendererTextWrap() : this(new T()) { }
public MenuDesignerRendererTextWrap(T renderer) => Renderer = renderer;
public T Renderer { get; }
private readonly TextFormatFlags flags =
TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) {
if (string.IsNullOrEmpty(e.Text)) return;
// The size of the Item is provided here
e.Item.AutoSize = false;
// An Item's viewport is 3 pixels higher than the text's Height
var textHeight = TextRenderer.MeasureText(
e.Graphics, e.Text, e.TextFont,
new Size(e.TextRectangle.Width, int.MaxValue), flags).Height + 3;
var textRect = new Rectangle(
e.TextRectangle.Left, e.TextRectangle.Top, e.TextRectangle.Width, textHeight);
// Bounds of the Item's text
e.TextRectangle = textRect;
// Overall height of the MenuItem: text's viewport.Height + 3 pixels
e.Item.Height = textHeight + 3;
base.OnRenderItemText(e);
}
}