我有一个Winforms对话框,其中包含一个允许单行输入的TextBox。我想允许用户能够按Ctrl-Backspace删除整个单词。这不是开箱即用的TextBox的默认行为;我得到一个矩形字符,而不是删除单词。
我已确认ShortcutsEnabled
属性设置为True
。
我确实发现我可以使用RichTextBox而不是TextBox来获取我想要的行为。这个问题是RichTextBox的外观(特别是边框)与TextBox的外观不同,我不需要或不想要标记文本的能力。
所以我的问题是如何最好地处理这种情况?我错过了TextBox上的一些属性吗?或者最好使用RichTextBox,更新外观以使其一致,并禁用文本的标记?
如果没有更好的方法,我很乐意编写处理KeyDown和KeyPress事件明确的代码,但认为值得先检查一下。
答案 0 :(得分:23)
/ *更新:请看下面Damir的答案,这可能是一个更好的解决方案:) * /
我会通过将Ctrl + Shift + Left和Backspace发送到TextBox来模拟Ctrl + Backspace。效果几乎相同,无需手动处理控件的文本。您可以使用以下代码实现它:
class TextBoxEx : TextBox
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.Back))
{
SendKeys.SendWait("^+{LEFT}{BACKSPACE}");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
您还可以修改app.config文件以强制SendKey类使用更新的发送密钥方法:
<configuration>
<appSettings>
<add key="SendKeys" value="SendInput" />
</appSettings>
</configuration>
答案 1 :(得分:22)
老问题,但我偶然发现了一个不需要任何额外代码的答案。
为文本框启用自动完成功能,CTRL-Backspace应该可以按照您的意愿使用。
CTRL-Backspace删除插入符号左侧的整个单词似乎是自动完成处理程序的“流氓功能”。这就是启用自动完成功能修复此问题的原因。
-
您可以启用自动完成功能,将AutoCompleteMode
和AutoCompleteSource
设置为您喜欢的任何内容(例如Suggest
和RecentlyUsedList
)
答案 2 :(得分:7)
虽然ProcessCmdKey覆盖功能很好,但它只限于Ctrl + Backspace的一次迭代,主要是因为SendWait的使用模仿了击键,如果你在按Backspace时按住Ctrl键,系统只有似乎认识到按下Backspace键。如果你要记录覆盖的击键,你会发现一组你从未真正按下的额外键。
另一种方法是在ProcessCmdKey覆盖中显式管理文本框的外观,而不是向系统发送更多密钥。这也很容易应用于Ctrl + Delete。
我已经为Ctrl + Backspace行为包含了一些常见的“停止点”,并且使用了switch语句而不是RegEx。他们从来没有觉得足够干净,我通常最终错过了一个角色
如果您发现我的代码有任何问题,请务必告诉我们。对于仍然被这个难题迷惑的人来说,祝你好运!
public class TextBoxEx : TextBox
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Back | Keys.Control))
{
for (int i = this.SelectionStart - 1; i > 0; i--)
{
switch (Text.Substring(i, 1))
{ //set up any stopping points you want
case " ":
case ";":
case ",":
case "/":
case "\\":
Text = Text.Remove(i, SelectionStart - i);
SelectionStart = i;
return true;
case "\n":
Text = Text.Remove(i - 1, SelectionStart - i);
SelectionStart = i;
return true;
}
}
Clear(); //in case you never hit a stopping point, the whole textbox goes blank
return true;
}
else
{
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
答案 3 :(得分:6)
我不确定如果没有自定义KeyDown或KeyPress事件,以下代码可以正常工作:
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyCode == Keys.Back) && e.Control)
{
e.SuppressKeyPress = true;
int selStart = textBox1.SelectionStart;
while (selStart > 0 && textBox1.Text.Substring(selStart - 1, 1) == " ")
{
selStart--;
}
int prevSpacePos = -1;
if (selStart != 0)
{
prevSpacePos = textBox1.Text.LastIndexOf(' ', selStart - 1);
}
textBox1.Select(prevSpacePos + 1, textBox1.SelectionStart - prevSpacePos - 1);
textBox1.SelectedText = "";
}
}
答案 4 :(得分:3)
这是我使用的,它还处理多行文本框
private void HandleCtrlBackspace_KeyDown(object sender, KeyEventArgs e) {
switch (e.KeyData) {
case (Keys.Back | Keys.Control):
e.SuppressKeyPress = true;
TextBox textbox = (TextBox)sender;
int i;
if (textbox.SelectionStart.Equals(0)) {
return;
}
int space = textbox.Text.LastIndexOf(' ', textbox.SelectionStart - 1);
int line = textbox.Text.LastIndexOf("\r\n", textbox.SelectionStart - 1);
if (space > line) {
i = space;
} else {
i = line;
}
if (i > -1) {
while (textbox.Text.Substring(i - 1, 1).Equals(' ')) {
if (i.Equals(0)) {
break;
}
i--;
}
textbox.Text = textbox.Text.Substring(0, i) + textbox.Text.Substring(textbox.SelectionStart);
textbox.SelectionStart = i;
} else if (i.Equals(-1)) {
textbox.Text = textbox.Text.Substring(textbox.SelectionStart);
}
break;
}
}
答案 5 :(得分:2)
这就是你去的方式:)
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
//if ctrl+bcksp
if (e.KeyChar == 127)
{
//if not last word
if (textBox1.Text.Split (' ').Count() > 1)
{
//remoce last word form list and put it back together (gotta love lambda)
textBox1.Text = textBox1.Text.Split (' ').Take (textBox1.Text.Split (' ').Count() - 1).Aggregate ((a,b) => a + " " + b);
//set selection at the end
textBox1.SelectionStart = textBox1.Text.Length;
}
else if (textBox1.Text.Split (' ').Count() == 1)
{
textBox1.Text = "";
}
}
}
答案 6 :(得分:2)
这很好用:
static Regex RegExWholeWord = new Regex(@"(\r\n|[^A-Za-z0-9_\r\n]+?|\w+?) *$", RegexOptions.Compiled);
在按键时,使用
var m = RegExWholeWord.Match(textbox.Text, 0, textbox.SelectionStart);
if (m.Success)
{
textbox.Text = textbox.Text.Remove(m.Index, m.Length);
textbox.SelectionStart = m.Index;
}
答案 7 :(得分:1)
正则表达式就是为此而做的。使用它。
var index = array.findIndex(function(item){return item.value === value};
console.log(index);
答案 8 :(得分:0)
我在VB中回答而不是C#cuz我在VB中寻找这个解决方案却找不到一个,但是这些C#响应帮助我解决了这个问题:-D
在模块中创建此子
Public Sub ctrl_bksp(ByRef t As TextBox)
Dim ss As Integer = t.SelectionStart
Dim sl As Integer = t.SelectionLength
Dim tl As Integer = t.TextLength
'//Split either side of selection start
Dim strPre As String = Strings.Left(t.Text, tl - (tl - ss))
Dim strPost As String = Strings.Right(t.Text, tl - ss - sl)
'//Get Last Space Location in StrPre
Dim s As Integer = Strings.InStrRev(RTrim(strPre), " ")
strPre = Strings.Left(strPre, s)
t.Text = strPre & strPost
t.SelectionStart = s
End Sub
然后你可以在任何文本框的KeyPress事件中调用这个子:
Private Sub Textbox1_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles Textbox1.KeyPress
Select Case e.KeyChar
Case Chr(127) '//Ctrl+Backspace
e.Handled = True
Call ctrl_bksp(Textbox1)
End Select
End Sub
无论选择在字符串中的哪个位置,以及是否选择了文本,这都会有效,并且响应非常好!
答案 9 :(得分:0)
DWF和giangurgolo,感谢您提供的信息。下面是它的精致版本。请注意,它还会考虑ComboBox
,因为它与TextBox
具有完全相同的问题。另请注意,只有TextBox
或ComboBox
的配置允许时,快捷方式才有效。
TextBoxEx:
public class TextBoxEx : TextBox
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
// Attention:
// Similar code exists in ComboBoxEx.ProcessCmdKey().
// Changes here may have to be applied there too.
if (ShortcutsEnabled)
{
if (keyData == (Keys.Control | Keys.Back))
{
if (!ReadOnly)
{
if (SelectionStart > 0)
{
int i = (SelectionStart - 1);
// Potentially trim white space:
if (char.IsWhiteSpace(Text, i))
i = (StringEx.StartIndexOfSameCharacterClass(Text, i) - 1);
// Find previous marker:
if (i > 0)
i = StringEx.StartIndexOfSameCharacterClass(Text, i);
else
i = 0; // Limit i as it may become -1 on trimming above.
// Remove until previous marker or the beginning:
Text = Text.Remove(i, SelectionStart - i);
SelectionStart = i;
return (true);
}
else
{
return (true); // Ignore to prevent a white box being placed.
}
}
}
else if (keyData == (Keys.Control | Keys.A))
{
if (!ReadOnly && Multiline)
{
SelectAll();
return (true);
}
}
}
return (base.ProcessCmdKey(ref msg, keyData));
}
}
ComboxBoxEx:
public class ComboBoxEx : ComboBox
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
// Attention:
// Similar code exists in TextBoxEx.ProcessCmdKey().
// Changes here may have to be applied there too.
if (keyData == (Keys.Control | Keys.Back))
{
if (DropDownStyle != ComboBoxStyle.DropDownList)
{
if (SelectionStart > 0)
{
int i = (SelectionStart - 1);
// Potentially trim white space:
if (char.IsWhiteSpace(Text, i))
i = (StringEx.StartIndexOfSameCharacterClass(Text, i) - 1);
// Find previous marker:
if (i > 0)
i = StringEx.StartIndexOfSameCharacterClass(Text, i);
else
i = 0; // Limit i as it may become -1 on trimming above.
// Remove until previous marker or the beginning:
Text = Text.Remove(i, SelectionStart - i);
SelectionStart = i;
return (true);
}
else
{
return (true); // Ignore to prevent a white box being placed.
}
}
}
return (base.ProcessCmdKey(ref msg, keyData));
}
}
字符串辅助(例如静态类StringEx):
/// <summary>
/// Returns the start index of the same character class.
/// </summary>
/// <param name="str">The <see cref="string"/> object to process.</param>
/// <param name="startIndex">The search starting position.</param>
/// <returns>
/// The zero-based index position of the start of the same character class in the string.
/// </returns>
public static int StartIndexOfSameCharacterClass(string str, int startIndex)
{
int i = startIndex;
if (char.IsWhiteSpace(str, i)) // Includes 'IsSeparator' (Unicode space/line/paragraph
{ // separators) as well as 'IsControl' (<CR>, <LF>,...).
for (/* i */; i >= 0; i--)
{
if (!char.IsWhiteSpace(str, i))
return (i + 1);
}
}
else if (char.IsPunctuation(str, i))
{
for (/* i */; i >= 0; i--)
{
if (!char.IsPunctuation(str, i))
return (i + 1);
}
}
else if (char.IsSymbol(str, i))
{
for (/* i */; i >= 0; i--)
{
if (!char.IsSymbol(str, i))
return (i + 1);
}
}
else
{
for (/* i */; i >= 0; i--)
{
if (char.IsWhiteSpace(str, i) || char.IsPunctuation(str, i) || char.IsSymbol(str, i))
return (i + 1);
}
}
return (0);
}
答案 10 :(得分:0)
我对这些方法有疑问:
查看.NET参考源的内容.Cut()确实引出了以下解决方案:选择TextBox中的文本,然后使用WM_CLEAR清除它。似乎工作正常,并没有发送人工按键事件。
class CtrlBackspaceSupport
{
TextBox textBox;
public CtrlBackspaceSupport(TextBox textBox)
{
this.textBox = textBox;
textBox.KeyDown += new KeyEventHandler(textBox_KeyDown);
}
[DllImport("user32.dll", SetLastError = true)]
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
const int WM_CLEAR = 0x0303;
void textBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.Back)
{ // Ctrl+Backspace -> remove till word border before cursor
e.SuppressKeyPress = true;
if (0 == textBox.SelectionLength && textBox.SelectionStart > 1)
{ // nothing selected
var text = textBox.Text;
int indexOfSpace = text.LastIndexOf(' ', textBox.SelectionStart - 2);
if (-1 != indexOfSpace)
{ // found something
indexOfSpace++;
textBox.Select(indexOfSpace, textBox.SelectionStart - indexOfSpace);
SendMessage(new HandleRef(textBox, textBox.Handle).Handle, WM_CLEAR, 0, 0);
}
}
}
}
}
答案 11 :(得分:0)
我对Avenicci的代码进行了一些修改,以止于一组分隔符;包含逗号,括号等的字符串数组。
if (e.KeyData == (Keys.Back | Keys.Control))
{
e.SuppressKeyPress = true;
string text="";
foreach (string s in separators)
{
// (\))?\W*$ not word
// (\w)?\w*$ word
Match m = Regex.Match(Text, $@"(\{s})?\W*$");
if (!m.Value.Equals(""))
{
text = Regex.Replace(Text.Substring(0, SelectionStart), $@"(\{s})?\W?$", "");
break;
}
}
if (text.Equals(""))
text = Regex.Replace(Text.Substring(0, SelectionStart), @"(\w)?\w*$", "");
Text = text + Text.Substring(SelectionStart);
SelectionStart = text.Length;
}