Different AcceptButton for different Panels on Form - c#

I have a form with a Search button & Submit button, they are sitting on different panels but in the same form. I want to use the AcceptButton property of the Form with both buttons based on criteria.
The user will search for a ticket using the box on the right panel, which will populate the datagridview below. The user will then select a row, which I have bound to the text boxes on the left panel. The New Asset textbox is blank, so the user will have to populate that before hitting submit.
Is there anyway to bind the AcceptButton Property to the search textbox (right panel) if the new asset number textbox is null and then bind the property to the submit button (left panel) after the user populates the text box.
Sorry if this is hard to understand. I am still learning c# and all of the things I can do with it.

Note: While I also believe the UI design in the question case can be improved, but in general having multiple default button for different parts of a page can be considered as a normal requirement, like html form elements which
are supposed to run their submit button code if you press enter on any
form element, while you can have multiple form on a single
page.
What you need to do is overriding ProcessDialogKey and check if the first panel contains focus then perform click of the first button and if the second panel contains focus then perform click of the second button:
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Enter)
{
if (panel1.ContainsFocus)
{
button1.PerformClick();
return true;
}
if (panel2.ContainsFocus)
{
button2.PerformClick();
return true;
}
}
return base.ProcessDialogKey(keyData);
}
You also have the option of handling Enter event of controls and assign AcceptButton of the form based on the focused control. But above/below
solutions are more general with less code.
Note - Creating a Panel class having AcceptButton property
Apart from above solution, as a more reusable solution for those who wants to handle such cases by less code, you can use such panel class having AcceptButton property:
using System.Windows.Forms;
public class MyPanel : Panel
{
public Button AcceptButton { get; set; }
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Enter)
{
AcceptButton?.PerformClick();
return true;
}
return base.ProcessDialogKey(keyData);
}
}

Related

C# How to escape form's Accept Button from getting triggered

I have a Windows Form, with a ListView and a TextBox placed right on top of it. Also there is a button named 'Submit' on the Form with AcceptButton set to true.
Now, my requirement is that, whenever I load the form and hit Enter, Submit button gets triggered, which is fine. But, when I select the TextBox and type something in it and then hit Enter, the focus should come to the ListView , but not the AcceptButton
How can I achieve this?
You can set the AcceptButton based on the active control, or as a better option, handle ProcessDialogKey yourself. This is the method which contains the logic for AcceptButton.
So you can override it, for example:
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Enter)
{
if (this.ActiveControl == this.textBox1)
{
MessageBox.Show("Enter on TextBox1 handled.");
return true; //Prevent further processing
}
}
return base.ProcessDialogKey(keyData);
}

How to prevent a textbox inside Webbrowser control from losing focus when user clicks outside webbrowser control? [duplicate]

How can I make a button that can sendkeys into a datagridview (so I need to somehow return datagridview to its state prior to it losing focus)
I'll explain the problem
I have a form with a datagridview and some buttons
I can click the button and it will enter the corresponding letter,
dataGridView1.Focus();
SendKeys.Send("a"); //or m or z, depending on the text of the button.
The button is meant to be for entering data into datagridview. I could make it so it's enabled only when datagridview's enter method is called. When the button is clicked then the datagridview loses focus so I have to give it the focus again, prior to sending the key. Hence the two lines above.
I want the user to be able to use the buttons to enter data into datagrid.
The problem is, let's say I want the user to be able to enter the text "maz" I can't do it. I only get an individual a or m or z. And even if I could put a cell back into editable mode, i'd need to remember where the cursor was.. So if somebody typed asdf into a cell and put the cursor between d and f, and clicked m, i'd want it to say asdmf.
I don't know how to accomplish that.
I have an idea.. that maybe if there was a way of registering that the mouse was over a button and the click was pushed, but without losing focus on datagridview, then that would be one way that it could be done. I don't know how to do that though, and i'm interested in different ways.
Non-Selectable Button to Send Key like Virtual Keyboard
It's enough to make a non-selectable button and then handle it's click event and send key. This way these buttons can work like virtual keyboard keys and the focus will remain on the control which has focus while it sends the key to the focused control.
public class ExButton:Button
{
public ExButton()
{
SetStyle(ControlStyles.Selectable, false);
}
}
Then handle click event and send key:
private void exButton1_Click(object sender, EventArgs e)
{
SendKeys.SendWait("A");
}
SetStyle extension method for a Control
Is there any way of calling SetStyle on a Control without using
inheritance?
Yes, using Reflection, there is. Create this extension method:
public static class Extensions
{
public static void SetStyle(this Control control, ControlStyles flags, bool value)
{
Type type = control.GetType();
BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
MethodInfo method = type.GetMethod("SetStyle", bindingFlags);
if (method != null)
{
object[] param = { flags, value };
method.Invoke(control, param);
}
}
}
Then use it this way, in your form Load event:
this.button1.SetStyle(ControlStyles.Selectable, false);
Then button1 will be non-selectable without inheritance.

Restrict tab order to a single user control

I have a user control that behaves as a floating control, and I would like to restrict the tab order only to my user control when its visible. Basically what I need is to have a control that behaves like a borderless Form. Actually it was a Form, but I needed to preserve the Focus in the MainForm window, so I had to change it to be a UserControl.
So, imagine a Form A (MainForm), and my UserControl B. B is a child control of A. Suppose that Form A has a button and a TextBox, and the control B also has a button and a Textbox. The secuence that currenly occurs is the following
What currently happens (natural tab order behavior):
When only A is visible (B is not visible):
1. The user manually focuses A textbox
2. Press tab key
3. A button is focused
When A is visible and also B is visible: (the natural tab order key is the following):
1. The user manually focuses B textbox
2. Press tab key
3. B button is focused
4. Press tab key
5. A textbox is focused
6. Press tab key
7. A button is focused
What I need (I need to change my user control to preserve the focus):
What I really need is that the B control preserves the tab order inside it, so what I need is with when B control is visible:
1. The user manually focuses B texbox
2. Press tab key
3. B button is focused
4. Press tab key
5. B textbox is focused
You can override the Controls' KeyDown event and manually move the focus over to the Control that should receive focus.
Aside from that, I agree with Will Hughes that it might break navigation...
I'm assuming you have some button you press that toggles the visibility of your B user control. And if it is visible and has focus then it keeps focus. It loses focus only when you toggle it to hidden. If that's the case, you could try this code in your A form which will keep your focus in the user control unless you hide the user control:
// store when we last clicked the toggle B user control visibility
private Stopwatch _sinceLastMouseClick;
public Form1()
{
InitializeComponent();
// instantiate the stopwatch and start it ticking
_sinceLastMouseClick = new Stopwatch();
_sinceLastMouseClick.Start();
}
The button that toggles the visibility on your floating B control's click handler:
private void btnToggleBUserControlVisibility_Click(object sender, EventArgs e)
{
// reset the stopwatch because we just clicked it
_sinceLastMouseClick.Restart();
myUserControl1.Visible = !myUserControl1.Visible;
}
In your parent A form, handle the floating user control's Leave event:
private void myUserControl1_Leave(object sender, EventArgs e)
{
// see if the mouse is over the toggle button
Point ptMouse = System.Windows.Forms.Control.MousePosition;
Point ptClient = this.PointToClient(ptMouse);
// if the mouse is NOT hovering over the toggle button and has NOT just clicked it,
// then keep the focus in the user control.
// We use the stopwatch to make sure that not only are we hovering over the button
// but that we also clicked it, too
if (btnToggleBUserControlVisibility != this.GetChildAtPoint(ptClient) ||
_sinceLastMouseClick.ElapsedMilliseconds > 100)
{
myUserControl1.Focus();
}
}
Finally I solved the issue including the following code in the parent control:
private int WM_KEYDOWN = 0x100;
public override bool PreProcessMessage(ref Message msg)
{
Keys key = (Keys)msg.WParam.ToInt32();
if (msg.Msg == WM_KEYDOWN && key == Keys.Tab)
{
if (itemSearchControl.Visible)
{
bool moveForward = !IsShiftKeyPressed();
bool result = itemSearchControl.SelectNextControl(itemSearchControl.ActiveControl, true, true, true, true);
return true;
}
}
return base.PreProcessMessage(ref msg);
}
From another question, add this to your UserControl xaml.
KeyboardNavigation.TabNavigation="Cycle"
Restrict tab order to a single user control (WPF)

