【发布时间】:2009-08-19 07:59:46
【问题描述】:
给定一个带有MultiLine = true 和AcceptsTab == true 的WinForms TextBox 控件,如何设置显示的制表符的宽度?
我想将其用作插件的快速而肮脏的脚本输入框。真的完全不需要花哨,但如果标签不显示为 8 个字符宽就好了……
【问题讨论】:
-
你也应该处理图形,也许把它放在 using 语句中。
给定一个带有MultiLine = true 和AcceptsTab == true 的WinForms TextBox 控件,如何设置显示的制表符的宽度?
我想将其用作插件的快速而肮脏的脚本输入框。真的完全不需要花哨,但如果标签不显示为 8 个字符宽就好了……
【问题讨论】:
我认为将 EM_SETTABSTOPS 消息发送到 TextBox 会起作用。
// set tab stops to a width of 4
private const int EM_SETTABSTOPS = 0x00CB;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr h, int msg, int wParam, int[] lParam);
public static void SetTabWidth(TextBox textbox, int tabWidth)
{
Graphics graphics = textbox.CreateGraphics();
var characterWidth = (int)graphics.MeasureString("M", textbox.Font).Width;
SendMessage
( textbox.Handle
, EM_SETTABSTOPS
, 1
, new int[] { tabWidth * characterWidth }
);
}
这可以在 Form 的构造函数中调用,但请注意:确保首先运行 InitializeComponents。
【讨论】:
我知道您目前使用的是TextBox,但如果您可以改用RichTextBox,那么您可以使用 SelectedTabs 属性来设置所需的标签宽度:
richTextBox.SelectionTabs = new int[] { 15, 30, 45, 60, 75};
请注意,这些偏移量是像素,而不是字符。
【讨论】:
提供的示例不正确。
EM_SETTABSTOPS 消息要求标签大小以 dialog template units 而不是像素为单位指定。经过一番挖掘,似乎对话模板单元等于1/4th the average width of the window's character。因此,您需要为 2 个字符的长制表符指定 8,为 4 个字符指定 16,依此类推。
所以代码可以简化为:
public static void SetTabWidth(TextBox textbox, int tabWidth)
{
SendMessage(textbox.Handle, EM_SETTABSTOPS, 1,
new int[] { tabWidth * 4 });
}
【讨论】:
通过使用扩展方法,您可以向 TextBox 控件类添加新方法。这是我从上面的先前贡献者那里收集到的实现(包括一个额外的扩展方法,它为您提供插入插入符号当前位置的坐标):
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Extensions
{
public static class TextBoxExtension
{
private const int EM_SETTABSTOPS = 0x00CB;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr h, int msg, int wParam, int[] lParam);
public static Point GetCaretPosition(this TextBox textBox)
{
Point point = new Point(0, 0);
if (textBox.Focused)
{
point.X = textBox.SelectionStart - textBox.GetFirstCharIndexOfCurrentLine() + 1;
point.Y = textBox.GetLineFromCharIndex(textBox.SelectionStart) + 1;
}
return point;
}
public static void SetTabStopWidth(this TextBox textbox, int width)
{
SendMessage(textbox.Handle, EM_SETTABSTOPS, 1, new int[] { width * 4 });
}
}
}
【讨论】:
对于任何想要不同标签宽度的人,我采用了这种方法:
using System.Runtime.InteropServices;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr h, int msg, int wParam, uint[] lParam);
private const int EM_SETTABSTOPS = 0x00CB;
private void InitialiseTabStops()
{
// Declare relative tab stops in character widths
var tabs = new uint[] { 2, 2, 4, 8, 2, 32 };
// Convert from character width to 1/4 character width
for (int position = 0; position < tabs.Length; position++)
tabs[position] *= 4;
// Convert from relative to absolute positions
for (int position = 1; position < tabs.Length; position++)
tabs[position] += tabs[position - 1];
SendMessage(textBox.Handle, EM_SETTABSTOPS, tabs.Length, tabs);
}
【讨论】: