I am subclassing TextBox:
class Editor : TextBox
I have overridden OnKeyDown, because I want tabs to be replaced by four spaces:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab) {
SelectedText = " ";
e.SuppressKeyPress = true;
}
}
This works, but unfortunately it also clears the undo buffer. The end result is that when the user presses tab, Ctrl+Z doesn't work and 'Undo' on the right-click menu becomes disabled. The problem appears to be the "e.SuppressKeyPress = true;" part.
Does anyone have any idea of how to get around this?
For more info, I am creating a fairly simple text editor, and I'm handling not only the Tab key (as above), but also the Enter key. So I have this problem with Tab and Enter. I am aware that this problem doesn't exist with RichTextBox, but for various reasons I want to use TextBox instead.
Any help would be much appreciated, as this is a show-stopping problem in my project.
Thanks,
Tom
This isn't a result of overriding OnKeyDown, it's that you're setting SelectedText (any text modification will have the same effect). You can see this by commenting out your code that sets the SelectedText while leaving everything else. Obviously you won't get a tab or four characters, but the undo buffer will be preserved.
According to this blog post, you should be able to use the Paste(string) function rather than setting the SelectedText property and preserve the undo buffer:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
Paste(" ");
e.SuppressKeyPress = true;
}
}
I've finally found the solution, which is to use the Windows API as follows:
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab) {
WinApi.SendMessage(Handle, WinApi.WmChar, WinApi.VkSpace, (IntPtr)4);
e.SuppressKeyPress = true;
}
base.OnKeyDown(e);
}
Here is my WinApi class:
using System;
using System.Runtime.InteropServices;
class WinApi
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public const UInt32 WmChar = 0x102;
public static readonly IntPtr VkSpace = (IntPtr)0x20;
}
Related
I have a requirement to override the Minimize function of the Telerik radWindow (clicking the Minimize Button on the Title Bar). Instead of the Window Minimizing to the bottom left of the screen I intend to simply reduce the Height of the Window leaving just the Title Bar showing (in the original position). Restoring the Window will again simply set the Height back to its original value to expand the Window….
So, I need to set Handle = true when the SC_MINIMIZE Message is received, this will prevent the Window from minimizing, then i can implement my own code to change the Height of the Window… This is the code so far…
private void RadWindow_HostCreated(object sender, HostWindowCreatedEventArgs e)
{
e.HostWindow.SourceInitialized += HostWindow_SourceInitialized;
}
private void HostWindow_SourceInitialized(object sender, EventArgs e)
{
var source = PresentationSource.FromVisual((Window)sender) as HwndSource;
if (source != null) source.AddHook(new HwndSourceHook(HandleMessages));
}
private IntPtr HandleMessages(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// 0x0112 == WM_SYSCOMMAND, 'Window' command message.
// 0xF020 == SC_MINIMIZE, command to minimize the window.
if (msg == 0x0112 && ((int)wParam & 0xFFF0) == 0xF020)
{
handled = true; // not catching SC_MINIMIZE here
}
return IntPtr.Zero;
}
However, while I seem to be catching Windows Messages in the HandleMessages function, it is not setting handled = true; when I click the Minimize Button…
Any help would be appreciated…
I am creating a form which has a Multiline TextBox to enter an URL. Expected URLs will be very long.
User will paste the URL and move to next box.
Right now, TextBox shows ending part of the URL when user moves to next TextBox. I want such that it will show starting of URL (Domain name) instead of trailing part.
Current:
Expected:
And this should happen when user leaves the TextBox.
I tried various methods of Selection in textBox_Leave() event but I guess, these methods won't work if focus is lost.
I am using .Net framework 3.5.
Update: Textbox I am using is Multiline. Answers suggested by #S.Akbari and #Szer are perfect if the Mutliline property is set to False. I realized it late that Multiline will play such a significant role. Hence updating the question!
Use SelectionStart in the Leave event should works:
private void textBox1_Leave(object sender, EventArgs e)
{
textBox1.SelectionStart = 0;
}
Before:
After leaving TextBox:
Tried it and it works. Proof
public Form1()
{
InitializeComponent();
textBox1.LostFocus += TextBox1_LostFocus;
}
private void TextBox1_LostFocus(object sender, EventArgs e)
{
textBox1.SelectionStart = 0;
textBox1.SelectionLength = 0;
}
I can see how it doesn't work with the Multiline property set to true.
A simple API call can make this work:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
private const int WM_VSCROLL = 0x115;
private const int SB_TOP = 6;
void textBox1_Leave(object sender, EventArgs e) {
SendMessage(textBox1.Handle, WM_VSCROLL, (IntPtr)SB_TOP, IntPtr.Zero);
}
We have an UserControls which herits from the RichTextBox. We would like to forbid the user to enter any image(with copy paste) in this user control.
I found several places where speaking of this:
This doesn't work with Winforms
This will not work when doing a right-click -> Paste
Currently I've this solution:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys)Shortcut.CtrlV || keyData == (Keys)Shortcut.ShiftIns)
{
if (Clipboard.ContainsImage())
{
return false;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
Which works for copy paste with CTRL+C-CTRL+V, but not with the contextual menu.
EDIT
I tried the given proposition:
public class CustomRichBox : RichTextBox
{
private const int WM_PASTE = 0x0302;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PASTE )
{
if (Clipboard.ContainsImage())
{
return;
}
}
base.WndProc(ref m);
}
}
But when I do the CTRL+V, I don't receive this message
Sadly there is no global Paste-Event, on which you can subscribe like in WPF. But maybe this is a solution for you:
hook on default "Paste" event of WinForms TextBox control
This worked for me.
You could try to override the WndProc method to filter the WM_PASTE message:
protected override void WndProc(ref Message m)
{
// Trap WM_PASTE with image:
if (m.Msg == 0x302 && Clipboard.ContainsImage())
{
return;
}
base.WndProc(ref m);
}
EDIT
Unfortunatly, this approach won't work because the RichTextBox control doesn't send the WM_PAINT message to itself. See also: Detecting if paste event occurred inside a rich text box.
As a quick workaround, I tried to copy only the Text (using RichTextBox.Text) in another RichTextBox, then copy the Rtf string in the first RichTextBox, all of that in the "TextChanged" event. However there are a lot of downsides for this workaround. First: is not optimized, second and most important: you lose all text formatting, which might be the reason you chose RichTextBox in the first place, and third: you can still see the image for one or two frames in the RTB until it disappears, and if the user is writing a large text it isn't working very smoothly (but fortunately you can fix this if you copy-paste the code in paste events). However, it turned out to be very useful in my app, which is the reason I posted this answer here.
So here is all the code (assuming you have a RichTextBox named RTB and an auxiliary RichTextBox named auxRTB):
private void RTB_TextChanged(object sender, EventArgs e)
{
int selStart = RTB.SelectionStart;
int selLenght = RTB.SelectionLength;
auxRTB.Text = RTB.Text;
RTB.TextChanged -= RTB_TextChanged;
RTB.Rtf = string.Copy(auxRTB.Rtf);
RTB.TextChanged += RTB_TextChanged;
try
{
RTB.SelectionStart = selStart;
RTB.SelectionLength = selLenght;
}
catch (Exception) { }
}
Now, if you are interested, here I'm going to explain how is that useful in my app. So I built a command system, and the only reason I chose RichTextBox insted of normal TextBox is because I wanted to give different colors to each type of thing from a command. The commands are not meant to be long so I don't have any optimiziation problems, and I don't care about losing formatting, since I always change the colors automatically.
Edit: by the way, here are some links to the same problem on other sites, which might actually help you:
Link 1: https://social.msdn.microsoft.com/Forums/en-US/0f762cb8-7383-4937-8ee8-f8df5d3a9852/disable-image-paste-in-richtextbox?forum=wpf
Link 2: C# / WPF: Richtextbox: Find all Images
Link 3: https://thomaslevesque.com/2015/09/05/wpf-prevent-the-user-from-pasting-an-image-in-a-richtextbox/
I have two link labels in my windows forms program which links to my website.
I got rid of the underlines and the ugly blue colour and tried to fix them up a little bit.
But the biggest problem still remains and It's just so disturbing for me, I don't know why.
The hand cursor when you hover over them is that old Windows 98 hand/link cursor.
Is there any way to change it to the system cursor?
I've checked some other links about this problem, but I couldn't get it to work so I decided to ask here.
Here's my code to get rid of the underline btw:
linkLabel1.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
Unfortunately the LinkLabel class is hard-coded to use Cursors.Hand as the hover cursor.
However, you can work around it by adding a class like this to your project:
public class MyLinkLabel : LinkLabel
{
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
OverrideCursor = Cursors.Cross;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
OverrideCursor = null;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
OverrideCursor = Cursors.Cross;
}
}
and using that instead of LinkLabel on your form. (This sets the cursor to a cross for testing purposes, but you can change it to whatever you want.)
I should say that the real LinkLabel code has much more complex logic to do with changing the cursor according to whether or not the link is enabled, but you might not care about that.
Set the Cursor property to Arrow in the properties pane of the LinkLabel in Visual Studio
Update
I prefer Hamido-san's answer here. His solution works properly when the LinkLabel is set to AutoSize = false and works with a LinkArea.
Old solution:
public class LnkLabel : LinkLabel
{
const int WM_SETCURSOR = 32,
IDC_HAND = 32649;
[DllImport("user32.dll")]
public static extern int LoadCursor(int hInstance, int lpCursorName);
[DllImport("user32.dll")]
public static extern int SetCursor(int hCursor);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
{
int cursor = LoadCursor(0, IDC_HAND);
SetCursor(cursor);
m.Result = IntPtr.Zero; // Handled
return;
}
base.WndProc(ref m);
}
}
I found that when I attach a MouseWheel event to my UserControl or a TextBox, it does not trigger if the TextBox is scrollable. It will scroll the textbox and not trigger MouseWheel, can I make it such that it runs MouseWheel and then after doing something e.Cancel it so that the TextBox does not scroll?
UPDATE: With Video & Code
Video
http://screenr.com/ZGF
Code
http://www.mediafire.com/?x3o09dz6dr5zoym
public MainWindow()
{
InitializeComponent();
textBox.MouseWheel += (s, e) =>
{
Random rand = new Random();
Debug.WriteLine(rand.NextDouble());
};
}
I assume you mean that the MouseWheel event on the UserControl won't trigger. That's normal, the TextBox is happy to accept the message when it is multiline. The reason that the MouseWheel event is not visible in the designer. The parent window will only see the message when the control with the focus won't process it.
Not sure if you should fix this, the user would really expect the text box to scroll. But you can by intercepting the message so the text box can't see it and passing the message to the parent. Add a new class to your project, paste the code shown below. Compile. Drop the new control from the top of the toolbox.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MyTextBox : TextBox {
protected override void WndProc(ref Message m) {
if (m.Msg == 0x20a && this.Parent != null) {
PostMessage(this.Parent.Handle, m.Msg, m.WParam, m.LParam);
}
else base.WndProc(ref m);
}
[DllImport("user32.dll")]
private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}