C#: How to deselect and defocus textbox? - c#

I'm writing a simple calc-based app in C# WinForms. My issue is that I have textbox to display results which cannot be clicked/focused because buttons are binded to keyboard. Textbox has to have ContextMenuStrip, but only this action should be handled within textbox. Also this app requirement is that it cannot have any focusable controls. Something like Windows' Calc result box. Any advice?

Create custom text box:
public class TextBoxWithoutFocus : TextBox
{
private const int WM_SETFOCUS = 0x7;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETFOCUS)
return;
base.WndProc(ref m);
}
}
That's all. Text box will never receive a focus (actually it will receive focus, but it will not change its look). Also consider suppress WM_CHAR message (0x102) if you want to disable user input.
UPDATE (trick you can use with buttons):
public class ButtonWithoutFocus : Button
{
public ButtonWithoutFocus()
{
SetStyle(ControlStyles.Selectable, false);
}
}

Make it a label, not a textBox. You can set dimensions and background so it looks like a textBox, and it has ContextMenuStrip.

Try both TextBox.Enabled = False; and TextBox1.ReadOnly = True;

Does TextBox.Enabled = false meet your needs?

Try this
TextBox1.ReadOnly = True;

to defocus simply do this:
TextBox.Select(0, 0);

Related

Disable image paste in RichTextBox in Winforms

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/

How to set Focus on the Form?

I'm trying the get the inputted values from the user and save it on a char variable, but the problem is that nothing occurs, and I think the problem is with the Form Focus, this is the code, and when I run it no errors occurs, but also nothing happen. What I did wrong?
char keyPressed;
public FrmZigndSC()
{
InitializeComponent();
this.Focus();
}
private void FrmZigndSC_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
keyPressed = e.KeyChar;
LblResult.Text += Convert.ToString(keyPressed);
}
You can try with this code - Based on KeyPressEventHandler
public FrmZigndSC()
{
InitializeComponent();
this.Focus();
//Subscribe to event
this.KeyPress += new KeyPressEventHandler(FrmZigndSC_KeyPress);
}
private void FrmZigndSC_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
keyPressed = e.KeyChar;
LblResult.Text += Convert.ToString(keyPressed);
// Indicate the event is handled.
e.Handled = true;
}
If you want to recieve a key notification from the application'e mesage pipeline, relaying on focus of the element, in this case, make architecture fragile. You can not gurantee that from other forms in your app that one would be focused, or the form is nto covered by some control that absorbes that event. You can not forse to a form having a focus, cause it's completely bad UX design (not very sure even if this is possible to implement in 100% working way).
What you can do it, instead, is declare class derived from IMessageFilter:
public class MessageFilter : IMessageFilter
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
public bool PreFilterMessage(ref Message m)
{
// Intercept KEY down message
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
if ((m.Msg == WM_KEYDOWN)
{
//get key pressed
return true;
}
else
{
return false;
}
}
}
and after register it within your application:
MessageFilter filter = new MessageFilter(); //init somewhere
Application.AddMessageFilter(filter ); // add
.....
//on application end don't forget to remove it
Application.RemoveMessageFilter(filter );
I tried to reproduce it in an empty little Windows Forms project. This code worked just fine without the Shown event handler:
public partial class FrmZigndSC : Form
{
public FrmZigndSC()
{
InitializeComponent();
this.KeyPress += (s, e) => this.LblResult.Text += e.KeyChar.ToString();
// this might be a solution, but i did not need it
this.Shown += (s, e) => this.Activate();
}
}
You could try to use this.Activate() anyway and see if it helps. If you got other input controls such as text boxes on your form, try setting the form's KeyPreview property to true.
Using the FocusManager instead of this.Focus() should do the trick!
http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.aspx
I Think the problem is that when you have differenct controls on your Form that catch ketpressed. I had the same probmel with an DateTimePicker on my control.
Try to delete all of them and then try it, it will work. And from that point add the controls again to see which on is the problem.
The code you have given is working fine for me. Set the startup page as FrmZigndSC and try again.

How to avoid deselecting text on Winforms TextBox when clicking on selected text?

I would like to implement a drag&drop operation from TextBox to another control.
The problem is that when you select some part of text and then click on the TextBox text is deselected. Therefore when I perform DoDragDrop in MouseDown event the textBox.SelectedText is already empty.
Is there any way to avoid such behavior? I found following example but I don't want to loose the possibility to drag&drop only a part of text.
I found solution. You need to inherit TextBox and override OnMouseDown and WndProc:
public class DragTextBox : TextBox
{
private string dragText;
private const int WM_LBUTTONDOWN = 0x201;
protected override void OnMouseDown(MouseEventArgs e)
{
if (dragText.Length > 0)
{
SelectionStart = Text.IndexOf(dragText);
SelectionLength = dragText.Length;
DoDragDrop(dragText, DragDropEffects.Copy);
SelectionLength = 0;
}
base.OnMouseDown(e);
}
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_LBUTTONDOWN))
dragText = SelectedText;
base.WndProc(ref m);
}
}
Original code author post here

How to disable cursor in textbox?

