一个Ctrl+V下的问题

时间:2022-09-28 08:41:39

对于电脑快捷键来说恐怕没什么比Ctrl+C和Ctrl+V更熟悉的了。

最近做了一个小程序,界面上有一个文本框,要做的事情就是把从别的地方复制内容后粘贴到文本框中,然后以自己处理后的格式显示出来。

为了方便说明问题,举个例子,把其他功能简化后只用最简单的例子来说明就是,我复制了一个文本"C",然后再从文本框上按Ctrl+V后想把它放到文本框里,现在程序要做的事是让文本框只显示我想显示的内容,而不管你复制来的东西。这样操作就变成了在文本框上粘贴后,文本框就显示"哈"。

当时一想,这还不简单,记性中只要在文本框的KeyDown事件里判断下是否按了ctrl+v然后再进行相应处理就可以了,因为当时已经有了一个粘贴菜单了,按下ctrl+v和点击粘贴菜单其实做的都是同一件事情。

简化界面如下

一个Ctrl+V下的问题

文本框的KeyDown事件与获取字符串的方法

         private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
textBox1.Text = GetText();// 简化操作,只返回一个哈
}
}
private string GetText()
{
string s = "哈";
// 一系列处理操作略掉,最后应返回处理过的字符串...
return s;
}

接着运行,不出意外,结果应该像我们想像的那样,在文本框上按下ctrl+v后文本框里应该只有一个"哈"字,但实际结果却出乎意料,我复制了一个文本"c",按下ctrl+v后竟然是这样的

一个Ctrl+V下的问题

当时就震惊,什么情况,"c"也粘贴进去了,后来想到还有事件参数里有个Handled属性,表示是否处理过此事件,可能忘了设置,然后修改代码如下

         private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
textBox1.Text = GetText();// 简化操作,只返回一个哈
e.Handled = true;
}
}

然后再运行,结果还是一样,不信您自己试下。Handled属性看来在按下事件里不起作用。

接着在KeyDown,KeyPress,KeyUp这三个事件下输出文本框内容看下到底在什么时候系统给自己添加复制的内容到文本框中的,测试代码

         private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
textBox1.Text = GetText();// 简化操作,只返回一个哈
e.Handled = true;
Console.WriteLine("KeyDown: " + textBox1.Text);
}
} private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
Console.WriteLine("KeyPress: " + textBox1.Text);
} private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
Console.WriteLine("KeyUp: " + textBox1.Text);
}

运行,复制文本"c",在文本框上按下ctrl+v快捷键,看VS输出如下

一个Ctrl+V下的问题

从输出窗口可以看出,在KeyPress事件后KeyUp事件前系统把复制的文本"c"插入到了文本框中。

这真是个不愉快的事,系统在KeyPress事件后和KeyUp事件前干了什么,我无法控制。但问题还得解决。

解决方法一:

既然系统把复制的内容粘贴到文本框里,把我把剪贴版的内容清空掉,我看你还怎么放。

         private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
textBox1.Text = GetText();// 简化操作,只返回一个哈
Clipboard.Clear();
}
}

这个方法虽然容易达到效果,但是简单粗暴了点,而且真正的应用中是要使用剪贴版里的内容的,如果是这样,那使用的人就会发现只要在程序里粘贴后,再粘贴到其他地方就会发现粘贴时没了内容,这样又该跟他们解释各种原因,我太懒,所以不用这种方式。

解决方法二:

既然文本框的值在KeyUp之前已经被剪贴版里的文本设置进去了,那我就在KeyUp事件下把我的值设置到文本框中不行吗?

答案是可以的,把KeyDown事件下的代码放到KeyUp事件下

         private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
textBox1.Text = GetText();// 简化操作,只返回一个哈
}
}

经过测试,这样虽然大至是解决了,不过有一个缺点,就是操作时想要让文本框显示处理过的值,操作时还得有点技巧才行,按ctrl+v时不能太快的按下与弹起,你得先按下ctrl键后不放,再按V键,然后先松开V键,再松开ctrl键,这样文本框才会显示"哈",不然只显示你复制的文本,你处理的文本就不会显示,而且显示时会先显示复制的文本+处理的文本(类似:"c哈"),然后才显示你处理过的值到文本框中,很明显示看出的一个文本框的值被改修的过程。

不过这个有技巧性的操作对于编辑部的人来说,让我想起了经典的程序员与上帝打赌的笑话,看来这样还不是比较好的方法。

解决方法三:

网上搜到一篇文章说用richTextBox在KeyDown事件下使用Handled属性是可以的,所以把原来的代码放到KeyDown事件下试试,测试了确实可以达到效果。

         private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V)
{
richTextBox1.Text = GetText();
e.Handled = true;
}
}

不过换控件,对我来说"代价"还是有点大,懒得换了...

解决方法四:

这也是最后采用的一种方法,先定义一个bool变量,表示是否按下ctrl+v快捷键,在KeyDown事件下进行判断设置为true,在KeyPress事件下进行判断文本框的值是否要设置和重置变量为false,这样就不用考虑技巧性操作的问题了,也不会明显的看出文本框先显示粘贴的文本再显示自己设置的值了。

         private bool isCtrlv = false;// 是否按下ctrl+v
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V) // 是否按下ctrl+v
{
isCtrlv = true;
}
} private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (isCtrlv)// 如果按下ctrl+v
{
textBox1.Text = GetText();
e.Handled = true;
}
isCtrlv = false;

没想到一个简单的ctrl+v操作竟然整得如此曲折,想像中应该是一帆风顺的正常按照自己想像当中出现的结果。

之前没有好好测试,算是一个坑了,如果你还有什么更好的方法能解决,可以分享下....