Detect in Control.GotFocus whether user navigates forward or backward?

Having the following WinForms dialog form, I am handling the GotFocus event of MyControl:
MyControl derives from the DevExpress XtraUserControl which in turn derives from the Microsoft WinForms standard UserControl.
What I want to achieve is that when MyControl gets the focus when the user navigates with the Tab and MyControl gets the focus, that the focus is forwarded to the child controls.
I do this successfully with the following code:
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
// Forward.
foreach (Control control in Controls)
{
if (control.TabStop)
{
control.Select();
break;
}
}
}
I.e. if Button 1 is focused and the user presses the Tab key, the focus is set to Button 2.
What I'm not able to solve is if the user navigates backward. I.e. if Button 4 is focused and the user presses the Shift+Tab keys, the focus should be set to Button 3.
My two questions are:
Is there a way to detect the navigation order of the user inside the GotFocus event?
Am I doing it the right way at all? Maybe there is a built-in function/flag I can set to MyControl to automatically forward the focus to its child controls?
So many possibilities:
use the OnLostFocus event to store the current control and calculate whether TAB or SHIFT TAB was pressed
override ProcessKeyPreview to calc the action to be performed in OnGotFocus (SO answer)
override ProcessCmdKey as in this answer

Validating Data In A WinForm

I have created a dialog box in my WinForms application. This has many text boxes and ok/cancel buttons. When the user clicks ok, I only want the dialog box to close if all entries are valid. I can see how to do this with the "Validating" events for each control separately. That is fine. But these only seem to fire when a control loses focus. However, empty text boxes in my dialog are also invalid input which means the user may never have focused on that control. I would prefer to just validate all controls on clicking OK.
I can't work out how to do this though. Overriding the onclick of the OK button doesn't seem to have an option for stopping the window from closing. The Form IsClosing event does by setting Cancel = true. But this doesn't seem to be able to distinguish between whether the OK or Cancel button is clicked. Obviously if the cancel button is clicked I don't care about validation and want to allow the form to close regardless.
What is the best approach for doing this?]
Update:
I already had CausesValidation set to true on both my form and ok button but my validation event does not get fired when I click the ok button. I mention this as it was suggested as a solution below.
Please select the form > Set the property CausesValidation to true
Select OK button and again set property CausesValidation to true
and then it will take care of all the validations.
Important points:
1) You must mention e.Cancel=true in all the validating eventhandlers
2) If your buttons are in panels then you must set panels (or any parent control's) CausesValidation property to true
Edit:
3) Validate fires just before loss of focus. While pressing Enter will
cause the default button to Click, it doesn't move the focus to that
button, hence no validation event will be fired if you have set forms AcceptButton Property to OK button
First make sure to cancel the validation when any of the textboxes have validation errors. For example:
private void nameTextBox_Validating(object sender, CancelEventArgs e) {
if (nameTextBox.Text.Length == 0)
{
e.Cancel = true;
return;
}
}
Now add the following code to the beginning of the ok button action:
if (!ValidateChildren())
return;
This will trigger the validation event for all controls on the form,
You can also use this simple code. just introducing a simple Boolean variable named hasError can do the job.
public partial class Form1 : Form
{
private bool hasError;
public Form1()
{
InitializeComponent();
}
private void OkBtn_Click(object sender, EventArgs e)
{
errorProvider1.Clear(); hasError=false;
if (ValidateTxt.Text.Length == 0)
{
errorProvider1.SetError(ValidateTxt, "must have a value");
hasError=true;
}
if (!hasError)
{
//Do what you want to do and close your application
Close();
}
}
private void CancelBtn_Click(object sender, EventArgs e)
{
Close();
}
}

Categories