Is there any way to disable cursor in textbox without setting property Enable to false?
I was trying to use ReadOnly property but despite the fact that I can't write in textbox, the cursor appears if I click the textbox. So is there any way to get rid of this cursor permamently?
In C#, you can use the following read-only textbox:
public class ReadOnlyTextBox : TextBox
{
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
public ReadOnlyTextBox()
{
this.ReadOnly = true;
this.BackColor = Color.White;
this.GotFocus += TextBoxGotFocus;
this.Cursor = Cursors.Arrow; // mouse cursor like in other controls
}
private void TextBoxGotFocus(object sender, EventArgs args)
{
HideCaret(this.Handle);
}
}
In C# you can disable the cursor in a textbox by temporarily disabling and then re-enabling the text box whenever it receives the focus. Note there is no need to make the textbox read only if using this method. For example:
private void TextBox_Enter(object sender, EventArgs e)
{
TextBox.Enabled = false;
TextBox.Enabled = true;
}
You could use a Label instead. When in the designer, you set BorderStyle = Fixed3D, BackColor = Window and AutoSize = False, it looks a lot like a TextBox.
However, the cursor in a TextBox is provided so that the user can scroll through the text when it is longer than the box. You'll lose that functionality with a Label, unless you are sure that it will always fit. Other than that, it is not possible to remove the cursor from a TextBox.
Putting the hideCaret function inside the TextChanged event will solve the problem:
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
private void textBox1_TextChanged(object sender, EventArgs e)
{
HideCaret(textBox1.Handle);
}
Easiest solution for me was to just override the on focus event and focus back to the parent. This prevents the cursor and any editing of the textbox by the user and basically disables the text box with out having to set the Enabled = false property.
private void Form1_load(object sender, EventArgs e) {
textBox1.ReadOnly = true;
textBox1.Cursor = Cursors.Arrow;
textBox1.GotFocus += textBox1_GotFocus;
}
private void textBox1_GotFocus(object sender, EventArgs e) {
((TextBox)sender).Parent.Focus();
}
Like #Mikhail Semenov 's solution, you can also use lambda express to quickly disable the cursor if you do not have many textboxes should do that:
[DllImport("user32.dll")]
static extern bool HideCaret(IntPtr hWnd);
textBox1.ReadOnly = true;
textBox1.BackColor = Color.White;
textBox1.GotFocus += (s1, e1) => { HideCaret(textBox1.Handle); };
textBox1.Cursor = Cursors.Arrow;
You can set it programatically.
textBox1.Cursor = Cursors.Arrow;
This is not strictly an answer to the question, but perhaps it can solve some similar problem(s). I use a textbox control which can look like a label for a control which displays a scale, but can be edited, when clicked. Start enabled = false and make an activation (enabled = true) in a mousehandler of the parent of the textbox control (which, when disabled, border None and backcolor = parent backcolor, looks like a label). E.g. when enter hit or other event, disable again in KeyDown handler.
(Of course the parent mouse click routine can check whether the mouseclick really occured in the label/textbox control).
If you need the textbox control to activate by tabbing, some more work is required (than I have done).
I use the form constructor to find the textbox parent at runtime and to apply the delegate mouse control. Perhaps you can do this as wel in compile time (Form header), but that seemed a little error-prone to me.
One way of doing it is using View + TabIndex, you can do indexing of some other controls on the dialog as first, let say for buttons if there any. Then as if the control tabIndex is not the first i.e 0, cursor won't get appear there.
To disable the edit and cursor in the TEXT BOX
this.textBox.ReadOnly = true;
this.textBox.Cursor = Cursors.No;//To show a red cross icon on hover
this.textBox.Cursor = Cursors.Arrow //To disable the cursor
you can use RightToLeft Property of Text Box, set it to true, you will not get rid of the Cursor, but it will get fixed at right corner and it will not appear automatically after every text you type in your text Box. I have used this to develop an application like Windows Calculator.

How to handle Form caption right click

I'd like a context menu on the caption bar right click
any tips/samples pref in c# ?
UPDATE - for various reasons, right click on the form won't work because the form is not empty and the form is composited dynamically so....
You can do this by trapping the WM_NCRBUTTONDOWN notification that Windows sends when the user right-clicks the title bar. The control class does not have an event for it, you'll need to override WndProc(). Here's an example form, you'll need to add a ContextMenuStrip:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected void OnTitlebarClick(Point pos) {
contextMenuStrip1.Show(pos);
}
protected override void WndProc(ref Message m) {
const int WM_NCRBUTTONDOWN = 0xa4;
if (m.Msg == WM_NCRBUTTONDOWN) {
var pos = new Point(m.LParam.ToInt32());
OnTitlebarClick(pos);
return;
}
base.WndProc(ref m);
}
}
MSDN explains how to handle right-clicks on Windows Forms controls. Controls, including Forms, inherit the MouseClick event.
MouseEventArgs will tell you what button was clicked through the Button property. Have a look at the MouseButtons Enumeration.
if you handle the form mouse-click, you can then use the following code:
private void Dialog_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
this.Text = "new caption text";
}
}
But you'll have to make sure that you generate this event for the top-level control on a form. For instance if you have a group box on the form, it will receive the mouse-click events rather than the form itself, for the areas of the form that are under the group box.
There is already a menu managed by Windows when you right-click the titlebar.
Do you want to replace it completely?
If you want to add to it you will have to use the Win32 API and interop and you will have to subclass the form.
See the AppendMenu() function.
Basically you need to use p-invoke to do this. There is a really great example at Here
You can see from the example you will need to manually mimic the event handlers, but this is pretty straight forward.
You can override WndProc of the form and capture the WM_NCRBUTTONDOWN message:
protected override void WndProc(ref Message m)
{
const int WM_NCRBUTTONDOWN = 0xA4;
if (m.Msg == WM_NCRBUTTONDOWN)
{
MessageBox.Show("Caption right clicked!");
}
else
{
base.WndProc(ref m);
}
}
This code will suppress the window's context menu, however. You may not wish this. The WM_NCRBUTTONDOWN message will also be sent if you right click the window borders as well. You may not desire this either.

Categories